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

C++20销毁式delete能否安全延迟对象销毁?是否符合标准?

关于C++20 destroying delete的合规性与风险问题

假设库导出Foo类,客户端持有Foo*指针,使用完对象后需调用delete指针。希望在库内部让这些被调用delete的对象,在满足某些内部条件(如内部引用计数归零、异步操作完成)前保持存活,为此编写了基于C++20 destroying delete操作符的核心代码:

struct Foo {
  void real_delete_(Foo* ptr)
  {
    ptr->~Foo();
    ::operator delete(ptr);
  }

  void operator delete(Foo* ptr, std::destroying_delete_t)
  {
    if (/*some condition to see if we can delete now*/)
      real_delete_(ptr);
    // otherwise real_delete_() will be called when condition gets met
  }
};

同时编写了最小测试程序验证逻辑:

#include <cstdio>
#include <memory>

struct Foo
{
  int refCount = 0;

  Foo()
  {
    printf("Foo created at %p\n", this);
  }

  ~Foo()
  {
    printf("Foo %p destroyed\n", this);
  }

  void incRef()
  {
    ++refCount;
  }

  void decRef()
  {
    --refCount;
    if (refCount == 0)
      realDelete();
  }

  void realDelete()
  {
    this->~Foo();
    ::operator delete(this);
  }

  void operator delete(Foo* ptr, std::destroying_delete_t)
  {
    if (ptr->refCount == 0)
      ptr->realDelete();
  }
};

int main()
{
  auto foo = new Foo();
  foo->incRef();
  delete foo;
  foo->decRef();
}

该测试程序在GCC-14中可编译运行,Valgrind检测无错误,但在Clang-11中触发编译器内部错误。针对以下问题解答:


1. 上述代码是否符合C++标准,应当可编译运行吗?

符合C++20标准,应当可以编译运行。

C++20引入的destroying delete特性,核心就是允许自定义operator delete重载来接管对象销毁的完整流程:当调用delete ptr时,如果存在匹配的operator delete(T*, std::destroying_delete_t)重载,编译器不会自动调用对象的析构函数,而是将销毁对象和释放内存的责任完全交给这个重载函数。你的代码逻辑完全符合这一设计意图——在重载中判断条件,满足则立即销毁,不满足则延迟到条件达成时再手动执行析构和内存释放。

Clang-11触发内部错误属于编译器自身的bug,因为Clang-11对C20特性的支持尚不完善(该版本发布于C20正式定稿初期),升级到Clang-13及以上版本即可解决这个编译问题。


2. 此类让对象在delete调用后继续存活的代码是否定义良好?存在哪些风险?

代码的核心逻辑是标准允许的,但存在以下关键风险:

  • 客户端语义误解风险:按照C++的常规认知,调用delete后指针即成为悬垂指针,客户端不应该再访问。但你的逻辑中对象可能仍存活,若客户端不知情后续访问指针,虽然在技术上对象仍有效,但这违背了通用编程约定,极易引发客户端写出语义错误的代码。
  • 线程安全隐患:如果多个线程同时操作引用计数、调用delete或触发realDelete,未对refCount等共享状态做原子性保护的话,会出现竞态条件,导致重复销毁、内存泄漏或对象提前释放。
  • 手动销毁的正确性风险:realDelete中手动调用析构函数和全局operator delete,必须严格保证这些操作只执行一次,否则会触发重复析构或重复释放内存的未定义行为。
  • 编译器兼容性问题:老版本编译器(如Clang-11、GCC-10及以下)对C++20 destroying delete的支持存在bug或不完整,若需要兼容这些环境,该写法会受限。

内容的提问来源于stack exchange,提问作者Always Confused

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 06:29:54