07 August 2023

C++ 核心指南目录

“Destructors, deallocation, swap, and exception type copy/move construction must never fail”

理由

如果析构、释放、交换、异常类型的复制/移动构造函数也会失败,我们就无法知道如何编写可靠的程序了。所谓的失败,就是这些地方抛出异常,或者无法完成指定的动作。

错误例子

class Connection {
    // ...
public:
    ~Connection()   // Don't: very bad destructor
    {
        if (cannot_disconnect()) throw I_give_up{information};
        // ...
    }
};

注意

我们做了很多尝试,怎么在违反此规则的前提下,写出可靠的代码。比如,网络连接无法关闭。就我们所知,还没人发现比较通用的方法。偶尔的,我们会遇到一些很特殊的例子,比如说,我们设置某个状态,等到以后再去清理。比如说,我们可以把一个暂时无法关闭的 socket 放到 bad socket 列表,等到后续系统开始清理的时候,再统一删除。我们看到的几乎所有这些例子都是很特殊、容易出错。

注意

标准库函数假定构造函数、释放函数(比如 delete 操作)、 swap 交换函数不会抛出异常。如果它们会抛出异常,那么标准库中的不变式就很难维护了。

注意

释放函数,包括 delete 操作符,必须为 noexcept

swap 函数也必须是 noexcept

大部分析构函数,默认是 noexcept 的

还有,请将 move 操作设置为 noexcept

如果编写一个类型作为异常类型使用,请确保它的复制构造函数不是 noexcept 。一般来说,我们无法机械式地强调这个,因为我们不知道这个类型是否会作为一个异常类型。

避免抛出一个复制构造函数不是 noexcept 的类型。我们不能机械式的强调这个,因为 std::string(...) 有时候也会作为异常抛出。

强化

  • 找出构造函数、内存释放操作、 swap 会抛出异常的情况。
  • 找出这类操作没有标记为 noexcept 的情况。