C++Primer学习笔记(五)语句
简单语句
C++ 中,大多数语句以分号结束。例如,像 ival + 5 这样的表达式,在后面加上分号,就是一条表达式语句。
表达式语句用于计算表达式。
但执行下面的语句
ival + 5; // expression statement
却没有任何意义:
因为计算出来的结果没有用于赋值或其他用途;
空语句
程序语句最简单的形式是空语句,它使用以下的形式(只有一个单独的分号):
; // null statement
如果在程序的某个地方,语法上需要一个语句,但逻辑上并不需要,此时应该使用空语句。
无关的空语句并非总是无害的。
声明语句
用于声明或定义对象或类
复合语句(块)
复合语句,通常被称为块,是用一对花括号括起来的语句序列(也可能是空的)。
块标识了一个作用域,在块中引入的名字只能在该块内部或嵌套在块中的子块里访问。
通常,一个名字只从其定义处到该块的结尾这段范围内可见。
复合语句用在语法规则要求使用单个语句但程序逻辑却需要不止一个语句的地方
语句作用域
有些语句允许在它们的控制结构中定义变量:
while (int i = get_num())
cout << i << endl;
i = 0; // error: i is not accessible outside the loop
在条件表达式中定义的变量必须初始化,该条件检验的就是初始化对象的值。
在语句的控制结构中定义的变量,仅在定义它们的块语句结束前有效。
if 语句
if 语句根据特定表达式是否为真来有条件地执行另一个语句。
if 语句有两种形式:
其中一种带 else 分支而另一种则没有。
当多个语句必须作为单个语句执行时,比较常见的错误是漏掉了花括号。
switch 语句
① switch 在计算表达式的值后跳转到匹配的标号处(无匹配则跳转至 default),并从该点开始一直执行下去,
直至 switch 语句结束或遇到 break 语句
default 标号提供了相当于 else 子句的功能。
② switch 求解表达式的结果须为整型,每个 case 标号的值也须为各不相同的整型常量表达式
③ switch 内部的变量定义
· 可以在 switch 求解的表达式中定义和初始化变量
· 为防止跳过变量定义,只允许在最后一个标号后定义变量
· 也可以引入块语句,在其中定义变量
while 语句
循环条件中定义的变量在每次循环时都要经历创建和撤销的过程
for 循环语句
语句头中的初始化语句、循环条件和表达式三者都可以省略
循环条件省略表示永远为 true
do while 语句
不能在循环条件中定义变量
break 语句
用于结束最近的外围 while, do while, for 或 switch 语句,并在该语句后继续执行
continue 语句
导致最近的外围循环语句(for, while, do while)正在进行的这次迭代提前结束
goto 语句
⑴ goto 语句提供了函数内部的无条件跳转,实现从 goto 语句跳转到同一函数内某个带标号的语句
除非有足够理由,应避免使用 goto 语句
⑵ 在任何语句前提供一个标识符和冒号,就得到一个带标号的语句(labeled statement)
标识符: 语句
使用 goto 语句跳转到该语句: goto 标识符;
由于这里的标识符只能用作 goto 的目标,因此可以与其它类型的标识符(如变量名)同名
⑶ goto 语句不能跨越变量的定义语句向前跳转
若确实需在 goto 和跳转目标位置间定义变量,则须定义在块中
try 块和异常处理
C++ 的异常处理中包括:
1. throw 表达式,
错误检测部分使用这种表达式来说明遇到了不可处理的错误。
可以说,throw 引发了异常条件。
2. try 块,错误处理部分使用它来处理异常。
try 语句块以 try 关键字开始,并以一个或多个 catch 子句结束。
在 try 块中执行的代码所抛出(throw)的异常,通常会被其中一个 catch 子句处理。
由于它们“处理”异常,catch 子句也称为处理代码。
3. 由标准库定义的一组异常类,用来在 throw 和相应的 catch 之间传递有关的错误信息。
throw 表达式
系统通过 throw 表达式抛出异常。
throw 表达式由关键字 throw 以及尾随的表达式组成,通常以分号结束,这样它就成为了表达式语句。
throw 表达式的类型决定了所抛出异常的类型。
// first check that data is for the same item
if (!item1.same_isbn(item2))
throw runtime_error("Data must refer to same ISBN");
// ok, if we're still here the ISBNs are the same
std::cout << item1 + item2 << std::endl;
这段代码检查 ISBN 对象是否不相同。
如果不同的话,停止程序的执行,并将控制转移给处理这种错误的处理代码。
try 块
try 块的通用语法形式是:
try {
program-statements
} catch (exception-specifier) {
handler-statements
} catch (exception-specifier) {
handler-statements
} //...
try 块以关键字 try 开始,后面是用花括号起来的语句序列块。
try 块后面是一个或多个 catch 子句。
每个 catch 子句包括三部分:
关键字 catch,
圆括号内单个类型或者单个对象的声明——称为异常说明符,
以及通常用花括号括起来的语句块。
如果选择了一个 catch 子句来处理异常,则执行相关的块语句。
一旦 catch 子句执行结束,程序流程立即继续执行紧随着最后一个 catch 子句的语句。
try 语句内的 program-statements 形成程序的正常逻辑。
这里面可以包含任意 C++ 语句,包括变量声明。
与其他块语句一样,try 块引入局部作用域,
在 try 块中声明的变量,包括 catch 子句声明的变量,不能在 try 外面引用。
标准异常
C++ 标准库定义了一组类,用于报告在标准库中的函数遇到的问题。
程序员可在自己编写的程序中使用这些标准异常类。
标准库异常类定义在四个头文件中:
1. exception 头文件定义了最常见的异常类,它的类名是 exception。
这个类只通知异常的产生,但不会提供更多的信息。
在<stdexcept> 头文件中定义的标准异常类
exception 最常见的问题。
runtime_error 运行时错误:仅在运行时才能检测到问题
range_error 运行时错误:生成的结果超出了有意义的值域范围
overflow_error 运行时错误:计算上溢
underflow_error 运行时错误:计算下溢
logic_error 逻辑错误:可在运行前检测到问题
domain_error 逻辑错误:参数的结果值不存在
invalid_argument 逻辑错误:不合适的参数
length_error 逻辑错误:试图生成一个超出该类型最大长度的对象
out_of_range 逻辑错误:使用一个超出有效范围的值
使用预处理器进行调试
使用预处理变量来避免重复包含头文件
⒈ 使用 NDEBUG 预处理变量实现有条件的调试代码(类似头文件保护符)
#ifndef NDEBUG
#define NDEBUG
// 调试代码
#endif
如果定义了 NDEBUG 就不执行调试代码
⒉ 使用 NDEBUG 预处理变量以及 assert 预处理宏
定义在头文件cassert中,常用来检查不可能发生的状况,形式为
assert(表达式)
如果表达式结果为 false, assert 输出信息并终止程序
如果定义了 NDEBUG 预处理变量,assert 将被忽略,不会产生任何运行时代价
⒊ 预处理器定义了四种在调试时有用的常量
__FILE__ 文件名
__LINE__ 当前行号
__TIME__ 文件被编译时间
__DATE__ 文件被编译日期