Basic
语句通常是顺序执行的,而 控制流 flow-of-control 可以支持更复杂的执行路径。
简单语句
表达式语句 expresion statement
一个表达式末尾加上分号就是 表达式语句 。表达式语句的作用是执行表达式并丢弃掉求值结果。
空语句 null statement
只有一个分号的语句,用于在语法上需要但逻辑上不需要语句时:
while(...) ;
后面这个分号是空语句,代表while的循环体。注意while循环体内除非用花括号,否则以分号作为空语句代表循环体结束。
cout<<"Hello";;;
后面两个分号是空语句。
复合语句 compound statement
指花括号括起来的语句,可以为空。又称做 块 block。一个块就是一个作用域。在块中引入的名字只能在块内部及其嵌套的子块内访问。通常名字在有限区域内可见,该区域从名字定义处开始到名字所在最内层块的结尾为止。
在某个地方语法上需要单条语句但逻辑上应使用多条语句时,如while和for的循环体内应使用复合语句。
块不以分号作为结束。没有任何内容的块称为空块,作用同空语句一致。如:while(...){}
没有分号但正确。
语句作用域 statement scope
https://en.cppreference.com/w/cpp/language/scope
条件语句
if语句
if condition
statement
else if condition //嵌套if,可选
statement
else //else语句,可选
statement
悬垂else (dangling else)
为消除多个if和随后else的二义性,C++规定else和最近的一个未匹配的if匹配。若需要匹配更远处的if。则需要使用花括号。
switch语句
Syntax |
---|
attr(optional) switch ( condition ) statement (until C++17)
|
attr(optional) switch ( init-statement(optional) condition ) statement (since C++17)
|
- attr(C++11): any number of attributes
- condition - any expression of integral or enumeration type, or of a class type contextually implicitly convertible to an integral or enumeration type, or a declaration of a single non-array variable of such type with a brace-or-equals initializer.
-
init-statement(C++17) - either
- an expression statement (which may be a null statement ";")
- a simple declaration, typically a declaration of a variable with initializer, but it may declare arbitrarily many variables or structured bindings
- Note that any init-statement must end with a semicolon ;, which is why it is often described informally as an expression or a declaration followed by a semicolon.
- statement - any statement (typically a compound statement).
-
case: and default: labels are permitted in statement and
break;
statement has special meaning.
attr(optional) case constant_expression : statement |
attr(optional) default : statement |
- constant_expression - a constant expression of the same type as the type of condition after conversions and integral promotions
语句示例:统计分数段及人数
int main(){
unsigned cnt=0, cntA=0, cntB=0, cntC=0, cntD=0, cntF=0;
unsigned score;
while (cin>>score) {
char grade = score>=90 ? 'A':
score>=80 ? 'B':
score>=70 ? 'C':
score>=60 ? 'D': 'F';
switch (grade) {
case 'A': ++cntA; break;
case 'B': ++cntB; break;
case 'C': ++cntC; break;
case 'D': ++cntD; break;
case 'F': ++cntF; break;
}
++cnt;
}
cout<<"Total number of students: "<<cnt<<'\n'
<<"Grade A: "<<cntA<<" student\n"
<<"Grade B: "<<cntB<<" student\n"
<<"Grade C: "<<cntC<<" student\n"
<<"Grade D: "<<cntD<<" student\n"
<<"Grade F: "<<cntF<<" student\n";
}
case和它对应的值一起被称为case标签 case label。case标签必须是整型常量表达式。
对于case语句,仅在被循环语句包围时可以使用 continue
。注意continue不仅会结束当前switch,还会结束当前循环语句,是直接作用于最近的一个循环语句而不是switch 。而break仅直接作用于case,因此加花括号块并在case块内break也不影响跳出switch。
注意:在 switch
语句中定义的 default
并不会强制执行,且定义在该块中的 非case语句
是 unreachable
。
switch内部的控制流
若某个case标签匹配成功,则除非程序显式中断该过程,否则会一直到switch结尾才会停下来。即:可跳转入,不跳转出,除非中断。
default语句
用于接收未被case语句匹配的情况。可出现在switch任意位置,但出现在最后且没有case语句接着时,需要在后面添加空语句或空块。
switch和作用域
在switch中case外定义语句会使其unreachable;在case中定义语句有可能因为跳过而无法初始化,所以不能在case中定义变量的显式/隐式初始化(如空string、空vector)。
可以在case中或switch内case外声明,但声明必须写在使用之前,且声明语句所在的case可以unreachable。
int main(){
bool i = 0;
switch (i) {
int c; //unreachable,但有效
case true:
c = 1;
int d; //有效
break;
case false:
c = 0; d = 0;
cout<<c<<d; //有效,但是未初始化的值
}
}
如果在某处一个带初值的变量位于作用域外,而在另一处该变量在作用域内,则从前一位置跳到后一位置是非法行为。
要在单个case内定义变量,则只能初始化在该变量所在块内以确保其作用域不在其他的case语句中。
迭代语句
while语句
前置判断条件:普通while语句
attr(optional, since C++11) while
( condition ) statement
后置判断条件:do while语句
attr(optional, since C++11) do
statement while
( expression ) ;
attr: https://en.cppreference.com/w/cpp/language/attributes
for语句
formal syntax: |
---|
attr(optional) for ( init-statement condition(optional) ; iteration_expression(optional) ) statement
|
informal syntax: |
---|
attr(optional) for ( declaration-or-expression(optional) ; declaration-or-expression(optional) ; expression(optional) ) statement |
#include <iostream>
#include <vector>
int main()
{
// typical loop with a single statement as the body
for (int i = 0; i < 10; ++i)
std::cout << i << ' ';
std::cout << '\n';
// init-statement can declare multiple names, as long as they
// can use the same decl-specifier-seq
for (int i = 0, *p = &i; i < 9; i += 2) {
std::cout << i << ':' << *p << ' ';
}
std::cout << '\n';
// condition may be a declaration
char cstr[] = "Hello";
for (int n = 0; char c = cstr[n]; ++n)
std::cout << c;
std::cout << '\n';
// init-statement can use the auto type specifier
std::vector<int> v = {3, 1, 4, 1, 5, 9};
for (auto iter = v.begin(); iter != v.end(); ++iter) {
std::cout << *iter << ' ';
}
std::cout << '\n';
// init-statement can be an expression
int n = 0;
for (std::cout << "Loop start\n";
std::cout << "Loop test\n";
std::cout << "Iteration " << ++n << '\n')
if(n > 1)
break;
std::cout << '\n';
}
output:
0 1 2 3 4 5 6 7 8 9
0:0 2:2 4:4 6:6 8:8
Hello
3 1 4 1 5 9
Loop start
Loop test
Iteration 1
Loop test
Iteration 2
Loop test
range-based for(C++11)
Syntax |
---|
attr(optional) for ( init-statement(optional, since C++20) range_declaration : range_expression ) loop_statement
|
可以通过指针、引用、转发引用、const引用、值来进行access。
跳转语句
break
- 负责终止最近的
while
、do while
、for
或switch
。 - 直接终止而不进行下一次迭代。
continue
- 负责终止最近的
while
、do while
、for
- 只能出现在上述循环体内。对不在循环体内的
switch
无效 - 仅终止当前的迭代,然后进行下一次。
goto
- 从goto语句无条件跳转到同一函数内另一条语句。使用时需要用带标签语句labeled statement给要跳转的语句命名。
- 尽量不在程序中使用。
try语句块和异常处理
C++中的异常处理包括:
- throw expression - 异常检测部分使用throw表达式表示遇到了无法处理的问题。即:throw raised the expression(throw引发了异常)
- try block - 异常处理部分使用try语句块处理异常。该语句块以关键字try开始并以一个或多个catch子句 - catch clause 结束。try语句块中代码抛出的异常通常会被某个子句处理,因此被称为异常处理代码(exception handler)
- exception class 异常类,用于在throw表达式和相关的catch子句之间传递异常的具体信息。
throw 表达式
异常检测部分使用throw表达式引发一个异常。throw表达式包含关键字throw和紧随其后的一个表达式,表达式类型就是抛出的异常类型。
throw表达式通常紧跟一个分号。
if(!condition_satisfied)
throw runtime_error("Error text here");
try 语句块
try语句块的通用语法形式:
try {
program-statements
} catch (exception-declaration) {
handler-statements
} catch (exception-declaration) {
handler-statements
} //.......
https://en.cppreference.com/w/cpp/language/try_catch
string data;
while (cin>>data){
try{
} catch (runtime_error err) { //实例化error类型的err
cout<<err.what()
<<"\nTry again? (Y/N)"<<endl;
char c;
cin>>c;
switch(c) {
default:
case 'N': continue;
case 'Y': ;
}
}
}
#include <exception>
#include <iostream>
int throwError(int arg){
switch (arg){
case 1:
throw std::runtime_error("Runtime error occurred!");
break;
case 2:
throw std::exception("Exception occurred!");
break;
}
return 0;
}
int main(){
int a = 1;
try{
throwError(a);
} catch (std::runtime_error err){
std::cout<<err.what()<<std::endl;
} catch (std::exception exc){
std::cout<<exc.what()<<std::endl;
}
}
C++标准异常
定义在<exception>中,需要以std::
访问的异常类如下:
https://en.cppreference.com/w/cpp/error/exception
异常类 | 描述 |
---|---|
exception | 常见问题 |
runtime_error | 运行时才能检测出的错误 |
range_error | 运行时错误:结果超范围 |
overflow_error | 运行时错误:计算上溢 |
underflow_error | 运行时错误:计算下溢 |
logic_error | 程序逻辑错误 |
domain_error | 逻辑错误:参数对应结果值不存在 |
invalid_error | 逻辑错误:无效参数 |
length_error | 逻辑错误:视图创建超出类型最大长度的对象 |
out_of_range | 逻辑错误:使用一个超出有效范围的值 |
上述类的对象只能默认初始化而不可以通过任何形式赋初值。它们只有一个成员函数 what()
,返回一个 const char*
提供关于该异常的文本信息。