语句
简单语句
空语句
当某个地方语法上需要,逻辑上不需要,则会使用,且应该加注释标明。
1 | //重复读入数据直至到达文件末尾或某次输入的值等于sought |
复合语句
指用花括号括起来的(可能为空的)语句和声明的序列,复合语句也被称作块(block)。一个块就是一个作用域,在块中引入的名字只能在块内部以及嵌套在块中的子块里访问。通常,名字在有限的区域内可见,该区域从名字定义处开始,到名字所在的(最内层)块的结尾为止,通常在语法上需要一条语句,但逻辑上需要多条语句的使用。
所谓空块,是指内部没有任何语句的一对花括号。空块的作用等价于空语句:
语句作用域
可以在if、switch、while和 for语句的控制结构内定义变量。定义在控制结构当中的变量只在相应语句的内部可见,一旦语句结束,变量也就超出其作用范围了。
条件语句
if语句
1 | //if else语句的形式是 |
悬垂else
1 | //错误:实际的执行过程并非像缩进格式显示的那样; else 分支匹配的是内层if语句 |
这里程序将else与内层if匹配,导致错误,加上花括号可避免此问题。
switch语句
1 | switch (ch){ |
break 跳转出switch,case必须是整形常量表达式,一个switch内case不能相同。
内部控制流
有时候故意省略break语句,使得程序连续执行多个标签。如:也许我们想统计的是所有元音字母出现的总次数:
1 | unsigned voweICnt =o; |
default标签
如果没有任何一个case 标签能匹配上 switch 表达式的值,程序将执行紧跟在default 标签(default label)后面的语句。例如,可以增加一个计数值来统计非元音字母的数量,只要在 default分支内不断递增名为otherCnt的变量就可以了。
内部变量定义
如果需要为某个case分支定义并初始化一个变量,我们应该把变量定义在块内,从而确保后面的所有case标签都在变量的作用域之外。
1 | case true:{ |
迭代语句
while循环
当不确定循环多少次时或循环结束后访问循环控制变量
1 | vector<int> v; |
传统for语句
1 | for (init-statemen; condition; expression) |
for语句头中的多重定义
init-statemen可以定义多个变量,但只能定义一种类型。
for语句头省略
for语句头能省略掉init-statement、condition和 expression中的任何一个(或者全部),但分号必须保留。
范围for语句
1 | for (declaration : expression) |
do while语句
do while语句( do while statement)和 while语句非常相似,唯一的区别是,do while语句先执行循环体后检查··条件。不管条件的值如何,我们都至少执行一次循环。do while语句的语法形式如下所示:
1 | do |
do while语句应该在括号包围起来的条件后面用一个分号表示语句结束。
跳转语句
break语句
break语句(break statement)负责终止离它最近的while、do while、for或switch语句,并从这些语句之后的第一条语句开始继续执行。
continue语句
continue语句(continue statement)终止最近的循环中的当前迭代并立即开始下一次迭代。continue语句只能出现在 for、while和 do while循环的内部,或者嵌套在此类循环里的语句或块的内部。和 break语句不同的是,只有当switch语句嵌套在迭代语句内部时,才能在 switch里使用continue,它会使程序直接跳出switch,并重新执行switch外的循环。
goto语句
不要在程序中使用goto语句,因为它使得程序既难理解又难修改。
try语句块和异常处理
throw表达式
1 | sales_item item1, item2; |
在真实的程序中,应该把对象相加的代码和用户交互的代码分离开来。此例中,我们改写程序使得检查完成后不再直接输出一条信息,而是抛出一个异常:
1 | //首先检查两条数据是否是关于同一种书籍的 |
在这段代码中,如果ISBN不一样就抛出一个异常,该异常是类型runtime_error的对象。抛出异常将终止当前的函数,并把控制权转移给能处理该异常的代码。
try语句块
1 | try { |
try语句块的一开始是关键字try,随后紧跟着一个块,跟在try 块之后的是一个或多个catch子句。catch子句包括三部分:关键字catch、括号内一个(可能未命名的)对象的声明(称作异常声明,exception declaration)以及一个块。当选中了某个catch子句处理异常之后,执行与之对应的块。catch 一旦完成,程序跳转到try语句块最后一个catch子句之后的那条语句继续执行。
try语句块中的program-statements组成程序的正常逻辑,像其他任何块一样,program-statements可以有包括声明在内的任意C++语句。一如往常,try语句块内声明的变量在块外部无法访问,特别是在catch子句内也无法访问。
编写处理的代码
1 | while (cin >> item1 >> item2) { |
那么如果输入书籍不同,则会输出两句话:
Data must refer to same ISBN
Try Again? Enter y or n
函数在寻找处理代码的过程中退出(*****)
在复杂系统中,程序在遇到抛出异常的代码前,其执行路径可能已经经过了多个try语句块。例如,一个try语句块可能调用了包含另一个try语句块的函数,新的try语句块可能调用了包含又一个try语句块的新函数,以此类推。
寻找处理代码的过程与函数调用链刚好相反。当异常被抛出时,首先搜索抛出该异常的函数。如果没找到匹配的 catch子句,终止该函数,并在调用该函数的函数中继续寻找。如果还是没有找到匹配的 catch子句,这个新的函数也被终止,继续搜索调用它的函数。以此类推,沿着程序的执行路径逐层回退,直到找到适当类型的catch子句为止。
如果最终还是没能找到任何匹配的 catch子句,程序转到名为terminate的标准库函数。该函数的行为与系统有关,一般情况下,执行该函数将导致程序非正常退出。
处理复杂异常超出了本书的范围。
标准异常(*)
表5.1:
类 | 问题 |
---|---|
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风格字符串初始化这些类型的对象,但是不允许使用默认初始化的方式。当创建此类对象时,必须提供初始值,该初始值含有错误相关的信息。