学习目标:
语句
学习内容:
一、简单语句
-
表达式语句: 以;结束的,一个;表示当前语句结束了。表达式语句的作用是执行表达式并丢弃掉求值结果。
c = a + b; // 计算a+b的值,并把值赋值给c,做后丢弃计算值 a + b; // 这是一句没用意义的表达式语句 cout << a;
-
空语句:空语句只有一个单独的分号; 别漏写分号,也别多写分号;
while (cin >> s && s != "end"); // while语句只包含一句空语句 c = a + b;; // 这里其实是两个语句 包含一个表达式语句和一个空语句 while(iter != v.end()); // 多了一个分号,while循环就结束了 下面的语句就无效了 ++iter;
-
复合语句:用{}括起来的语句和声明的序列,称为复合语句,也称为块,一个块就是一个作用域。在块中定义的名字只在这个块内是有效的。空块作用等价于空语句。
while(val <= 10){ // {}内是一个块 也是一个复合语句 sum += val; ++val; }
二、语句作用域
-
定义在控制结构(if、switch、while、for)内部的变量,只能在内部使用,一旦超过控制结构部分,就不能使用了;
while(int i = get_num()) cout << i << endl; i = 0; // 报错 超过范围了
-
如果需要在控制结构外部使用变量,那么这个变量就必须定义在控制语句之前;
// 查找vector中第一个负值 auto beg = v.begin(); while(beg != v.end() && *beg > 0) ++beg; if(beg != v.end()) // 此时找到第一个负值
三、条件语句
1.if语句
-
两种形式
if(condition) // condition为真 执行statement statement if(condition) // condition为真 执行statement1 statement1 else // condition为假 执行statement2 statement2
-
嵌套if语句
const vector<string> scores = {"F", "D", "C", "B", "A", "A++"}; int grade = 99; string lettergrade; if(grade < 60){ lettergrade = scores[0]; }else{ lettergrade = scores[(grade - 50) / 10]; if(grade != 100){ if(grade % 10 > 7) // 8、9 给个+ lettergrade += '+'; else if(grade % 10 < 3) // 0 1 2 给个- lettergrade += '-'; } } cout << lettergrade << endl;
-
if-else应该都用花括号{}隔开,增加代码的可读性,也减少错误的可能问题
if(grade != 100){ // 本意:89 N+ 012 N- 3~7 N // 现在:89 N+ 012 N 3~7 N- if(grade % 10 > 3) // 1 if(grade % 10 > 7) // 2 lettergrade += '+'; // 这个else虽然是和1位置对齐,但是它是属于它最近的if 也就是2的 else lettergrade += '-'; }
花括号修改:
if(grade != 100){ if(grade % 10 > 3){ // 1 if(grade % 10 > 7) // 2 lettergrade += '+'; } else{ // 花括号括起来,那么这个else就和1处的if对齐了 lettergrade += '-'; } }
2. switch语句
-
switch可以从若干个固定选项中做出选择。一般形式:
switch(expression){ case A: xxx; break; case B: xxx; break; case C: xxx; break; default: xxx; break; }
执行顺序:首先对switch中的表达式expression求值,再将求值结果转换为整数类型,然后将整数类型和每个case标签的值作对比,如果哪个匹配成功,就从该标签下的第一条语句开始执行,直到碰到break语句或者switch结尾为止。如果匹配不成功,就执行default下面的语句。
char ch; int acnt = 0, ecnt = 0, icnt = 0, ocnt = 0, ucnt = 0; cin >> ch; // 输入'a' acnt=1 ecnt=1 icnt=1 ocnt=1 ucnt=1 switch(ch){ case 'a': ++acnt; case 'e': ++ecnt; // 如果这里加一个break; 输入'a' acnt=1 ecnt=1 icnt=0 ocnt=0 ucnt=0 case 'i': ++icnt; case 'o': ++ocnt; case 'u': ++ucnt; }
-
case标签后面对应的值一定是常量表达式;
char ch = getVal(); int ival = 42; switch(ch){ case 3.14: // 错误 case后的值必须是整数 case ival: // 错误 case后的值必须是常量 }
-
最好在switch内写好该写的break和default;
-
多个case如果执行的相同的功能,其实是可以合并写的;
// 统计元音和非元音的个数 switch(ch){ // 相同的功能,可以叠在一起写 case 'a': case 'e': case 'i': case 'o': case 'u': ++vowelCnt; break; default: ++otherCnt; break; }
-
如果在case中定义并初始化一个变量,最好是用{},确定其作用域,防止发生错误;
bool flag = true; switch(flag){ case true: string file_name; // 错误 含义隐式初始化 为"" int ival = 0; // 错误 显式初始化 int jval; // 正确 只定义 没有初始化 break; case false: jval = 2; // 赋值 正确 }
修改,增加{}:
string file_name; bool flag = true; switch(flag){ case true: { string file_name; // 正确 但是作用域只在当前case int ival = 0; // 正确 但是作用域只在当前case int jval; break; } case false: jval = 2; // 错误 超出作用域 if(file_name.empty()): // 错误 超出作用域 cout << "empty" << endl; break; }
四、 迭代语句
while和for是先检查条件,再执行循环体;do while是先执行一次循环体,再检查条件。
1. while语句
-
只要条件condition为真 就一直执行循环体statement;condition可以是表达式也可以是带初始化的变量声明,但是不能为空。
while(condition) statement
-
当不清楚要迭代多少次时,使用while循环比较合适,比如cin>>a;
vector<int> v; int i; while(cin >> i){ v.push_back(i); }
2. 传统for语句
-
一般情况:init_statement负责初始化一个值,它只在for循环中执行一次;condition是循环控制条件,为真就执行一次statement,为假for循环就结束;expression一般用来修改init_statement初始的值;
for(init_statement; condition; expression) statement;
-
init_statement可以是声明语句、表达式语句或者空语句;init_statement可以定义多个对象,但是只能有一条声明语句,所以多个对象必须是相同类型的;
vector<int> v = {1, 2, 3}; // i 和 sz的类型必须一致 for(decltype(v.size()) i = 0, sz = v.size(); i != sz; ++i) v.push_back(v[i]);
-
for语句中定义的对象的作用域只能在for循环内,超出for循环不能使用;
-
for语句可以省略init_statement、condition和expression中的任何一个,甚至是全部;
i. 省略init_statementauto beg = v.beg(); for(; beg != v.end() && *beg >= 0; ++beg) ;
ii. 省略condition,相当于在条件部分写了个true,在循环体中必须要有语句负责退出循环,否则将一直循环下去:
for(int i = 0; ; ++i) // 循环体中必须要有终止迭代的程序 如break等
iii. 省略expression,但是这样就要求条件部分或者循环体内必须可以改变迭代变量的值;
vector<int> v; for(int i = 0; i != 10; ){ v.push_back(i); ++i; }
3. 范围for语句
-
执行流程:expression必须是一个序列,declaration声明一个变量,序列中每个元素都可以转换为这个变量的类型,每次都会从新初始化这个变量,即每次从序列中拿到下一个元素,之后执行statement语句,直到序列中的元素都取完为止;
for(declaration : expression) statement
-
为了确保变量类型和序列中元素类型一致,一般都用auto自动转换;
vector<int> v = {1, 2, 3}; for(auto &r : v) r *= 2;
4. do while语句
-
do while语句和while语句差不多,唯一的区别:do while语句会先执行一次循环体,在判断条件是否成立,所以do while比较适合用在至少要执行一次循环体的情况下;
do statement; while(condition);
-
do while用的很少,了解下就可以了;
-
变量定义不要定义在条件中:
do{ a... }while(int a = ..) // 变量定义在变量出现之后 错误
五、 跳转语句
1. break语句
-
break语句终止离他最近的while、do while、for或switch语句,并从这些语句后的第一条语句开始继续执行;
string buf; while(cin >> buf && !buf.empty()){ switch(buf[0]){ case '-': for(...){ if(...) break; // 离开for } break; // 离开switch } break; // 离开while }
2. continue语句
-
continue语句终止最近循环中的当前迭代并立即开始下一次迭代(循环还在继续);
// 读取单词,且只操作那些第一个字母不是_的单词 string buf; while(cin >> buf && !buf.empty()){ if(buf[0] != '_') continue // 结束当前迭代,继续输入buf 开始下一次迭代 // 处理语句 }
-
continue只能用于while、for、do while等迭代语句中;
3. goto语句
- goto语句可以无条件跳转到同一函数的另一条语句,但是c++尽量不要使用goto语句;
六、try语句和异常处理
1. throw表达式
-
throw表达式可以在程序发生异常时,抛出异常,终止程序,并不会对异常进行任何的处理;
Sales_item item1, item2; cin >> item1 >> item2; if(item1.isbn() != item2.isbn()) throw runtime_error("Data must refer to same ISBN"); cout << item1 + item2 << endl;
2. try表达式
-
try表达式可以用来处理异常。形式:
try{ }catch(exception-declaration){ handler-statements }catch(exception-declaration){ handler-statements }
执行try中的语句,如果发生异常,catch接收异常,对比异常类型属于哪一个catch,执行相应catch中的异常处理方式;
Sale_item item1, item2; while(cin >> item1, item2) { try{ item1 += item2; }catch(runtime_error err){ // 异常处理 // 打印异常信息 判断是否重新输入 cout << err.what() << "\n Try Again? Entry y or n" << endl; char c; cin >> c; if(!cin || c == 'n') break }catch(exception-declaration){ handler-statements } }
3. 标准异常
-
c++标准库中定义了一组类,用于报告标准库函数遇到的问题。
i. exception头文件定义了最普通的异常类,它只报告异常类的发生,不提供是什么异常;
ii. stdexcept头文件定义了几种常见的异常类,可以提供是什么具体的异常;
iii. new头文件定义了bad_alloc异常类型;
iv. type_info头文件定义了bad_cast异常类型; -
stdexcept头文件定义的常见异常类
i. exception:最常见的问题
ii. runtime_error:运行时才能检测的问题;
iii. range_error: 运行时错误,超出值域范围;
iv. overflow_error:运行时错误,计算上溢;
v. underflow_error:运行时错误,计算下溢;
vi. logic_error:程序逻辑错误;
vii. domin_error: 逻辑错误,参数对应的结果值不存在;
viii. invalid_argument: 逻辑错误,无效参数;
ix. length_errr: 逻辑错误,超出对象最大长度;
x. out_of_range: 逻辑错误,超出有效范围的值;