C++学习 十八、异常
前言
本篇学习C++中的异常处理。
异常机制
C++中,如果出现了除0、数组越界、分配内存不足时,程序会直接报错崩溃。为了追踪BUG,让程序出错时能够正常退出,可以使用异常处理机制。
try, catch, throw
try, catch
语句块与throw
表达式是C++异常处理的方式。
try
语句块用于执行可能发生异常的程序块,throw
语句块用于处理异常。
throw
表达式用于抛出一个异常。
int main(){
int a=3, b=0;
try{
if(b==0)
throw "bad division";
a /= b;
}
catch(const char* s){
std::cout << s << std::endl;
}
return a;
}
// bad division
try
块中判断除数b
是否为0,如果为0,通过throw
抛出异常表达式"bad division"
,catch
块接收到异常表达式并与类型const char* s
匹配,执行该catch
块。
一个try
块可以跟着多个catch
块,但至少要有一个catch
块。异常发生时,执行异常类型匹配的catch
块,后面的catch
块将不被执行。
int main(){
int a=3, b=0;
try{
if(b==0)
throw "bad division";
a /= b;
}
catch(const char* s){
std::cout << s << std::endl;
}
catch(int b){
std::cout << b << std::endl;
}
return a;
}
// bad division
如果没有匹配的catch
块,这个异常就没有得到正常处理,程序直接中止。为了处理任何异常表达式,可以使用catch(...){}
块,它只能放在所有catch
块的最后:
int main(){
int a=3, b=0;
try{
if(b==0)
throw "bad division";
a /= b;
}
catch(int b){
std::cout << b << std::endl;
}
catch(...){
std::cout << "...";
}
return a;
}
// ...
如果异常在函数中抛出后未被处理,将会抛给上一层函数,直到找到包含try
块的函数:
void funcc(int n){
std::cout << "funcc" << std::endl;
if(n==0)
throw n; // throw back to func
}
void func(int n){
std::cout << "func" << std::endl;
funcc(n); // throw back to main
}
int main(){
int a=3, b=0;
try{
func(b);
a /= b;
}
catch(char s){
std::cout << s << std::endl;
}
catch(...){
std::cout << "...";
}
return a;
}
// ...
// 执行顺序:
// main_try->fun->funcc->funcc_throw->fun->main_catch
如果try
块没有出现异常,则执行完try
块后,直接执行最后一个catch
块后的程序。
总结一下异常处理机制:
- 执行
try
块语句; throw
抛出异常表达式;catch
捕获异常表达式类型,执行对应catch
块语句;catch
块语句执行完后,程序跳转到最后一个catch
块后继续运行。
如果抛出异常是一个派生类对象,catch
类型是其基类对象,则也可以处理该异常。
异常规范
C++98中新增了异常规范,但C++11废弃了。
异常规范是指出某个函数是否使用某种类型的异常:
void func() throw(int, double);
void funcc() throw();
表明func()
可能抛出int, double
型的异常,funcc()
不会抛出异常。
C++11把这项功能废弃了,不过新增了关键字noexcept
指出函数不会引发异常。如果函数抛出异常,则程序直接中止。
void func() noexcept;
C++异常类
C++ exception头文件中定义了异常类exception
,有一个虚方法what()
返回一个报错用的字符串。
C++ stdexcept头文件定义了另外几个异常类,首先是两个expection类的派生类logic_error
和runtime_error
。
logic_error
类的派生类:
domain_error
定义域异常invalid_argument
参数异常length_error
空间长度异常out_of_bounds
索引超出异常
runtime_error
类的派生类:
range_error
类型范围异常overflow_error
上溢异常underflow_error
下溢异常
此外,还有bad_alloc
异常,通常是new
动态内存失败时抛出。