c++ 如果构造函数抛出,调用一个对成员变量进行一些清理的类成员函数安全吗?

ovfsdjhp  于 2023-04-01  发布在  其他
关注(0)|答案(1)|浏览(163)

我有下面的代码片段,这是为了演示构造函数的function-try-block的用例。因为Controller的构造函数没有抛出它成功创建的对象,即当抛出异常时,client_成员变量也应该不再存在,是这样吗?如果是这样,那么调用deinit()来清理client_是安全的吗?如果不安全,假设client_是一个原始指针,那么我如何捕获异常并为它做清理工作?

class Client {
 public:
  Client() { std::cout << "Client()" << std::endl; }
  ~Client() { std::cout << "~Client()" << std::endl; }
  void Start() { throw "Start() failed with exception"; }
};

class Controller {
 private:
  std::shared_ptr<Client> client_;

 public:
  Controller() try {
    client_ = std::make_shared<Client>();
    client_->Start();
  } catch (...) {
    deinit();
  }

  ~Controller() {
    deinit();
  }

 private:
  void deinit() {
    if (client_) {
      client_.reset();
    }
  }
};
soat7uwm

soat7uwm1#

当抛出异常时,client_ member变量也不应该存在,对吗?
不,这是不正确的。异常在新类示例的client_成员被构造之后被抛出。
一个类的构造函数只有在新类示例的所有成员都被构造之后才被调用;构造函数的工作是在所有的类成员都构造好之后,完成任何构造类本身的任务。
一旦你进入构造函数,你就可以保证新类示例的所有成员都被完全构造,你可以把它带到银行。
调用deinit()安全吗
当然,它是“安全的”,因为它是定义的行为。但是,在这种情况下,它是完全不必要的。如果在构造函数中抛出异常,则新类示例的所有构造成员都将以相反的构造顺序被自动销毁。client_将被正常销毁,就像类完全构造然后在稍后的某个时候被销毁一样。
如果client_是一个原始指针,那么我如何捕获异常并对其进行清理?
与上面的代码差不多,但它不是一个原始指针,而是一个shared_ptr,它处理这个问题。

相关问题