C++ Primer 4 第六章 语句

 

第六章 语句

1. 简单语句

程序语句最简单的形式是空语句,它使用以下的形式(只有一个单独的分号):;  // null statement

2. 声明语句

3. 复合语句(块)

复合语句,通常被称为块,是用一对花括号括起来的语句序列(也可能是空的)。块标识了一个作用域,在块中引入的名字只能在该块内部或嵌套在块中的子块里访问。通常,一个名字只从其定义处到该块的结尾这段范围内可见。

与其他大多数语句不同,块并不是以分号结束的。

4. 语句作用域

在条件表达式中定义的变量必须初始化,该条件检验的就是初始化对象的值。

在语句的控制结构中定义的变量,仅在定义它们的块语句结束前有效。这种变量的作用域限制在语句体内。

5. if 语句

如果在条件表达式中定义了变量,那么变量必须初始化。将已初始化的变量值转换为 bool 值后,该 bool 值决定条件是否成立。变量类型可以是任何可转换为 bool 型的类型,这意味着它可以是算术类型或指针类型。一个类类型能否用在条件表达式中取决于类本身。如IO 类型可以用作条件,但 vector 类型和 string 类型一般不可用作条件。

6. switch 语句

每个 case 标号的值都必须是一个常量表达式。除此之外,还有一个特殊的 case 标号——default 标号。

为了避免继续执行其后续 case 标号的内容,程序员必须利用 break 语句清楚地告诉编译器停止执行 switch 中的语句。

case 标号必须是整型常量表达式。例如,下面的标号将导致编译时的错误:

     case 3.14:  // noninteger

     case ival:  // nonconstant

每个 case 标号不一定要另起一行。为了强调这些 case 标号表示的是一个要匹配的范围,可以将它们全部在一行中列出:

     switch (ch)

     {

         case 'a': case 'e': case 'i': case 'o': case 'u':

             ++vowelCnt;

             break;

     }

如果所有的 case 标号与 switch 表达式的值都不匹配,并且 default 标号存在,则执行 default 标号后面的语句。

如果 switch 结构以 default 标号结束,而且 default 分支不需要完成任何任务,那么该标号后面必须有一个空语句。

对于 switch 结构,只能在它的最后一个 case 标号或 default 标号后面定义变量:

     case true:

          // error: declaration precedes a case label

          string file_name = get_file_name();

          break;

     case false:

          // ...

制定这个规则是为避免出现代码跳过变量的定义和初始化的情况。回顾变量的作用域,变量从它的定义点开始有效,直到它所在块结束为止。现在考虑如果在两个 case 标号之间定义变量会出现什么情况。该变量会在块结束之前一直存在。对于定义该变量的标号后面的其他 case 标号,它们所关联的代码都可以使用这个变量。如果 switch 从那些后续 case 标号开始执行,那么这个变量可能还未定义就要使用了。

7. while 语句

在循环条件中定义的变量在每次循环里都要经历创建和撤销的过程。

8. for 循环语句

for 语句头中,可以省略 init-statement、condition 或者 expression(表达式)中的任何一个(或全部)。省略 condition,则等效于循环条件永远为 true。

可以在 for 语句的 init-statement 中定义多个对象;但是不管怎么样,该处只能出现一个语句,因此所有的对象必须具有相同的一般类型:

         for (int ival = 0, *pi = ia, &ri = val;

           ival != size;

           ++ival, ++pi, ++ri)

                   // ...

9. do while 语句

与 while 语句不同。do-while 语句总是以分号结束。

10. break 语句

break 只能出现在循环或 switch 结构中,或者出现在嵌套于循环或 switch 结构中的语句里。对于 if 语句,只有当它嵌套在 switch 或循环里面时,才能使用 break。break 出现在循环外或者 switch 外将会导致编译时错误。当 break 出现在嵌套的 switch 或者循环语句中时,将会终止里层的 switch 或循环语句,而外层的 switch 或者循环不受影响。

11. continue 语句

continue 语句导致最近的循环语句的当次迭代提前结束。对于 while 和 do while 语句,继续求解循环条件。而对于 for 循环,程序流程接着求解 for 语句头中的 expression 表达式。

continue 语句只能出现在 for、while 或者 do while 循环中,包括嵌套在这些循环内部的块语句中。

12. goto 语句

goto 语句提供了函数内部的无条件跳转,实现从 goto 语句跳转到同一函数内某个带标号的语句。

13. try 块和异常处理

异常机制提供程序中错误检测与错误处理部分之间的通信。C++ 的异常处理中包括:

         1)throw 表达式,错误检测部分使用这种表达式来说明遇到了不可处理的错误。可以说,throw 引发了异常条件。

         2)try 块,错误处理部分使用它来处理异常。try 语句块以 try 关键字开始,并以一个或多个 catch 子句结束。在 try 块中执行的代码所抛出(throw)的异常,通常会被其中一个 catch 子句处理。由于它们“处理”异常,catch 子句也称为处理代码。

         3)由标准库定义的一组异常类,用来在 throw 和相应的 catch 之间传递有关的错误信息。

每一个标准库异常类都定义了名为 what 的成员函数。这个函数不需要参数,返回 C 风格字符串。

寻找处理代码的过程与函数调用链刚好相反。抛出一个异常时,首先要搜索的是抛出异常的函数。如果没有找到匹配的 catch,则终止这个函数的执行,并在调用这个函数的函数中寻找相配的 catch。如果仍然找到相应的处理代码,该函数同样要终止,搜索调用它的函数。如此类推,继续按执行路径回退,直到找到适当类型的 catch 为止。

如果不存在处理该异常的 catch 子句,程序的运行就要跳转到名为 terminate 的标准库函数,该函数在 exception 头文件中定义。这个标准库函数的行为依赖于系统,通常情况下,它的执行将导致程序非正常退出。

在程序中出现的异常,如果没有经 try 块定义,则都以相同的方式来处理:毕竟,如果没有任何 try 块,也就没有捕获异常的处理代码(catch 子句)。此时,如果发生了异常,系统将自动调用 terminate 终止程序的执行。

C++ 标准库定义了一组类,用于报告在标准库中的函数遇到的问题。程序员可在自己编写的程序中使用这些标准异常类。标准库异常类定义在四个头文件中:

         1) exception 头文件定义了最常见的异常类,它的类名是 exception。这个类只通知异常的产生,但不会提供更多的信息。

         2) stdexcept 头文件定义了几种常见的异常类

         3) new 头文件定义了 bad_alloc 异常类型,提供因无法分配内在而由 new抛出的异常。

         4) type_info 头文件定义了 bad_cast 异常类型

14. 使用预处理器进行调试

程序所包含的调试代码仅在开发过程中执行。当应用程序已经完成,并且准备提交时,就会将调试代码关闭。可使用 NDEBUG 预处理变量实现有条件的调试代码:

     int main()

     {

     #ifndef NDEBUG

              cerr << "starting main" << endl;

     #endif

     // ...

预处理器还定义了其余四种在调试时非常有用的常量:

__FILE__ 文件名

__LINE__ 当前行号

__TIME__ 文件被编译的时间

__DATE__ 文件被编译的日期

另一个常见的调试技术是使用 NDEBUG 预处理变量以及 assert 预处理宏,assert 宏是在 cassert 头文件中定义的.

assert 宏需要一个表达式作为它的条件:

         assert(expr)

只要 NDEBUG 未定义,assert 宏就求解条件表达式 expr,如果结果为 false,assert 输出信息并且终止程序的执行。如果该表达式有一个非零(例如,true)值,则 assert 不做任何操作。

与异常不同(异常用于处理程序执行时预期要发生的错误),程序员使用 assert 来测试“不可能发生”的条件。例如,对于处理输入文本的程序,可以预测全部给出的单词都比指定的阈值长。那么程序可以包含这样一个语句:

     assert(word.size() > threshold);

在测试过程中,assert 等效于检验数据是否总是具有预期的大小。一旦开发和测试工作完成,程序就已经建立好,并且定义了 NDEBUG。在成品代码中,assert 语句不做任何工作,因此也没有任何运行时代价。当然,也不会引起任何运行时检查。assert 仅用于检查确实不可能的条件,这只对程序的调试有帮助,但不能用来代替运行时的逻辑检查,也不能代替对程序可能产生的错误的检测。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值