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
相关产品推荐
相关产品推荐

