概述
当抛出一个异常后,程序暂停当前函数的执行过程并立即开始寻找与异常匹配的catch语句,这叫栈展开。如果找不到,最后会调用标准库函数terminate。
自动销毁
栈展开的时候,在该块里面的局部变量将进行销毁,如果是类,会调用析构。所以说才要尽量使用智能指针,就不用在finally里面写delete了。
异常对象
其实可以throw任何对象,这个对象会被保存在编译器管理的空间中,确保无论是哪个地方catch了,都能访问到这个对象。也可以throw出任何数组和函数,会被编译器转化为指针。
但是,一般不要直接甩出来一个对象,否则这个对象在throw之前会被析构一次,传到catch之后又会析构一次,很蠢。
重新抛出
在catch块里面,单单写这么一句就可以重新抛出
throw;
捕获所有异常
类似于Java在最后写
catch(Exception e){
}
c++版本的写法是
catch(...){}
同样一般是放在最后
捕获构造函数异常
直接用try catch包起来
class A{
public:
A() try : arg(arg){
} catch(){
}
}
noexcept
说明这个函数不会抛错误,对编译器来说大有好处,能够执行一些特殊的优化。
但是就算是写了这个,也可以真的抛出异常,只是程序会直接terminate。
运算符
noexcept(func()); // true代表这个函数不会抛出异常
异常层级
自定义的exception一般要继承于runtime_error或者logic_error
定义explicit的构造函数传入一个string,并透传到上一级的构造函数里面去。
Cautious
在throw那个语句临时创建的对象,只会执行一次构造函数,不会马上析构,等到catch执行完之后才会进行析构。假若是先新建了一个对象,然后再将其抛出,那么在抛出之前就会进行一次析构,然后抛出去之后再在catch里面析构一次,会造成不可预估的后果。
class CustomException : runtime_error{
public:
explicit CustomException(const string& what) : runtime_error(what){
cout << "CustomException()" << endl;
}
const char* getMsg(){
return this->what();
}
virtual ~CustomException(){
cout << "~CustomException()" << endl;
}
};
void func()
{
throw CustomException("msg content");
}
int _tmain(int argc, _TCHAR* argv[])
{
try{
func();
}
catch (CustomException &e){
cout << "error: " << e.getMsg() << endl;
}
system("pause");
return 0;
}