创建 RAII 类 时应该如何处理错误?

How should errors be handled when creating RAII classes?

如果例如构造函数的资源分配部分RAII 套接字包装器失败,我是否只是抛出异常并完成它?或者我应该如何std::fstream seems to do it,在构建对象后需要检查is_open()

前者似乎更符合“资源分配即初始化”的名称,但为什么标准库基本上让你在使用对象之前检查错误代码?

我指的是 basic_fstream 参考页上的示例(转述,添加注释):

int main() {
  std::string filename = "test.bin";
  std::fstream s(filename);

  if (!s.is_open()) { // should my socket class require the user to do this?
    std::cout << "failed to open " << filename << '\n';
  } else {
    // ... use it
  }
}

推荐的做法是对构造期间发生的任何 失败抛出异常。引用 C++ FAQ:

Q. How can I handle a constructor that fails?

A. Throw an exception.

Constructors don’t have a return type, so it’s not possible to use return codes. The best way to signal constructor failure is therefore to throw an exception. If you don’t have the option of using exceptions, the “least bad” work-around is to put the object into a “zombie” state by setting an internal status bit so the object acts sort of like it’s dead even though it is technically still alive.

如果构造涉及 RAII,那么构造函数有额外的责任在抛出之前清理任何已经分配的资源。

Q. How should I handle resources if my constructors may throw exceptions?

A. Every data member inside your object should clean up its own mess.

If a constructor throws an exception, the object’s destructor is not run. If your object has already done something that needs to be undone (such as allocating some memory, opening a file, or locking a semaphore), this “stuff that needs to be undone” must be remembered by a data member inside the object.

至于为什么std::fstream构造函数不抛出异常而默认使用回退选项(将对象置于“僵尸”状态),这是一个历史和便利的结合,在 Why are C++ STL iostreams not “exception friendly”?.

的答案中有更好的解释