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

析构函数抛异常时多次调用delete的C++标准合规性及跨平台问题

析构函数抛异常后重复delete的未定义行为问题

现象描述

  • Visual Studio编译运行:当析构函数抛出异常时,operator delete不会被调用;析构函数不抛异常时则会调用,代码可正常执行。
  • GCC编译运行:类包含std::string成员时触发段错误;移除std::string成员时,程序因free(): double free detected in tcache 2终止,说明此时即使析构函数抛异常,operator delete仍会被调用。

测试代码

#include <string>
#include <iostream>

class MyClass
{
public:
    void *operator new(size_t size)
    {
        void *p = std::malloc(size);
        if (!p) throw std::bad_alloc();
        return p;
    }

    void operator delete(void *p)
    {
        std::free(p);
    }

    ~MyClass() noexcept (false)
    {
        if (s == "123") throw int{};
    }

    std::string s{"123"};
};

int main()
{
    MyClass* myobject = new MyClass{};

    try
    {
        delete myobject;
    }
    catch (...) {}

    std::cout << "<" << myobject->s << ">";
    myobject->s = "abc";

    delete myobject;

    return 0;
}

问题解答

1. 是否属于C++标准定义的未定义行为?

是,这段代码的行为完全属于未定义行为。

C++标准并未明确规定:当delete表达式触发的析构函数抛出异常时,对应的operator delete是否会被调用。不同编译器可以根据自身实现选择调用或不调用,这直接导致了你看到的跨平台行为差异。

2. 首次delete抛异常后能否再次调用delete?

绝对不能。

  • 如果首次delete已经调用了operator delete(如GCC无std::string成员的场景),第二次delete就是对已释放内存的重复释放,必然触发内存错误。
  • 如果首次delete未调用operator delete(如VS的场景),此时对象已经经历了部分析构(比如std::string成员可能已经被销毁),对象处于无效状态。后续访问myobject->s以及再次delete的操作,本质是访问已销毁对象的内存,同样属于未定义行为——VS环境下没崩溃只是巧合,并非代码合法。

你的代码依赖了标准未规定的编译器实现细节,同时存在操作无效对象/重复释放内存的问题,因此不可能跨平台正常运行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:52:40