根据 C++ 自身的机制,有些函数是不允许抛出异常的。这些函数通常具备关键的功能,如资源管理或底层的基础功能,需要确保在任何情况下都能稳定运行,不会因抛出异常而中断。
遵循相关原则是保证异常安全的重要基础,下面我们就来盘点一下不应抛出异常的函数:
1. 析构函数
析构函数负责清理对象占用的资源,如果析构函数在异常处理过程中被调用(例如在栈展开时),它抛出的新异常会与当前处理的异常冲突。
C++ 标准推荐析构函数不应抛出异常,从 C++11 开始,析构函数隐式地被声明为noexcept(true)
,除非另有指定。
2. 移动构造函数和移动赋值运算符
在 C++11 及以后的标准中,移动构造函数和移动赋值运算符不应抛出异常,这是因为移动操作通常比复制操作更快,且不会抛出异常,编译器可以利用这一点来优化代码,例如通过消除不必要的临时对象。
3. swap 函数
swap 函数用于交换两个对象的状态。如果 swap 函数抛出异常,那么可能会导致对象状态的不一致。
4. 资源回收函数
如 operator delete 等,这些函数负责内存释放,如果这些函数抛出异常,可能会导致内存泄露或程序崩溃。
5. 重载或特化不抛异常的标准函数
标准库中的一些函数不抛出异,如 hash::operator() 等,对这些函数的重载或特化也不应抛出异常,否则会使标准库无法正常工作,也会造成出乎意料的错误。
6. 特定上下文中的函数
在某些特定上下文中,如异常处理过程中(catch 块)或异常类自身的成员函数中,除非是有意重新抛出异常,否则也不应抛出异常,以保持程序的异常安全状态。
总之,在设计函数时,应仔细考虑是否应该抛出异常。更进一步地,可参见下列文章对每一点的详细说明:
- 全局对象的初始化过程不可抛出异常
- 析构函数不可抛出异常
- 内存回收函数不可抛出异常
- 对象交换过程不可抛出异常
- 移动构造函数和移动赋值运算符不可抛出异常
- 异常类的拷贝构造函数不可抛出异常
- 异常类的构造函数和异常信息相关的函数不应抛出异常
- 与标准库相关的 hash 过程不应抛出异常
更多编程注意事项请参见:《360 安全规则集合》