You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++构造函数抛出异常时,调用成员清理函数是否安全?含指针场景

关于构造函数function-try-block的几个问题解答

针对你提出的三个问题,逐一解答如下:

1. Controller构造函数抛出异常时,client_成员变量是否不复存在?

不存在这种情况。在C++中,类的成员变量会在构造函数体执行之前完成初始化——哪怕你用了function-try-block,进入try块的时候,所有成员已经完成了默认初始化(这里client_作为std::shared_ptr,默认初始化为空指针)。当你在try块里执行client_ = std::make_shared<Client>()后,client_已经指向了一个有效的Client对象,之后Start()抛出异常时,client_这个成员变量仍然是存在的,直到构造函数的catch块执行完毕,才会自动销毁所有已初始化的成员变量。

2. 此时调用清理client_的deinit()是否安全?

安全。因为catch块执行时client_还存在,deinit()里的if (client_)判断是有效的,shared_ptr::reset()本身也是安全操作——哪怕client_是空指针,reset也不会引发问题。不过这里有个小细节:就算你不手动调用deinit(),当构造函数执行失败、成员变量被销毁时,std::shared_ptr的析构函数会自动释放持有的Client对象,所以这个deinit()其实是多余的,但调用它完全没问题。

3. 若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:
  Client* client_ = nullptr; // 裸指针默认初始化为nullptr

 public:
  Controller() try {
    client_ = new Client(); // 手动分配内存
    client_->Start();
  } catch (...) {
    deinit(); // 捕获异常时手动清理
    throw; // 注意:构造函数的function-try-block必须重新抛出异常,否则会自动调用std::terminate()
  }

  ~Controller() {
    deinit();
  }

 private:
  void deinit() {
    if (client_) {
      delete client_; // 手动释放内存
      client_ = nullptr; // 置空避免野指针
    }
  }
};

需要注意两个关键点:

  • 必须在catch块里重新抛出异常:构造函数的function-try-block如果捕获了异常但不重新抛出,C++标准会强制调用std::terminate(),因为构造函数失败意味着对象无法创建,必须让异常继续传播。
  • 裸指针要默认初始化为nullptr:避免deinit()里对野指针执行delete操作。

内容的提问来源于stack exchange,提问作者duong_dajgja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:02:59