带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
相关产品推荐
相关产品推荐

