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 的情况。