C++ Primer 第五章 语句

简单语句

表达式语句
  • 一个表达式末尾加上分号
ival + 5; // 一条没有什么实际用处的表达式语句
cout << ival; // 一条有用的表达式语句
空语句
  • 空语句中只含有一个单独的分号
; // 空语句

注:别漏写分号,也别多写分号

复合语句(块)
  • 复合语句是指用花括号括起来的语句和声明的序列,复合语句也被称作块 ,在块中引入的名字只能在块内部以及嵌套在块中的子块里访问。
while (val <= 10) {
sum += val;
++ val;
}
// 把要执行的语句用花括号括起来,就将其转换成了一条(复合)语句
  • 所谓空块,是指内部没有任何语句的一对花括号。空块的作用等价于空语句
while (cin >> s && s != sought)
{ } // 空块

语句作用域

  • 可以在 if、switch、while 和 for 语句的控制结构内定义变量。定义在控制结构当中的变量只在相应语句的内部可见,一旦语句结束,变量也就超出其作用范围了。

条件语句

if 语句
  • 判断一个指定的条件是否为真,根据判断结果决定是否执行另外一条语句
switch 语句
while (cin >> ch) {
switch(ch) {
case 'a':
++ aCnt;
break;
case 'e':
++ eCnt;
break;
case 'i':
++ iCnt;
break;
case 'o':
++ oCnt;
break;
case 'u':
++ uCnt;
break;
default:
break;
}
}
  • 如果某个 case 标签匹配成功,将从该标签开始往后顺序执行所有 case 分支,除非程序显式地中断了这一过程,否则直到 switch 的结尾处才会停下来。
  • case 标签必须是整型常量表达式
  • 如果在某处一个带有初值的变量位于作用域之外,在另一处该变量位于作用域之内,则从前一处跳转到后一处的行为是非法行为。

迭代语句

  • 迭代语句通常成为循环,它重复执行操作直到满足某个条件才停下来
while 语句
while (condition) {
statement;
}
传统的 for 语句
for (init-statement; condition; expression)
statement
------------------------------------------------
for (initializer; condition; expression)
statement;
for 语句头中的多重定义
  • init-statement 只能有一条声明语句,因此,所有变量的基础类型必须相同。
省略 for 语句头的某些部分
  • 省略掉 init-statement,相当于使用了一条空语句
auto beg = v.begin();
for (decltype(v.size()) i = 0, sz = v.size(); i != sz; ++ i)
v.push_back(v[i]);
  • 省略掉 condition 的效果等价于在条件部分写了一个 true
for (int i = 0; ; ++ i) {
// xxxxxxx
}
  • 省略掉 expression,但是在这样的循环中就要求条件部分或者循环体必须改变迭代变量的值。
vector<int> v;
for (int i; cin >> i; ) {
v.push_back(i);
}
范围 for 语句
for (declaration : expression)
statement
  • statement 表示的是一个序列,比如用花括号括起来的初始值列表、数组或者 vector 或 string 等类型的对象,这些类型的共同特点是拥有能返回迭代器的 begin 和 end 成员。
for (auto x : {1, 2, 3}) cout << x << ' ';
cout << endl;
  • 在范围 for 语句中,预存了 end() 的值。一旦在序列中添加(删除)元素,end() 函数的值可能就变得无效了。
do while 语句
  • 先执行循环体后检查条件
do
statement
while (condition); // 不允许在条件部分定义变量

跳转语句

break continue goto return

break 语句
  • 负责终止离它最近的 while、do while、for 或 switch语句,并从这些语句之后的第一条语句开始继续执行
continue 语句
  • continue 语句终止最近的循环中的当前迭代并立即开始下一次迭代。continue 语句只能出现在 for、while 和 do while 循环的内部,或者嵌套在此类循环里的语句或块的内部。和 break 类似的是,出现在嵌套循环中的 continue 语句也仅作用于离它最近的循环。和 break 语句不同的是,只有当 switch 语句嵌套在迭代语句内部时,才能在 switch 里使用 continue。
goto 语句
  • goto 语句的作用是从 goto 语句无条件跳转到同一函数内的另一条语句(不要在程序中使用goto语句,因为它使得程序既难理解又难修改)
goto label;
  • label 是用于标识一条语句的标识符。带标签语句是一种特殊的语句,在它之前有一个标识符以及一个冒号
    标签标示符可以和程序中其他实体的标示符使用同一个名字而不会互相干扰
    goto 也不能将程序的控制权从变量的作用域之外转移到作用域之内
  • 向后跳过一个已经执行的定义是合法的。跳回到变量定义之前意味着系统将销毁该变量,然后重新创建它
begin:
int sz = get_size();
if (sz <= 0) {
goto begin;
}

try 语句块和异常处理

  • throw 表达式,异常检测部分使用 throw 表达式来表示它遇到了无法处理的问题。我们说 throw 引发了异常
  • try 语句块,异常处理部分使用 try 语句块处理异常。try 语句块以关键字 try 开始,并以一个或多个 catch 子句结束。try 语句块中代码抛出的异常通常会被某个 catch 子句处理。因为 catch 子句“处理”异常,所以它们也被称作异常处理代码
  • 一套异常类,用于在 throw 表达式和相关 catch 子句之间传递异常的具体信息
throw 表达式
  • 程序的异常检测部分使用 throw 表达式引发一个异常。throw 表达式包含关键字 throw 和紧随其后的一个表达式,其中表达式的类型就是抛出的异常类型。
if (item1.isbn() != item2.isbn())
throw std::runtime_error("Data must refer to same ISBN");
// 如果程序执行到了这里,表示两个 ISBN 是相同的
cout << item1 + item2 << endl;

抛出异常将终止当前的函数,并把控制权转移给能处理该异常的代码

  • 类型 runtime_error 是标准库异常类型的一种,定义在 #include <stdexcept> 中,我们必须初始化 runtime_error 的对象,方式是给它提供一个 string 对象或者一个 C 风格的字符串,这个字符串中有一些关于异常的辅助信息。
string error = "Data must refer to same ISBN";
if (item1.isbn() != item2.isbn())
throw std::runtime_error(error);
cout << item1 + item2 << endl;
try 语句块
  • 语法形式
try {
program-statements
} catch (exception-declaration) { // 括号内的叫做异常声明,当选中了某个 catch 子句处理异常之后,执行与之对应的块
handler-statements // catch 一旦完成,程序跳转到 try 语句块最后一个 catch 子句之后的那条语句继续执行
} catch (exception-declaration) {
handler-statements
} // ...

例:

Sales_item item1, item2;
while (cin >> item1 >> item2) {
try {
string error = "Data must refer to same ISBN";
if (item1.isbn() != item2.isbn()) // 代码抛出一个 runtime_error 异常
throw std::runtime_error(error);
cout << item1 + item2 << endl;
} catch (std::runtime_error err) { // 抛出异常后会进入这个 catch 子句
cout << err.what() << endl; // what() 是一个成员函数,返回的是一个 C 风格的字符串,
//runtime_error 的 what() 返回的是初始化一个具体对象时所用的 string 对象的副本
cout << "try again ? enter y or n" << endl;
char ch;
cin >> ch;
if (!cin || ch == 'n') break;
}
}
  • 当异常被抛出时,首先搜索抛出该异常的函数。如果没有找到匹配的 catch 子句,终止该函数,并在调用该函数的函数中继续寻找。如果还是没有找到匹配的 catch 子句,这个新的函数也被终止,继续搜索调用它的函数,以此类推,沿着程序的执行路径逐层回退,直到找到适当类型的 catch 子句为止。
    如果最后还是没能找到任何匹配的 catch 子句,程序转到名为 terminate 的标准库函数
    对于那些没有任何 try 语句块定义的异常,也按照类似的方式处理
标准异常
  • exception 头文件定义了最通用得异常类 exception。它只报告异常的发生,不提供任何额外信息。
  • stdexcept 头文件定义了几种常见的异常类
  • new 头文件定义了 bad_alloc 异常类型
  • type_info 头文件定义了 bad_cast 异常类型

<stdexcept>定义的异常类

exception 最常见的问题
runtime_error 只有在运行时才能检测出的问题
range_error 运行时错误:生成的结果超出了有意义的值域范围
overflow_error 运行时错误:计算上溢
underflow_error 运行时错误:计算下溢
logic_error 程序逻辑错误
domain_error 逻辑错误:参数对应的结果值不存在
invalid_argument 逻辑错误:无效参数
length_error 逻辑错误:试图创建一个超出该类型最大长度的对象
out_of_range 逻辑错误:使用一个超出有效范围的值
  • 我们只能以默认初始化的方式初始化 exception、bad_alloc 和 bad_cast 对象,不允许为这些对象提供初始值
  • 其他异常类型的行为恰好相反:应该使用 string 对象或者 C 风格字符串初始化这些类型的对象,但是不允许使用默认初始化的方式。

5.25

#include <iostream>
#include <string>
#include <vector>
#include <cstring>
#include <cstddef>
#include <iterator>
#include <stdexcept>
#include "Sales_item.h"
using std::string;
using std::cin;
using std::cout;
using std::endl;
using std::vector;
using std::begin;
using std::end;
using std::cerr;
int main() {
int a, b;
while (cin >> a >> b) {
try {
if (b == 0) {
cout << "asd" << endl;
throw std::runtime_error("除0错误");
} else {
cout << a / b << endl;
}
} catch (std::runtime_error err) {
cout << err.what() << endl;
cout << "try again ? enter y or n" << endl;
char ch;
cin >> ch;
if (!cin || ch == 'n') break;
}
}
return 0;
}
posted @   HuiPuKui  阅读(34)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示