构造函数可抛异常,但对象自身析构函数不会调用;已构造的成员/基类按逆序析构,裸指针资源若未raii封装将泄漏;析构函数不可抛异常,否则触发std::terminate。

不会。构造函数抛出异常时,本对象的析构函数 ~A() 根本不会被调用——因为该对象的生命周期从未开始。
构造失败时,已成功构造的成员/基类会怎样
编译器会自动“回滚”已成功完成初始化的部分:
- 初始化列表中已构造成功的成员对象,会按声明逆序调用其析构函数
- 多继承中已执行完构造函数的基类,会按继承顺序逆序调用其析构函数
- 但派生类自己的
~A()体永远不会进入 - 如果在构造函数体(
A::A()函数体内)分配了裸指针资源(如new char[100]),而没用 RAII 封装,这部分内存会泄漏
为什么不能在析构函数里 throw 异常
当栈展开(stack-unwinding)正在进行时(比如正在处理另一个异常),若析构函数又抛出新异常,std::terminate() 会被立即调用,程序强制终止。
典型风险场景:
-
~FileHandler()中fclose()失败并throw—— 若此时已有未捕获异常,进程崩 - 多个对象析构过程中,第二个析构函数抛异常,直接中断整个 unwind 过程
- 标准明确要求:析构函数应是
noexcept(C++11 起默认隐式为noexcept(true))
如何安全处理构造期间的资源分配
核心原则:把资源绑定到对象生命周期上,让编译器接管清理责任。
- 用
std::unique_ptr替代裸new:初始化列表中_data(std::make_unique<char>(100))</char>,失败时自动释放已构造的智能指针 - 成员变量优先使用 RAII 类型(
std::vector、std::string、std::fstream),而非原始指针或 FILE* - 避免在构造函数体中做可能失败的重操作;把复杂逻辑拆到
init()成员函数,并由用户显式调用 - 若必须报告构造失败,抛异常是合法手段,但前提是所有资源都已由 RAII 托管
最容易被忽略的一点:构造函数里 new 之后、异常发生前的那段“窗口期”,没有任何机制能帮你回收那块内存——它既不属于任何智能指针,也不属于已构造完成的对象。这个空档,只能靠设计规避,不能靠事后补救。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











