第六章 语句 statements
一般语句是顺序执行的。控制流语句,允许又提案件的执行或者重复执行部分功能。
if、switch语句为条件分支结构
for、while、do while是循环迭代语句
6.2 表达式语句以分号结束,空语句(null statements) ;(单独一个分号作为一个程序语句)
while (cin >> s && s != sought)
; // null statement 最好加上注释能够知道该语句是有意省略的
6.3 复合语句(compound statements)块语句是用一对花括号成立的
6.4 语句作用域
// index is visible only within the for statement
for (vector<int>::size_type index = 0;
index != vec.size(); ++index)
{ // new scope, nested within the scope of this for statement
int square = 0;
if (index % 2) // ok: index is in scope
square = index * index;
vec[index] = square;
}
if (index != vec.size()) // error: index is not visible here
在控制结构里引入的名字是该语句的局部变量,其作用域局限在语句内部。
// index is visible only within the for statement
for (vector<int>::size_type index = 0;
index != vec.size(); ++index)
{ // new scope, nested within the scope of this for statement
int square = 0;
if (index % 2) // ok: index is in scope
square = index * index;
vec[index] = square;
}
if (index != vec.size()) // error: index is not visible here
如果程序需要访问某个控制结构中的变量,那么这个变量必须在控制语句外部定义。
6.5 if语句
当多个语句必须作为单个语句执行时,比较常见的错误是漏掉了花括号。
悬垂else问题:所有语言的 if 语句都普通存在着潜在的二义性。C++ 中悬垂 else 问题带来的二义性,通过将 else 匹配给最后出现的尚未匹配的 if 子句来解决,可以通过用花括号将内层的 if 语句括起来成为复合语句,从而迫使这个 else 子句与外层的 if 匹配。
6.6 switch语句
char ch;
// initialize counters for each vowel
int aCnt = 0, eCnt = 0, iCnt = 0,
oCnt = 0, uCnt = 0;
while (cin >> ch) {
// if ch is a vowel, increment the appropriate counter
switch (ch) {
case 'a':
++aCnt;
break;
case 'e':
++eCnt;
break;
case 'i':
++iCnt;
break;
case 'o':
++oCnt;
break;
case 'u':
++uCnt;
break;
}
}
// print results
cout << "Number of vowel a: \t" << aCnt << '\n'
<< "Number of vowel e: \t" << eCnt << '\n'
<< "Number of vowel i: \t" << iCnt << '\n'
<< "Number of vowel o: \t" << oCnt << '\n'
<< "Number of vowel u: \t" << uCnt << endl;
关键字case和所关联的值称为case标号,case标号必须是整型常量表达式
尽管没有严格要求在 switch 结构的最后一个标号之后指定 break 语句,但是,为了安全起见,最好在每个标号后面提供一个 break 语句,即使是最后一个标号也一样。如果以后在 switch 结构的末尾又需要添加一个新的 case 标号,则不用再在前面加 break 语句了。
case 'a': case 'A': 写成case 'a', 'A': 冒号里面不能有逗号
优先级 | 运算符 | 说明 | 结合性 |
---|---|---|---|
1 | :: | 范围解析 | 自左向右 |
2 | ++ -- | 后缀自增/后缀自减 | |
() | 括号 | ||
[] | 数组下标 | ||
. | 成员选择(对象) | ||
−> | 成员选择(指针) | ||
3 | ++ -- | 前缀自增/前缀自减 | 自右向左 |
+ − | 加/减 | ||
! ~ | 逻辑非/按位取反 | ||
(type) | 强制类型转换 | ||
* | 取指针指向的值 | ||
& | 某某的地址 | ||
sizeof | 某某的大小 | ||
new,new[] | 动态内存分配/动态数组内存分配 | ||
delete,delete[] | 动态内存释放/动态数组内存释放 | ||
4 | .* ->* | 成员对象选择/成员指针选择 | 自左向右 |
5 | * / % | 乘法/除法/取余 | |
6 | + − | 加号/减号 | |
7 | << >> | 位左移/位右移 | |
8 | < <= | 小于/小于等于 | |
> >= | 大于/大于等于 | ||
9 | == != | 等于/不等于 | |
10 | & | 按位与 | |
11 | ^ | 按位异或 | |
12 | | | 按位或 | |
13 | && | 与运算 | |
14 | || | 或运算 | |
15 | ?: | 三目运算符 | 自右向左 |
16 | = | 赋值 | |
+= −= | 相加后赋值/相减后赋值 | ||
*= /= %= | 相乘后赋值/相除后赋值/取余后赋值 | ||
<<= >>= | 位左移赋值/位右移赋值 | ||
&= ^= |= | 位与运算后赋值/位异或运算后赋值/位或运算后赋值 | ||
17 | throw | 抛出异常 | |
18 | , | 逗号 | 自左向右 |
6.7 while语句
// arr1 is an array of ints
int *source = arr1;
size_t sz = sizeof(arr1)/sizeof(*arr1); // number of elements
int *dest = new int[sz]; // uninitialized elements
while (source != arr1 + sz)
*dest++ = *source++; // copy element and increment pointers
*dest++ = *source++;
是一个经典的例子。这个表达式等价于:
{
*dest = *source; // copy element
++dest; // increment the pointers
++source;
}
语句 *dest++ = *source++; 执行过程如下:
(1)指针dest加1;
(2)指针source加1;
(3)将source原来所指向的对象赋给dest原来所指向的对象;
例题6.11 空格 ' ' 制表符 '\t' 换行符 '\n'
6.8 for循环
for(initializer; condition; expression) 初始声明必须是声明语句、表达式语句或空语句;必须有两个分号进行隔离
statement
必须要有一个分号表明init-statement,分号即代表一个init-statement
for(/*null*/;iter != l.end();++i) for(int i = 0; / * no condition */; ++i)
for(int i = 0; true; ++i) 循环体内必须包含一个break或return语句,否则,循环将会一直执行到耗尽系统资源为止
例题6.16
6.16
6.9 do-while语句 实现与用户的交互计算和判断
do
statement
while (condition); 必须要有分号结束 保证循环体至少循环一次
6.18
6.10 break语句
主要用于结束最近的while、do while、for和switch语句,将执行权传递给被终止语句之后的语句。
6.20
continue语句只能出现在for、while或者do while循环中,包括嵌套的循环内部的块语句中。
下面的循环每次从标准输入中读入一个单词,只有以下划线开头的单词才做处理。如果是其他的值,终止当前循环,接着读取下一个单词
string inBuf;
while (cin >> inBuf && !inBuf.empty()) {
if (inBuf[0] != '_')
continue; // get another input
// still here? process string ...
}
6.13 C++的异常处理:throw表达式:使用这种表达式遇到了不可处理的错误。try块:以try关键字开始,一个或多个catch子句结束,catch语句处理异常,catch子句称为处理代码。
throw表达式:以分号结束,throw表达式的类型决定了所抛出异常的类型。
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 外面引用
标准库异常类的定义:exception、stdexcept、new、type_info。
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 异常类型,提供因无法分配内存而由 new抛出的异常。
type_info 头文件定义了 bad_cast 异常类型,这种类型将第 18.2 节讨论。
异常类型只定义了一个名为 what 的操作。这个函数不需要任何参数,并且返回 const char* 类型值。它返回的指针指向一个 C 风格字符串(第 4.3 节)。使用 C 风格字符串的目的是为所抛出的异常提出更详细的文字描述
what 函数所返回的指针指向 C 风格字符数组的内容,这个数组的内容依赖于异常对象的类型。对于接受 string 初始化式的异常类型,what 函数将返回该 string 作为 C 风格字符数组。对于其他异常类型,返回的值则根据编译器的变化而不同。