总结:在c++编程中,不建议使用异常。因为c++标准没有定义异常的实现方式,并且异常也不可以跨线程。
构造函数异常
构造函数可以抛出异常,此时对象还没有完全构造完成,对象的生命周期提前结束,不会调用对象的析构函数。
禁止抛出异常
在c++中,可以在函数的后面添加noexcept
关键字来禁止函数抛出异常,如果该函数产生了异常,那么系统将中止该进程的执行。
如果将一个显示抛出异常的函数声明为noexcept
,那么在编译的时候会有以下告警信息:
throw will always call terminate()
并且,即使你对该代码进行 try ... catch
操作,还是会导致 crash
。测试代码和结果如下:
#include <stdexcept>
#include <stdlib.h>
#include <iostream>
struct tt
{
~tt()
{
std::cout << "~tt" << std::endl;
}
void func() noexcept
{
throw std::out_of_range("out_of_range error!!!");
}
};
void temp_func()
{
try
{
tt t;
t.func();
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
}
int main(int argc,char **argv)
{
temp_func();
return 0;
}
// ./a.out
//terminate called after throwing an instance of 'std::out_of_range'
// what(): out_of_range error!!!
//Aborted (core dumped)
析构函数异常
标准规定析构函数不能抛出异常,也不应该抛出异常。
如果对象在运行期间出现了异常,c++异常处理模型有责任清除哪些由于出现异常所导致的已经失效了的对象(对象超出了其作用域),并释放对象原来所分配的资源,这里就要调用这些对象的析构函数来完成资源释放的任务,所以从这个角度来看,析构函数实际上是异常处理的一部分。
如果析构函数中存在抛出异常的可能,那么需要进行显示的捕获,并进行处理,否则会导致terminate
。当然,应该尽可能避免在析构函数中抛出异常,将异常代码从析构中移除。
异常的捕获
使用try...catch
可以捕获异常。示例如下:
void temp_func()
{
try
{
// ...
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
}
}
跨线程异常
c++的异常不能跨线程。因此,如果一个线程的异常没有被捕获到,那么会导致整个系统异常终止。因此,在多线程环境中,尽量少的使用异常。否则应该在最外层添加try...catch
进行保护。