第5章 语句
5.3 switch语句
switch内部的变量定义
switch的执行流程由有可能会跨过某些case标签。若程序跳转到某个特定的case,则switch结构中该case标签之前的部分会被忽略掉。这会引出一个问题:若被忽略的代码中含有变量的定义该怎么办?
答案是:若在某处一个带有初值的变量位于作用域中,在另一处该变量位于作用域之内,则从前一处跳转到最后一处的行为是非法行为。
switch (case)
{//程序的执行流程可能绕开下面的初始化语句,所有该switch语句不合法
case true:
string file_name;//错误:控制流绕过了一个隐式初始化的变量
int ival=0;//错误:控制流绕过了一个显式初始化的变量
int jval;//正确:因为jval没有初始化
case false:
//正确:jval虽然在作用域内,但是它没有被初始化
jval=next_num();
break;
default:
break;
}
C++语言规定,不允许跨过变量的初始化语句直接跳转到该变量作用域内的另一位置。
若需要为一个case分支定义并初始化一个变量,我们应该把变量定义在块内,从而确保后面的所有case标签都在变量的作用域之外。
case true:
{//正确:声明语句在语句块内部
string file_name=get_file_name();
}
break;
case false:
if(file_name.empty()) //错误:file_name不在作用域之内
5.4 迭代语句
while和for语句在执行循环体之前检查条前,do_while语句先执行循环体,然后再检查条件。
5.5 跳转语句
5.5.1 break语句
break语句负责中止离它最近的while、do while、for和switch语句,并从这些语句之之后的第一条语句开始继续执行。
break语句只能出现在迭代语句或者switch语句内部(包括嵌套在此类循环里的语句或块的内部)。break语句的作用范围仅限于最近的循环或者switch。
5.5.2 continue语句
continue语句中止最近的循环中的当前迭代并立即开始下一次迭代。continue语句只能出现在for、while和do while循环的内部,或者嵌套在此类循环里的语句或块的内部。和break语句类似的是,出现在嵌套循环中的continue语句也仅作用离它最近的循环。和break语句不同的是,只有当switch语句嵌套在迭代语句内部时,才能在switch里使用continue。
continue语句中断当前的迭代,但是仍然继续执行循环。对于while或者do while语句来说,继续判断条件的值;对于传统for循环来说,继续执行for语句头的expression;而对于范围for循环来说,则是用序列值中的下一个元素初始化循环控制变量。
5.5.3 goto语句
goto语句的作用是从goto语句无条件跳转到同一函数内的另一条语句。
语法形式为:
goto label;//label是用于标识一条语句的标示符。
label: //带标签语句
int i=0;
5.6 try语句块和异常处理
异常是指存在于运行时的反常行为,这些行为超出了函数正常功能的范围,典型的异常行为如失去数据库连接以及遇到意外输入等。
异常处理机制为程序中异常检测和异常处理这两部分的协作提供支持。在C++语言中,异常处理包括:
方式 | |
---|---|
throw表达式 | 异常检测部分使用throw表达式来表示它遇到了无法处理的问题。throw引发了异常。 |
try语句块 | 异常处理部分使用try语句块处理异常。try语句块以关键字try开始,并以一个或多个catch子句结束。try语句中代码抛出的异常会被某个catch子句处理,catch子句也被称为异常处理代码。 |
一套异常类 | 用于在throw表达式和相关的catch子句之间传递异常的具体消息。 |
5.6.1 throw表达式
throw runtime_error("error");
抛出一个异常,该异常是类型runtimer_error的对象。抛出异常将中止当前的函数,并把控制权转移给能处理该异常的代码。
上面代码段中,类型runtime_error是标准库异常类型的一种,定义在stdexcept头文件中。使用runtime_error必须初始化对象,给它提供一个string对象或者一个C风格的字符串,字符串中有一些关于异常的辅助信息。
5.6.2 try语句块
try语句块的通用语法形式:
try{
program-statements
}catch(exception_declaration){
handler-statements
}catch(exception_declaration){
handler-statements
}
编写处理代码
在第一章中,我们为了避免对代表不同书籍的Sales_item对象相加,进行了if判断。现在我们假设执行Sales_item对象加法的代码是与用户交互的代码分离开来的,其中与用户交互的代码负责处理发生的异常:
while(cin>>item1>>item2){
try{
//执行添加两个Sales_item对象的代码
//如果添加失败,代码抛出一个runtime_error异常
}catch(runtime_error err){
//提醒用户两个ISBN必须一致,询问是否重新输入
cout<<err.what()<<"重新输入 y or n"<<endl;
char c;
cin>>c;
if(!cin||c=='n'){
break;
}
}
}
err.what()返回值是C风格字符串(即const char*),runtime_error的what成员函数返回的是初始化一个具体对象err时所用的string对象的副本。
5.6.3 标准异常
C++标准库定义了一组类,同于报告标准库函数遇到的问题。这些异常类也可以在用户编写的程序中使用,它们定义在4个头文件中:exception头文件定义了最通用的异常类exception,它只报告异常的发生,不提供任何额外消息;stdexcept头文件定义了几种常用的异常类;new头文件定义了bad_alloc异常类型;type_info头文件定义了bad_cast异常类型。
stdexcept头文件定义的异常类 | |
---|---|
exception | 最常见的问题 |
runtime_error | 只有运行时才能检测出问题 |
range_error | 运行时错误:生成的结果超出有意义的值域范围 |
overflow_error | 计算上溢 |
underflow_error | 计算下溢 |
logic_error | 程序逻辑错误 |
domain_error | 逻辑错误:参数对应的结果值不存在 |
out_of_range | 使用一个超过有效范围的值 |
invaild_argument | 无效参数 |
我们只能用默认初始化的方式初始化exception、bad_alloc和bad_cast类型,不允许为这些对象提供初始值。其他异常类型相反:需要使用string对象或者C风格字符串初始化这些类型的对象。
异常类型只定义了一个名为what的成员函数,该函数没有任何参数,返回值时一个指向C风格字符串的const cahr*,该字符串的目的是提供关于异常的一些文本消息。