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

C++中operator new与placement new使用后的内存释放合法性疑问

堆上分配对象时额外预留内存的几种写法合法性分析

1. 标准实现:合法且符合规范

你给出的第一种写法是该场景下的标准合规实现,在C++14及所有后续标准中均有效:

#include <new>

void f(std::size_t n) {
    void* buf = ::operator new(sizeof(SomeClass) + n);
    SomeClass* obj = new(buf) SomeClass;

    // 使用obj进行操作

    obj->~SomeClass();
    ::operator delete(buf);
}
  • 流程清晰:先通过::operator new分配足够容纳SomeClass与额外n字节的内存,再用placement new在该内存块上构造对象;
  • 销毁逻辑正确:手动调用析构函数完成对象资源清理,再通过原分配的buf指针释放整块内存;
  • 兼容性:C11到C20及之后版本均支持,无版本差异。

2. 用obj转void*调用::operator delete:合法(前提未重载placement new)

第二种写法的合法性依赖默认placement new的行为:

#include <new>

void f(std::size_t n) {
    void* buf = ::operator new(sizeof(SomeClass) + n);
    SomeClass* obj = new(buf) SomeClass;

    // 使用obj进行操作

    obj->~SomeClass();
    ::operator delete(static_cast<void*>(obj));
}

在你假设未重载new的前提下,C++标准规定:默认的placement new(即void* operator new(std::size_t, void*))会直接返回传入的内存指针。同时::operator new返回的内存地址满足SomeClass的对齐要求,因此obj与buf地址完全一致。此时将obj转为void*传给::operator delete,等价于传入原buf指针,完全合法。

若自定义了SomeClass的placement new,则需确保该重载返回的指针与::operator new分配的起始地址一致,否则会导致未定义行为。

3. 直接delete obj:完全不合法

第三种写法存在严重的标准违规问题:

#include <new>

void f(std::size_t n) {
    void* buf = ::operator new(sizeof(SomeClass) + n);
    SomeClass* obj = new(buf) SomeClass;

    // 使用obj进行操作

    delete obj;
}

C++标准明确规定:delete表达式仅能用于普通new表达式(即new SomeClass形式)创建的对象。而placement new构造的对象不属于普通new表达式的产物:

  • delete obj会先调用SomeClass的析构函数(这一步本身没问题),但随后会调用::operator delete(obj);
  • 普通delete默认认为分配的内存大小是sizeof(SomeClass),但实际分配的是sizeof(SomeClass)+n。即便部分编译器可能“碰巧”运行正常,这仍属于未定义行为,在任何C++版本中均不合法。

4. 重载带参数的operator new后用delete obj:合法(需注意细节)

你补充的重载写法合法,且在C++14及后续标准中均有效:

#include <new>

enum class ExtraSpace : std::size_t {}; 

void* operator new(std::size_t size, ExtraSpace n) {
    return ::operator new(size + static_cast<std::size_t>(n));
}

void f(std::size_t n) {
    SomeClass* obj = new(ExtraSpace(n)) SomeClass;

    // 使用obj进行操作

    delete obj;
}

合法性依据:

  1. C++标准允许重载带参数的operator new,对应的new(ExtraSpace(n)) SomeClass表达式会调用该重载,分配sizeof(SomeClass)+n的内存后构造对象;
  2. 对象构造成功后,delete obj会先调用SomeClass的析构函数,随后调用普通的::operator delete(void*);
  3. obj就是重载operator new返回的起始指针,符合::operator delete的参数要求,因此完全合法。

注意事项:

  • 若需处理构造异常,建议同时重载对应的placement delete:void operator delete(void*, ExtraSpace),这样当SomeClass构造函数抛出异常时,编译器会自动调用该重载释放内存;
  • 该写法在C11及之后版本均有效,C03中支持重载带参数的operator new,但enum class是C11引入的,如需兼容C03需改为普通枚举或其他参数类型。

内容的提问来源于stack exchange,提问作者Noam Elul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 01:15:00