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

带std::nothrow_t的自定义operator new未加noexcept时仍调用构造函数的疑问

带std::nothrow_t的自定义operator new未加noexcept时仍调用构造函数的疑问

最近我碰到一个挺反直觉的问题:给结构体TestNew实现了带std::nothrow_t参数的operator new重载,原本以为只要用了这个标签,当内存分配失败返回nullptr时,编译器就不会调用构造函数。结果发现完全不是这么回事——必须给这个operator new显式加上noexcept声明,才能达到预期效果。

先看一下测试代码:

#include <cstdlib>
#include <iostream>
#include <new>

struct TestNew {
    // 加上noexcept:不会调用构造函数
    void* operator new(std::size_t, std::nothrow_t) noexcept {
    // 不加noexcept:会意外调用构造函数
    // void* operator new(std::size_t, std::nothrow_t) {
        std::cout << "operator new of TestNew called!" << std::endl;
        return nullptr;
    }

    TestNew() {
        std::cout << "A TestNew created!" << std::endl;
    }
};

int main() {
    auto p = new (std::nothrow) TestNew;
    if (!p) {
        std::cout << "nullptr!" << std::endl;
        std::abort();
    }
    return 0;
}

两种情况的输出对比

  • 不加noexcept时,GCC 14.1和Clang 18.1.0的输出完全一致:

    operator new of TestNew called!
    A TestNew created!
    nullptr!
    

    明明operator new返回了空指针,构造函数却还是被调用了,这完全不符合预期。

  • 加上noexcept后,输出就正常了:

    operator new of TestNew called!
    nullptr!
    

    构造函数没有被执行,符合我们对nothrow版new的认知。

编译器的警告提示

其实编译器早就给出了提示,只是一开始没注意:

  • GCC的警告:warning: 'operator new' must not return NULL unless it is declared 'throw()' (or '-fcheck-new' is in effect)
  • Clang的警告:warning: 'operator new' should not return a null pointer unless it is declared 'throw()' or 'noexcept' [-Wnew-returns-null]

关键结论(更新)

后来搞明白了,要实现符合预期的非抛出式operator new(不管是自定义的还是标准库提供的),必须满足两个条件:

  • 第一,必须带std::nothrow_t标签:这个标签的作用仅仅是重载决议——让new (std::nothrow)语法能匹配到对应的重载,它本身不能告诉编译器“这个函数不会抛出异常”。
  • 第二,必须显式声明noexcept:这才是核心,告诉编译器“这个内存分配函数不会抛出异常,如果它返回空指针,就跳过构造函数的调用”。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 06:49:33