施工提示
本博文尚在施工中,内容并未完成且可能变更。
控制流语句
C++ 的控制流语句包括:
循环语句
条件语句
跳转语句
异常处理语句
循环语句:for 与 while
C++ 循环语句可以分为 for 与 while 两大类:
for 循环:最常用的循环体,常用于已知循环次数(或循环迭代对象)的情形。
常规 for 循环:控制循环变量的变化,以此来完成给定次数的循环。
范围 for 循环 C++ 11 :允许指定迭代对象来进行循环,而不用显式地指明循环次数。
while 循环:循环次数未知,或要在循环体之后使用循环变量。
do..while 循环:与 while 类似,用于循环体无论如何也要执行一次的情形。
常规 for 循环
for 循环的循环头包含三个部分:初始化语句、循环条件,以及表达式。
初始化语句中的对象 在离开 for 循环之后不可使用 。
下例将从 0 到 3 的整数累加:
1#include <iostream>
2using std::cout;
3using std::endl;
4
5int main() {
6 int s = 0;
7 for (int k = 0; k < 4; ++k) {
8 s += k;
9 }
10 cout << "Sum = " << s << endl;
11 return 0;
12}
13/*
14* 输出:6
15*/
C++ 中循环体如果只包含一条语句,可以不放在花括号内。例如:
for (int k = 0; k < 4; ++k) s +=k;
for (int j = 0; j < 10; ++j) ; // 空语句
范围 for 循环
范围 for 循环 C++ 11 使用冒号 :
来表示对迭代对象的元素进行遍历。
下例实现了对指定向量中的所有数字累加求和。关于向量,请参考 向量(std::vector) 一节。
1#include <iostream>
2#include <vector>
3using std::cout;
4using std::endl;
5using std::vector;
6
7int main() {
8 vector<int> vec = {1, 3, 5, 7};
9 int s = 0;
10 for (int k: vec) { // 遍历向量 vec 的每个元素
11 s += k;
12 }
13 cout << "Sum = " << s << endl;
14 return 0;
15}
16/*
17* 输出:16
18*/
范围 for 循环的另一个不同之处在于可以方便地使用引用,避免额外开销:
for (const int &k: vec) { // 使用引用
s += k;
}
while 循环
用 while 循环来处理未知循环次数的任务。下例从用户输入中读取若干个数字,并将它们相加:
1#include <iostream>
2using std::cout;
3using std::cin;
4using std::endl;
5
6int main() {
7 int s = 0, k = 0;
8 // 输入 Ctrl + Z 或 Ctrl + D 来标识输入流结束
9 while (cin >> k) {
10 s += k;
11 }
12 cout << "Sum = " << s << endl;
13 return 0;
14}
15/*
16* 输入:1 3 7 5 9^D
17* 输出:Sum = 25
18*/
cin 的读取何时会终止?
while(cin >> k)
表示从输入流依次地读取内容,并赋值给变量 k。这种 cin 的读入行为在以下情况终止:
读取到输入流的末尾(EoF)。对输入文件来说,EoF 就是它的最后内容;对控制台的用户输入来说,EoF 需要手动地输入:在 Windows 上,使用 Ctrl + D;在类 Unix 上,使用 Ctrl + Z。
读取到异常内容。上例中,限于变量 k 的类型,用户输入的内容应当是 int 类型的数字;如果用户输入其他内容(例如字母、符号),
cin
读取将会终止。
在上例中,如果输入 1 2 xyz 4
,程序将输出 Sum = 3
。
do..while 循环*
do..while 循环与 while 区别很小——除了它会先执行一次循环体后,再检查循环条件。也就是说,do..while 循环的循环体最少也会被执行一次。
请注意 do..while 的结尾需要有分号。
1#include <iostream>
2using std::cout;
3using std::cin;
4using std::endl;
5
6int main() {
7 int s = 0, k = 0;
8 do {
9 s += k;
10 } while (cin >> k); // 请勿忘记这个分号
11 cout << "Sum = " << s << endl;
12 return 0;
13}
14/*
15* 输入:1 4 7 2^D
16* 输出:Sum = 14
17*/
跳转语句:break 与 continue
警告
由于脆弱且可读性差,不建议在代码中使用 goto 语句。
跳转语句报考 break, continue 与 goto 三种。这里只介绍 break 与 continue 这两种跳转语句。
break:结束并跳出当前循环体,执行循环体之后的代码。
continue:结束当前迭代,执行循环的下一个迭代。
下例统计了字符串中第一个单词(以空格分割单词)的长度及其包含的字母 o 的数量。
1#include <iostream>
2#include <string>
3using std::cout;
4using std::endl;
5using std::string;
6
7int main() {
8 int count_o = 0, count_firstword = 0;
9 string s = "Hello world";
10 for (char c: s) {
11 if (c == 'o') {
12 count_o += 1;
13 count_firstword += 1;
14 continue;
15 } else if (c == ' ') {
16 break;
17 } else {
18 count_firstword += 1;
19 }
20 }
21 cout << "Count of 'o' = " << count_o << endl;
22 cout << "Length of first word = " << count_firstword << endl;
23 return 0;
24}
25/*
26 输出:
27 Count of 'o' = 1
28 Length of first word = 5
29*/
条件语句:if 与 switch
if 语句:根据条件判断是否执行条件体。它支持 if..else 形式以及嵌套的 if..else if 形式。
switch 语句:根据给定选项判断条件符合哪一种,然后执行对应的条件体。
if 语句
下例的 if..else 嵌套条件语句能够根据用户输入的整数,判断其正负号:
C++ 支持
else if
语法块。
1#include <iostream>
2using std::cout;
3using std::cin;
4using std::endl;
5
6int main() {
7 int val;
8 cin >> val; // 读取用户输入
9 char sign;
10 if (val > 0) {
11 sign = '+';
12 } else if (val < 0) {
13 sign = '-';
14 } else {
15 sign = '0';
16 }
17 cout << "Sign of " << val << " is " << sign << endl;
18 return 0;
19}
20/*
21* 输入:100
22* 输出:Sign of 100 is +
23*/
switch 语句
switch 语句将执行符合 case 标签的每一个语句直到 switch 的结尾,或者到发现 break 为止。
因此,一般在每个 case 标签的末尾都会写有
break;
语句。case 标签必须是整型常量表达式。
int val = 7; // 错误:以下 case 标签不合法,因为它们不是整型常量表达式 case 1.414: case val:
在同一 switch 语句内部,不能有相同的 case 标签。
switch 语句允许特殊的
default
关键字,表示在没有 case 匹配时需执行的操作。
下例分别计数了字符串中字符 a, b, c 以及它们以外的字符出现的次数:
1#include <iostream>
2#include <string>
3#include <vector>
4using std::cout;
5using std::endl;
6using std::string;
7using std::vector;
8
9int main() {
10 string s = "abaccbba13caabbbcc539207aacb";
11 vector<string> targets {"other", "a", "b", "c"};
12 vector<int> counts(targets.size() + 1, 0);
13 int total = 0;
14 for (char c: s) {
15 switch (c) {
16 case 'a':
17 ++counts[1];
18 break;
19 case 'b':
20 ++counts[2];
21 break;
22 case 'c':
23 ++counts[3];
24 break;
25 default: // 其他情形
26 ++counts[0];
27 break;
28 }
29 }
30 for (int i = 0; i < targets.size(); ++i) {
31 cout << "Count of '" << targets[i] << "': "
32 << counts[i] << endl;
33 }
34 return 0;
35}
36/*
37 输出:
38 Count of 'other': 8
39 Count of 'a': 7
40 Count of 'b': 7
41 Count of 'c': 6
42*/
一个常用技巧是,在 switch 的多个 case 需要执行的操作相同时,合并它们。下例统计了字母 a, b, c 的总数:
int count_abc = 0;
// ...
switch(c) {
// 将多个 case 写在同一行内或拆成多行均可
case 'a': case 'b': case 'c':
count_abc += 1;
break;
}
// ...
异常语句:try 与 throw
重要
try..catch 与 throw 语句是异常处理部分的内容。本节只对语句语法作介绍,而不涉及异常处理的详细知识。
try..catch 语句
try..catch 语句捕获异常并允发出有关该异常的信息。
try 语句块后至少需要一个 catch 语句块,也可以是多个。
通常需要包含
<stdexcept>
库。常用的异常是std::runtime_error
。
下例尝试将 string 转为 double,并在无法转换时捕获 std::invalid_argument
异常:
error 对象的
what()
方法将返回一个 C 风格字符串,用于提示错误信息。本例中将错误信息整理后输出到标准错误
std::cerr
。
1#include <iostream>
2#include <string>
3#include <stdexcept>
4using std::cout;
5using std::cerr;
6using std::endl;
7using std::string;
8
9int main() {
10 string s = "exp=2.718";
11 double x = 0;
12 try {
13 x = std::stod(s); // string 到 double 转换函数
14 } catch (std::invalid_argument err) {
15 cerr << "Invalid arg: " << err.what()
16 << "(\"" << s <<"\")" << endl;
17 }
18 cout << "x = " << x << endl;
19 return 0;
20}
21/*
22 输出:
23 Invalid arg: stod("exp=2.718")
24 x = 0
25*/
throw 语句
throw 语句用于直接抛出异常。
下面是一个简单的例子。在 int 除法时,若检查到除数为 0 就会抛出异常:
1#include <iostream>
2#include <stdexcept>
3using std::cout;
4using std::cerr;
5using std::endl;
6
7int main() {
8 int x = 7, y = 0;
9 int result = 0;
10 if (y == 0) {
11 throw std::runtime_error("Division by zero.");
12 }
13 // 如果抛出了异常,以下内容将不会被执行
14 cout << "x // y = " << x / y << endl;
15 return 0;
16}
17/*
18 输出:
19 terminate called after throwing an instance of 'std::runtime_error'
20 what(): Division by zero.
21*/
请注意,上例中的异常被抛出,但并没有 try..catch 来捕获它,因此程序在抛出异常后就终止了。通常我们在抛出异常时也会捕获它,请参考下一节的例子。
完整的异常处理例子
一般地,我们将 throw 写在被调函数内部,并在外部调用函数时用 try..catch 捕获可能的异常。
下面是一个更完整的异常抛出与捕获的例子,仍然是处理整数除法中除数为 0 的问题。本例中结合使用了 throw 与 try..catch 语句。
1#include <iostream>
2#include <stdexcept>
3using std::cout;
4using std::cerr;
5using std::endl;
6
7int int_div(int numerator, int denominator) {
8 if (denominator == 0) {
9 throw std::runtime_error("Error: Division by zero.");
10 }
11 return numerator / denominator;
12}
13
14int main() {
15 int x = 7, y = 0;
16 int result = 0;
17 try {
18 result = int_div(x, y);
19 } catch (std::runtime_error err) {
20 cerr << err.what() << endl;
21 cout << "result = N/A" << endl;
22 }
23 // 如果异常被成功捕获,那么以下内容会被正常执行
24 cout << "~ A message after division." << endl;
25 return 0;
26}
27/*
28 输出:
29 Error: Division by zero.
30 result = N/A
31 ~ A message after division.
32*/