1、C++语句 提供一组 控制流语句 以支持更复杂的执行路径
1、一个表达式 末尾加上分号 就变成了 表达式语句。表达式语句的作用是 执行表达式 并丢掉求值结果
ival + 5; // 没有实际用处的表达式语句
cout << ival; // 有用的表达式语句
表达式语句中的 表达式 在求值时 附带有其他效果,如 给变量赋了新值 或者 输出了结果
2、空语句:单独的分号 ; // 空语句
语法上需要 单逻辑上不需要:如 循环的全部工作 都可以在 条件部分完成:例 读取输入流的内容 直到遇到 一个特定的值为止
// 重复读入数据 直至到达文件末尾 或 某次输入的值等于 sought
while (cin >> s && s != sought)
; // 空语句
while循环 条件部分首先先从 标准输入读取一个值 并且隐式地检查cin,假设读取成功 检查读进来的值 是否为sought的值。如果发现等于sought,循环终止;否则,从cin中继续 读取另一个值,再一次判断 循环的条件
空语句应该加上注释
3、注意分号:
包含两段语句:表达式语句 和 空语句:ival = v1 + v2;;
if / while的条件后多跟了分号 会造成无休止的循环:
// 额外的分号,循环体 是空语句
while (iter != svec.end());
++iter; // 递增运算 不属于 循环的一部分,虽然形式上有缩进
4、复合语句(也称为 块):指用 花括号括起来的语句 和 声明的序列
一个块 就是一个作用域,在块中引入的名字 只能在块内部 以及 嵌套在块中的子块里访问。名字只在有限的区域可见,该区域从 名字定义处开始,到 名字所在的最内层块的结尾 为止
语法要求一条语句,逻辑上需要多条,需要 多条语句用 花括号括起来,把语句序列 转变成 块
while (val <= 10) {
sum += val;
++val;
}
使用 逗号运算符,使 不需要块
while (val <= 10)
sum += val, ++val;
块不以 分号 作为结束
1、可以在 if、switch、while 和 for语句 的控制结构内 定义变量。定义在控制结构中的变量 只在相应语句的内部可见
while (int i = get_num()) // 每次迭代创建并初始化i
cout << i << endl;
i = 0; // 错误:在循环外部无法访问i
其他代码也需要访问 控制变量,则 变量需要定义在 语句的外部:
错误代码
while (bool status = find(word)) { /* . . . */ }
if (!status) { /* . . . */ }
改成
bool status;
while (status = find(word)) {/* ... */}
if (!status) {/* ... */}
控制结构定义的对象 的值 马上要由结构本身 使用,所以 这些变量必须初始化
错误代码
while (string::iterator iter != s.end()) { /* . . . */ }
改成
std::string::iterator iter = s.begin();
while (iter != s.end()) { /* . . . */ }
包含if、switch语句
1、悬垂else:规定else 与 离他最近的尚未匹配的if 匹配,不管 格式缩进
2、使用花括号 控制执行路径
3、条件运算符的优先级太低,括号可以很少
1、能够在 若干固定选项中 做出选择
如果 输入的字符 与某个元音匹配,并将 该字母的数量+1
// 为 每个元音字母初始化 其计数值
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
char ch;
while (cin >> ch) {
// 如果ch是元音字母,将其对应的计数值+1
switch (ch) {
case 'a':
++aCnt;
break;
case 'e':
++eCnt;
break;
case 'i':
++iCnt;
break;
case 'o':
++oCnt;
break;
case 'u':
++uCnt;
break;
}
}
switch 语句 首先对括号里的 表达式求值,该表达式 紧跟在 关键字switch的后面,可以是 一个初始化变量声明;表达式的值 转换为 整数类型,然后与每个case 标签的值 比较
如果 表达式 和 某个case标签的值 匹配成功,程序从 该标签之后的第一条语句 开始执行,直到 到达了switch 的结尾 或者 遇到一条 break 语句为止。简言之,break的作用是 中断当前控制流,将 控制权 转移到 while语句的 右花括号处
如果 switch语句的表达式 和 所有case都没有匹配上,将直接跳转到switch结构之后的 第一条语句。case 关键字 和它对应的值 一起被称为case标签。case标签 必须是 整型 常量表达式
char ch = getVal();
int ival = 42;
switch (ch) {
case 3.14: // 错误:case标签 不是一个整数
case ival: // 错误:case标签 不是一个常量
2、switch 内部的控制流:如果某个case标签匹配成功,将 从该标签开始往后顺序 执行所有case分支,除非 程序显式中断,否则直到 switch结尾处 才停下来。要想 避免执行后续 case分支 的代码,必须 显式地告诉编译器终止执行过程。通常,在下一个case标签 之前应该有一条 break语句
有时,希望 两个或更多个值 共享一组操作,此时 省略break语句,使得 程序能够连续执行 若干个case标签
如:统计 所有元音字母出现的总次数:
unsigned vowelCnt = 0;
switch (ch)
{
// 出现了a, e, i, o, u 中任意一个 都会将vowelCnt的值+1
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
++vowelCnt;
break;
}
case 标签之后 不一定非得换行。把几个case标签 写在一行里,强调这些case 代表的是某个范围内的值
switch (ch)
{
// 另一种合法的书写方式
case 'a': case 'e': case 'i': case 'o': case 'u':
++vowelCnt;
break;
}
漏写break 容易引发缺陷,一般 不要省略case 分支最后的 break语句。如果没写 break 语句,最好加一段注释说明程序逻辑
尽管 switch语句 不是非得在 最后一个标签后面写上break,但是为了安全,最好这么做。即使以后 再增加新的case分支,也不用在前面 补充break语句
3、default标签:没有任何一个case标签 能配上switch表达式的值,程序 将执行紧跟在default标签 后面的语句
比如 可以增加一个计数值 来统计非元音字母的数量,只要在default分支内 不断递增名为 otherCnt的变量即可:
switch (ch) {
case 'a': case 'e': case 'i': case 'o': case 'u':
++vowelCnt;
break;
default:
++otherCnt;
break;
}
}
标签 不应该孤零零地出现,后面必须跟上 一条语句 或 另外一个case标签。如果 switch结构 以一个空的default标签作为 结束,则 应该在default标签后面 必须 跟上一条空语句 或者 一个空块
即使不准备在 default标签下 做任何工作,定义一个default标签也是有用的,告诉读者 考虑到了默认情况,只是啥也没做
4、switch内部的变量定义:如果被略过的代码中 含有变量的定义?
如果 在某处一个带有初值的变量 位于作用域之外,在另一处 该变量位于 作用域之内,则 从前一处跳转到后一处的行为 是非法行为
C++规定,不允许跨过变量的初始化语句 直接跳转到 该变量作用域内的 另一个位置
case true:
// 因为程序的执行流程 可能绕开下面的 初始化逻辑,所以 该switch语句 不合法
string file_name; // 错误:控制流绕过一个 隐式初始化的变量
int ival = 0; // 错误:控制流绕过一个 显式初始化的变量
int jval; // 正确:因为jval没有初始化
break;
case false:
// 正确:jval虽然在作用域内,但是它没有初始化
jval = next_num(); // 正确:给jval赋一个值
对于跳过部分,如果只是声明一个值 没有初始化的话,并不会跳过:
char s;
cin >> s;
switch (s) {
case 't':
int ival;
break;
case 'f':
ival = 5;
cout << ival << endl;
break;
}
输入f 时,会正常输出5
5、既统计元音字母的小写形式,也统计元音字母的大写形式,同时 也能统计空格、制表符、和换行符的数量
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, tabCnt = 0, newlineCnt = 0;
char ch;
while (cin >> std::noskipws >> ch) // while都没加括号,下面一整块 算一条语句
switch (ch) {
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'i':
case 'I':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
case '\n':
++newlineCnt;
break;
case '\t':
case '\v':
++tabCnt;
break;
}
cin >> std::noskipws >> ch
:不忽略 任意地方的空格
修改统计元音字母的程序,使其能统计含以下两个字符的字符序列的数量: ff、fl和fi
需要找个值 记录上一轮元素
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, tabCnt = 0, newlineCnt = 0, ffCnt = 0, flCnt = 0, fiCnt = 0;
char ch, prech = '\0';
while (cin >> std::noskipws >> ch)
{
switch (ch) {
case 'a':
case 'A':
++aCnt;
break;
case 'e':
case 'E':
++eCnt;
break;
case 'i':
if(prech == 'f') ++fiCnt;
case 'I':
++iCnt;
break;
case 'o':
case 'O':
++oCnt;
break;
case 'u':
case 'U':
++uCnt;
break;
case '\n':
++newlineCnt;
break;
case '\t':
case '\v':
++tabCnt;
break;
case 'f':
if(prech == 'f') ++ffCnt;
break;
case 'l':
if(prech == 'f') ++flCnt;
break;
}
prech = ch;
}
switch case语法不对:
unsigned evenCnt = 0, oddCnt = 0;
int digit = get_num() % 10;
switch (digit) {
case 1, 3, 5, 7, 9: // 错误
oddcnt++;
break;
case 2: case 4: case 6: case 8: case 0: // 正确
evencnt++;
break;
}
case标签必须是整形常量表达式:
unsigned ival=512, jval=1024, kval=4096; // 错误
const unsigned ival=512, jval=1024, kval=4096; // 正确
unsigned bufsize;
unsigned swt = get_bufCnt();
switch(swt) {
case ival:
bufsize = ival * sizeof(int);
break;
case jval:
bufsize = jval * sizeof(int);
break;
case kval:
bufsize = kval * sizeof(int);
break;
}
迭代语句 通常称之为 循环,其重复执行直到 满足某个条件才停下来。while 和 for 语句在执行循环体之前 检查条件,do while 语句先执行循环体,然后再 检查条件
1、while 的条件部分 可以是 一个表达式 或 是一个带初始化的 变量声明。应该 由条件本身 或者是 循环体 设法改变表达式的值,否则 循环可能无法终止
定义在while条件部分 或者 while循环体内的变量 每次迭代都经历 从创建到销毁的过程
2、使用 while循环:
1)不确定 到底要迭代多少次,比如 读取输入的内容
2)想在循环结束后 访问循环控制变量
如:寻找 第一个负值元素
auto beg = v.begin();
while (beg != v.end() && *beg >= 0)
++beg;
if (beg == v.end()) // 此时我们知道 v中的所有元素都大于等于0
1、for语句的语法格式
for (init-statement; condition; expression)
statement;
init-statement 必须是以下三种形式中的一种:声明语句、表达式语句 或者 空语句
一般情况下,init-statement 负责初始化一个值,这个值将随着 循环的进行 而改变。condition 作为循环控制的条件,只要condition为真,就执行一次statement。expression 负责修改 init-statement 初始化的变量,这个变量 正好就是 condition检查的对象,修改发生在 每次循坏迭代之后
2、for 语句头中定义的对象 只能在for循环体内 可见
3、for 语句头中的 多重定义:init-statement 也可以定义多个对象,但是 init-statement 只能有一条声明语句,因此,所有变量 的基础类型相同
vector <int> v;
// 记录下v的大小,当达到 原来的最后一个元素后 结束循环
for (decltype(v.size()) i = 0, sz = v.size(); i != sz; ++i)
v.push_back(v[i]);
在 init-statement 里同时定义了 索引i 和 循环控制变量sz
4、省略for语句头的某些部分:for 语句头 能省略掉 init-statement、condition 和 expression 中的任何一个(或者全部)
分号代表 一个空的init-statement。省略condition的效果 等价于 在条件部分写了个true。因为条件的值 永远是true,所以 在循环体内 必须有语句 负责退出循环,否则 循环就会无休止地执行下去
也能省略掉for语句头中的 expression,但是 在这样的循环中 就要求条件部分 或者 循环体 必须改变迭代变量的值
5、while 循环特别适用于 条件保持不变、反复执行操作的情况。for 循环更像是在 按步迭代,它的索引值 在某个范围内 依次变化
1、语法形式:
for (declaration : expression)
statement
expression 表示的必须是 一个序列,比如 用花括号括起来的 初始值列表、数组 或者 vector或string等类型的 对象,这些类型的共同特点是 拥有能返回迭代器的 begin和end成员
declration 定义一个变量,序列中每个元素 都能转换成该变量的类型。确保 类型相容 最简单的办法是 使用auto类型说明符
1、先执行循环 后检查条件。不管 条件如何,都 至少执行一次循环
do
statement
while (condition); // 有分号
condition 不能为空,condition 使用的变量 必须定义在循环体之外
// 不断提示用户输入一对数,然后求和
string rsp; // 定义在循环体之外
do { // 大括号别忘了
int val1 = 0, val2 = 0;
cin >> val1 >> val2;
cout << val1 + val2 << endl;
cin >> rsp;
} while (!rsp.empty() && rsp[0] != '\n'); // 分号别忘了
条件部分 检查用户做出的回答,如果用户没有回答,或者 用户的回答以字母n开始,循环都将终止。否则 循环继续执行
对于 do while 来说 先执行语句 或 块,后判断条件,不允许在条件部分 定义变量
do {
// ...
mumble(foo);
} while (int foo = get_foo()); // 错误:将变量声明放在了 do的条件部分
C++提供4种 跳转语句:break、continue、goto 和 return
1、break语句 负责终止离它最近的 while、do while、for 或 switch 语句,并从这些语句之后的 第一条语句 开始继续执行
1、continue语句 终止最近的循环中的 当前迭代 并立即 开始下一次迭代。continue 语句只能出现在 for、while和do while循环内部(没有switch),或嵌套在 此类循环里的语句 或 块的内部
和break一样,出现在 嵌套循环中的continue语句 仅作用于离它最近的循环
continue 语句中断当前迭代,仍然继续循环。对于 while或者 do while 语句,继续判断条件的值;对于 传统的for循环来说,继续执行 for语句头的expression;而对于 范围for循环 来说,序列中的下一个元素初始化 循环控制向量
例:找到的两个连续重复单词必须以大写字母开头
string pre_s, cur_s, tmp_s;
while (cin >> cur_s) {
if (!isupper(cur_s[0])) continue;// 不是大写字母开头的直接下一轮,判断大写字母 / string使用下标
if (cur_s == pre_s) {
cout << cur_s << " ";
tmp_s = cur_s;
break;
}
pre_s = cur_s;
}
if (tmp_s.empty()) {
cout << "no repeat" << endl;
}
1、goto语句 无条件跳转到 同一函数内的 另一条语句
不要在程序里使用 goto语句
2、goto语句 语法形式:goto label;
label 是用于标示一条语句的 标识符。带标签语句 是一种特殊的语句,在它的之前 有一个标识符 以及 一个冒号:
label: return; // 带标签语句,可作为 goto的目标
标签指示符 独立于变量 或 其他指示符的名字,因此,标签指示符 可以和 程序中其他实体的标识符 使用同一个名字 而不会相互干扰。goto 语句 和 控制权转向的那条带标签的语句 必须位于同一个函数内
和switch语句类似,goto 语句 也不能将程序的控制权 从变量的作用域之外 转移到 作用域之内
// ...
goto end;
int ix = 10; // 错误:goto语句 绕过了一个带初始化的变量定义
end:
ix = 10; // 错误:此处的代码需要使用ix,但是goto语句 绕过了它的声明
向后跳过一个已经执行的定义 是合法的。跳回到 变量定义之前 意味着 系统将销毁该变量,然后 重新创建它
// 向后跳过一个带初始化的变量定义 是合法的
begin:
int sz = get_size();
if (sz <= 0) {
goto begin;
}
goto语句 执行后将销毁sz。因为 跳回到 begin的动作 跨过了sz的定义 语句,所以sz将 重新定义并初始化
1、异常是指 存在于运行时的反常行为,这位行为 超出了函数的正常功能的范围
典型的异常 包括:失去和数据库的连接 以及 遇到意外输入等
2、异常检测:检测出问题的部分 只发出信号,信号的发出方 无需知道 故障将在何处解决
异常处理:专门的代码处理问题
3、异常处理机制 为程序中 异常检测 和 异常处理 这两部分的协作 提供支持。异常处理包括:
1)throw 表达式:异常检测部分 使用throw表达式 来表示它遇到了 无法处理的问题。throw引发了异常
2)try 语句块:异常处理部分 使用try语句块 处理异常。try 语句块 以关键字try 开始,并以 一个或多个catch子句 结束。try 语句块中代码 抛出的异常 通常会被某个catch子句处理
因为 catch 子句处理异常,所以 经常被称为 异常处理代码
3)一套 异常类:用于在throw表达式 和 相关的catch子句之间 传递异常的具体信息
1、程序的异常检测部分 使用throw表达式 引发了一个异常。throw表达式 包含关键字throw 和 紧随其后的一个表达式,表达式的类型 就是抛出的异常的类型。throw表达式后面通常加分号,构成 表达式语句
以 第一章 5.4 成员函数 与 输出异常处理信息 例子为例,程序是 检查它读入的记录是否是 关于同一种书籍的
#include
#include "Sales_item.h"
//练习1.24
int main() {
Sales_item book, book_sum;
if (std::cin >> book_sum) {
while (std::cin >> book) {
if (book.isbn() == book_sum.isbn()) {
book_sum += book;
}
else {
std::cout << book_sum << std::endl;
book_sum = book;
}
}
std::cout << book_sum << std::endl;
}
else {
//没有输入警告信息
std::cerr << "No data?" << std::endl;
return -1;//表示失败
}
return 0;
}
在真实的程序中,应该把 对象相加的代码 和 用户交互的代码 分离开来。改写程序 使得检查完成后 不再直接输出一条信息,而是抛出 一个异常
// 首先 检查两条数据 是否关于同一种书籍
if (item1.isbn() != item2.isbn())
throw runtime_error("Data must refer to same ISBN");
// 如果程序执行到了这里,表示两个ISBN是相同的
cout << item1 + item2 << endl;
如果ISBN不一样 就抛出一个异常,该异常是类型runtime_error 的对象。抛出异常 将终止当前函数,并把 控制权转移给 能处理该异常的代码
类型runtime_error 是标准库异常 中的一种,定义在 stdexcept 头文件中。我们必须初始化runtime_error 的对象,方式是 给它提供一个 string对象 或者一个C风格的字符串,这个字符串中 有一些关于异常的 辅助信息
1、try 语句块的 通用语法形式是
try {
program-statements
} catch (exception-declaration) {
handler-statements
} catch (exception-declaration) {
handler-statements
} // ...
try 语句块的一开始 是关键字try,随后 紧跟着一个块
跟在 try块之后的是 一个或多个catch子句。catch 子句包括三部分:关键字catch、括号内一个(可能未命名的)对象的声明(称作异常声明)以及 一个块。当选中了 某个catch子句 处理异常之后,执行与之对应的块。catch 一旦完成,程序跳转到 try语句块 最后一个catch子句之后 的那条语句继续执行
try 语句块中的 program-statements 组成程序的正常逻辑。像其他任意块一样,program-statements 可以有包括声明在内的 任意C++语句。try 语句块内声明的变量 在块外部无法访问,特别是在 catch子句内 也无法访问
2、编写处理代码:6.1中的例子,假设执行Sales_item对象加法的代码 是与用户交互的代码分开来的。其中与 用户交互的代码负责处理发生的异常,它的形式 可能如下所示
while (cin >> item1 >> item2) {
try {
// 执行添加两个Sales_item 对象的代码
// 如果添加失败,代码抛出一个 runtime_error 异常
} catch (runtime_error err) {
// 提醒用户两个ISBN必须一致,询问是否 重新输入
cout << err.what() << "\nTry again? y / n" << endl;
char c;
cin >> c;
if (!cin || c == 'n')
break; // 跳出while循环
}
}
程序本来要执行的代码 出现在 try语句块中
try 语句块 对应一个 catch子句,该子句负责处理类型为 runtime_error的异常。如果 try语句块的代码 抛出了runtime_error异常,接下来执行 catch块内的语句
在catch子句中,输出信息 要求用户指定程序是否继续。如果 用户输入’n’,执行 break语句 并退出while循环;否则,直接执行while循环的右侧花括号,意味着 程序控制权 跳回到while条件部分 进行下一次迭代
给用户的提示信息中 输出了 err.what() 的返回值。err的类型是 runtime_error,what是runtime_error 类的一个成员函数,返回的是 初始化一个具体对象时 所用的string对象的副本。每个标准库异常类 都定义了名为what的成员函数,这些函数没有参数,返回值是C风格字符串(const char*)
3、函数在 寻找处理代码的过程中退出:在 复杂系统中,程序在遇到 抛出异常的代码前,其执行路径 可能已经经过了 多个try语句块
寻找处理代码的过程 和 函数调用链 刚好相反
当异常被抛出的时候,首先 搜索抛出该异常的 函数
当 没找到匹配的catch子句,终止该函数,并在 调用该函数的函数中 继续寻找。如果 还是没有找到匹配的catch子句,终止该函数,并在 调用该函数的函数中 继续寻找
以此类推,沿着程序执行路径 逐层回退,直到 找到适当类型的catch子句为止
如果最终 还是没能找到 任何匹配的catch子句,程序转到名为 terminate 的标准库函数。该函数的行为 和系统有关,一般情况下,执行该函数 将导致程序非正常退出。对于 没有任何try语句块定义的异常 也是类似处理:没有try语句块 意味着没有匹配的catch子句
4、编写 异常安全代码 困难:异常中断了 程序的正常流程,从而导致对象处于无效或未完成状态,或者资源没有正常释放 等等。那些在异常发生期间 正确执行了“清理”工作的程序 被称为 异常安全代码
确实要 处理异常 并继续执行的程序,必须时刻清楚 异常何处发生,异常发生后 程序如何确保对象有效、资源无泄漏、程序处于合理状态 等等
1、C++标准库 定义了一组类,用于 报告标准库函数 遇到的问题。这些异常类 也可以在用户编写的程序中使用,分别定义在4个头文件中:
1)exception 头文件定义了 最通用的异常类 exception。它只报告异常的发生,不提供 任何额外的信息
2)stdexcept 头文件定义了 几种常用的异常类
定义的异常类 |
含义 |
---|---|
exception | 最常见的问题 |
runtime_error | 只有在运行时才能检测出问题 |
range_error | 运行时错误:生成的结果超出了有意义的值的范围 |
overflow_error | 运行时错误:计算上溢 |
underflow_error | 运行时错误:计算下溢 |
logic_error | 程序逻辑错误 |
domain_error | 逻辑错误:参数对应的结果值不存在 |
invalid_argument | 逻辑错误:无效参数 |
length_error | 逻辑错误:试图创建一个 超出该类型最大长度的对象 |
out_of_range | 逻辑错误:使用一个超出有效范围的值 |
3)new头文件 定义了 bad_alloc 异常类型
4)type_info 头文件 定义了 bad_cast 异常类型
2、标准库异常类 只定义了几种运算,包括 创建或拷贝 异常类型的对象,以及 为异常类型的对象赋值
只能 以默认初始化 的方式 初始化exception、bad_alloc 和 bad_cast 对象,不允许为这些对象 提供初始值
其他异常类型的行为 恰好相反:应该使用string对象 或者 C风格字符串 初始化这些类型的对象,但是不允许 使用默认初始化的方式。当创建 此类对象的时候,必须提供初始值,该初始值 含有错误相关的信息 runtime_error("Data must refer to same ISBN");
异常类型只定义了一个 名叫what的成员函数,该函数 没有任何参数,返回值是一个 指向C风格的字符串的 const char*。该字符串的目的是 提供关于异常的 一些文本信息
what函数 返回的C风格字符串的内容 与 异常对象的类型有关。如果 异常类型有一个字符串初始值,则what返回该字符串。对于其他无初始值的 异常类型来说,what返回的内容 由编译器决定
3、编写一段程序,从标准输入读取两个整数,输出第一个数除以第二个数的结果,使得当第二个数是0时抛出异常
#include
#include
int main()
{
int i1,i2;
std::cin >> i1 >> i2;
if(i2 == 0)
{
throw std::runtime_error("divisor can't be 0");
}
std::cout << i1/i2 << std::endl;
return 0;
}
输入:1 0
输出:
terminate called after throwing an instance of ‘std::runtime_error’
what(): divisor can’t be 0
Aborted (core dumped)
使用try语句块 去捕获异常。catch子句 应该为用户输出一条提示信息,询问其 是否输入新数 并重新执行try语句块的内容
#include
#include
using std::cout; using std::cin; using std::endl; using std::runtime_error;
int main()
{
int n1, n2;
while (cin >> n1 >> n2) { // 不管有没有错都可以继续,当然有错可以选择
try {
if (n2 == 0) throw runtime_error("divisor can't be zero.");
cout << n1 / n2 << endl;
}
catch (runtime_error err) // 加括号,申明了一个变量err
{
cout << err.what() << "\nTry again? Y/N" << endl;
char c;
cin >> c;
if (!cin || c == 'N')
break;
}
}
return 0;
}
1、块:(即复合语句)包围在花括号内的 由0条 或 多条语句组成的序列。块也是一条语句,只要是能使用语句的地方,就可以使用 块
2、case标签:在switch语句中 紧跟在case 关键字之后的常量表达式。在同一个 switch语句中 任意两个case标签的值 不能相同
3、continue语句:中止离它最近的循环的当前迭代。控制权转移到 while 或 do while 语句的条件部分、或者 范围for循环的下一次迭代、或者 传统for循环头部的表达式
4、catch 子句:由三部分组成:catch关键字、括号里的 异常声明(位于 catch子句中的声明,指定了该catch 子句能处理的异常类型)以及 一个语句块。catch 子句的代码 负责处理在异常声明中 定义的异常
5、throw表达式:一种 中断当前执行路径的 表达式。throw 表达式抛出一个异常 并把控制权转移到 能处理该异常的最近的 catch语句
6、try语句块:跟在try关键字后面的块,以及 一个或多个catch子句。如果 try语句块的代码 引发异常 并且其中一个catch子句 匹配该异常类型,则 异常被该catch子句处理。否则,异常 将由外围try语句块处理,或者 程序中止