语句
1 悬垂 else
当一个 if 语句嵌套在另一个 if 语句内部时,很可能 if 分支会多于 else 分支。这时候问题出现了: 我们怎么知道某个给定的 else 是和哪个 if 匹配呢 ?
这个问题通常称作 悬垂else,不同的语言解决该问题的思路不同,就C++而言,它规定 else 与离它最近的尚未匹配的 if 匹配,从而消除了程序的二义性。
// 错误:实际执行过程并非像缩进格式显示的那样;else分支匹配的是内层if语句
if (grade % 10 >= 30)
if (grade % 10 > 7)
lettergrade += '+'; // 末尾是8、9的成绩添加一个加号
else
lettergrade += '-'; // 末尾是3、4、5、6、7的成绩添加一个减号
2 goto语句
goto 语句的作用是从 goto 语句无条件的跳转到同一函数内的另一条语句。
goto 语句的语法形式是
goto label;
其中,label适用于表示一条语句的标识符。 带标签语句 是一种特殊语句,在它之前有一个标识符以及一个冒号:
end: return; // 带标签语句,可以作为goto的目标
goto end;
注: 不要在程序中使用goto语句,因为它使得程序既难理解有难修改。
3 try语句块和异常处理
异常 是指存在于运行时的反常行为,典型的异常包括失去数据库连接以及遇到意外输入等。
异常处理机制为程序中异常检测和异常处理这两部分的协作提供支持。
3.1 throw表达式
程序的异常检测部分使用 throw 表达式引发一个异常。throw 表达式包含关键字 throw 和紧随其后的一个表达式,其中表达式的类型就是抛出的异常类型。throw 表达式后面通常紧跟一个分号,从而构成一条表达式语句。
3.2 try语句块
try语句块的作用是启动异常处理机制,检测try语句块中程序语句执行时可能出现的异常。try 语句块以关键字 try 开始,并以一个或多个 catch 子句结束。其通用语法形式是
try {
program-statements
} catch (exception-declatation) {
handler-statements
} catch (exception-declatation) {
handler-statements
} // ...
try 语句块中代码抛出的异常通常会被某个 catch 子句处理。因为 catch 子句 “处理” 异常。所以它们也称作异常处理代码。
catch 自居包括三个部分: 关键字 catch、括号内一个(可能是未命名的)对象的声明(异常声明)以及一个块。当选中了某个 catch 字句处理异常之后,执行与之对应的块。catch 一旦完成,程序跳转到 try 语句最后一个 catch 自居之后的那条语句继续执行。
注: 可以通过 try 和 throw,将代码处理部分与用户交互部分分离开来。
函数在寻找处理代码的过程中退出
在复杂系统中,程序遇到抛出异常的代码前,其执行路径可能已经经过了多个 try 语句块。
寻找处理代码的过程与函数调用链刚好相反。如果沿着程序的执行路径逐层回退,都没有找到匹配的 catch 自居的话,程序会转到名为 terminate 的标准库函数。该函数的行为与系统有关,一般情况下执行该函数将导致程序非正常退出。
对于那些没有任何 try 语句块定义的异常,也按照类似的处理方式,没有 try 语句块也就意味着没有匹配的 catch 字句,此时系统会调用 terminate 并终止当前程序的执行。
注: 异常中断了程序的正常流程。异常发生时,调用者请求的一部分计算可能已经完成了,另一部分则尚未完成。通常情况下,略过部分程序意味着某些对象处理到一般就戛然而止了,从而导致对象处于无效或未完成的状态,或者资源没有正常释放,等等。那些在异常发生期间正确执行了 “清理”工作的程序被称作 异常安全 的代码。
一般来说异常发生时只是简单地终止程序,并不需要担心异常安全的问题。真正需要担心的是对于那些要处理一场并继续执行的程序。异常发生后程序应如何确保对象有效、资源无泄漏、程序处于合理状态等等。
3.3 标准异常
C++标准库定义了一组类,用于报告标准库函数遇到的问题。这些异常类也可以在用户编写的程序中使用,它们分别定义在4个头文件中:
- exception 头文件定义了最通用的异常类 exception。它只报告异常的发生,不提供任何额外信息。
- stdexcept 头文件定义了几种常用的异常类,如下表所示
类型 | 含义 |
---|---|
exception | 最常见的问题 |
runtime_error | 只有运行时才能检测出的问题 |
range_error | 运行时错误:生成的结果超出了有意义的值域范围 |
overflow_error | 运行时错误:计算上溢 |
underflow_error | 运行时错误:计算下溢 |
logic_error | 程序逻辑错误 |
domain_error | 逻辑错误:参数对应的结果值不存在 |
invalid_argument | 逻辑错误:无效参数 |
length_error | 逻辑错误:试图创建一个超出该类型最大长度的对象 |
out_of_range | 逻辑错误:使用一个超出有效范围的值 |
- new 头文件定义了 bad_alloc 异常类型。
- type_info 头文件定义了 bad_cast 异常类型。
对于 exception、bad_alloc 和 bad_cast 对象,我们只能以默认初始化的方式对其初始化,不允许为这些对象提供初始值。
其他异常类型则正好相反,应该使用 string 对象或者 C风格字符串初始化这些类型的对象,但是不允许使用默认初始化的方式。当创建此类对象时,必须提供初始值,该初始值含有错误相关的信息。