C++20中new表达式构造函数抛异常是否会导致内存泄漏
核心问题解答
该程序不存在内存泄漏
你给出的C++程序完全没有内存泄漏问题。
常规new表达式的执行逻辑是固定的:
- 先为目标对象申请对应大小的原始内存,拿到内存首地址
- 在这块内存上按固定顺序完成对象构造:先构造所有基类子对象,再按类内声明顺序构造所有非静态成员子对象,最后执行类的构造函数体
- 如果构造流程的任意环节抛出异常,会自动执行两步清理:
- 从最后一个完成构造的子对象开始,逆序调用所有已构造子对象的析构函数——这就是你看到输出
~S1的原因 - 无条件释放第一步申请到的整块原始内存
- 从最后一个完成构造的子对象开始,逆序调用所有已构造子对象的析构函数——这就是你看到输出
清理完成后,异常会直接向上传递,new表达式不会将内存地址赋值给你代码里的s2变量,你根本无法拿到这块已经被回收的内存地址,自然不存在内存泄漏。
S2自身的析构函数没有被调用,是完全符合语言规则的:S2的整体构造从未完成,对象的生命周期从未正式开启,不满足析构函数的合法触发时机。
该行为是C++标准强制保障的,不存在未定义/未指定行为
从C语言诞生到C20,这套清理流程一直是标准明确要求的强制规则,所有执行步骤都是确定性的:
- 标准明确规定,
new表达式的内存分配、构造、异常清理是严格配对的:只要构造函数抛出异常,运行时必须匹配之前为对象申请内存时调用的分配函数,完整释放整块内存。哪怕你为S2自定义了类级别的operator new和operator delete,编译器也会在构造异常时自动调用对应的operator delete完成释放,不需要用户手写额外的清理代码。 - 全流程的执行顺序没有留给编译器自由裁量的空间:子对象的构造顺序、异常中断时的逆序析构顺序、析构完成后的内存释放时机,都有明确的规则要求,不存在实现定义的行为。
- 只有在使用定位new(placement new)手动调用构造函数的场景下,语言才不会自动帮你配对内存释放逻辑——这种场景下如果构造抛出异常,需要开发者手动完成已构造子对象的析构和内存释放。你代码里使用的是常规
new表达式,不涉及这类特殊场景。
内容的提问来源于stack exchange,提问作者Andrew Tomazos
相关产品推荐
相关产品推荐

