构造函数中new失败会抛std::bad_alloc,此时已构造的成员按逆序析构,对象内存自动释放,但未完成构造的部分不析构;应使用RAII(如unique_ptr)而非手动清理。
直接抛 std::bad_alloc,这是 C++ 标准行为。但关键不在“抛不抛”,而在“抛了之后对象有没有被部分构造、析构

this 指针无效,连虚表都可能没初始化完。
常见错误是手动 delete 已分配的资源再 throw,比如:
MyClass() {
ptr1 = new int[100];
ptr2 = new char[200]; // 这里失败
delete[] ptr1; // 手动清理?错!容易漏、易重复、不异常安全
throw std::runtime_error("oops");
}
正确做法是把资源交给 RAII 对象管理:
std::unique_ptr 替代裸指针,分配成功自动接管,失败时已构造的 unique_ptr 会正常析构(释放其持有的资源)不会泄漏。C++ 标准保证:如果构造函数抛出异常,编译器会自动调用已完成构造的成员对象的析构函数,并释放该对象占用的内存(即调用 operator delete)。注意:这只是栈上或 new 分配的原始内存,不是你手动 malloc 或 VirtualAlloc 的。
但有例外:
operator new,且在其中做了额外资源分配(比如注册句柄),而没配套实现异常安全的 operator delete,那这部分资源就真漏了placement new 时,编译器不负责调用 operator delete,必须手动匹配调用 operator delete(哪怕构造失败)std::vector 等容器在扩容时内部调用 new 失败,也会抛异常,但它自己已确保无泄漏——前提是你的元素类型构造函数也是异常安全的C++ 构造函数没有返回值,无法返回 nullptr 或 std::nullopt。试图用“构造函数设标志位 + 提供 valid() 成员”是反模式:对象逻辑上已存在,但处于无效状态,后续任何成员函数都得加运行时检查,极易遗漏。
更安全的替代方案:
std::optional(C++17)或 std::unique_ptr,把构造逻辑和成败判断收口到一处std::expected(C++23)或第三方 outcome 库,显式表达可能失败如果派生类构造函数开始执行,但基类构造函数抛异常,则派生类的成员**一个都不会构造**,也不会调用派生类的析构函数。整个对象生命周期止步于基类。
这意味着:
virtual)init() 成员函数,并明确文档说明“必须在构造后立即调用”,但这本质上放弃了异常安全保证最棘手的其实是多继承:各基类按声明顺序构造,任一失败都会导致后续基类和所有成员跳过构造。调试时看到“某个成员的构造函数根本没进”,先查它前面的基类或成员是否抛了异常。