是否可以完全禁用默认的C++ new运算符?

Cra*_*rks 40 c++ new-operator

因为我们的应用程序具有硬性能和内存限制,我们的编码标准禁止使用默认堆 - 即,不malloc,没有默认值new.每个内存分配必须选择一些特定的分配器; 就像是

// declared globally
void* operator new( size_t size, CustomAllocHeap* heap, const char* perpetrator_name )
{
  return heap->Allocate( size, perpetrator_name );
} 
// imagine a bunch of CustomAllocHeap's declared globally or statically, thus

Vector* v = new( gPhysicsHeap, __FUNCTION__ ) Vector( 1.0f, 2.0f, 3.0f, 4.0f );
// or in a class
Thingy* p = new( this->LocalArenaHeap, __FUNCTION__ ) Thingy();
Run Code Online (Sandbox Code Playgroud)

尽管我们在代码中保持了良好的规范,但是一些标准的C++组件(容器std::function)却隐蔽地调用默认new堆,这非常糟糕.

new以某种方式完全禁用默认值会很好,因此隐式导致默认分配的任何代码行都会立即引发编译器错误.这会让我们马上注意到这些事情.

我们显然可以new抛出运行时错误,

void* operator new ( size_t ) { __debugbreak(); return NULL; }  
Run Code Online (Sandbox Code Playgroud)

但是在编译时得到关于它的警告要好得多.那可能吗?

我们的应用程序是为固定平台(使用Visual Studio的x64)而构建的; 可移植性是无关紧要的.

jxh*_*jxh 18

您可以实现默认值new来调用未实现的函数.然后,在链接时,您将收到裸new呼叫用户的错误:

#include <stdexcept>
inline void * operator new (std::size_t) throw(std::bad_alloc) {
    extern void *bare_new_erroneously_called();
    return bare_new_erroneously_called();
}
Run Code Online (Sandbox Code Playgroud)

当我在IDEONE上测试它时,我收到了这个错误:

/home/geXgjE/ccrEKfzG.o: In function `main':
prog.cpp:(.text.startup+0xa): undefined reference to `bare_new_erroneously_called()'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

在我的测试中,g++如果程序中没有对裸的引用,则没有链接错误new.这是因为g++不会为未使用的inline函数发出代码.

我的系统上没有安装Visual Studio,因此以下信息仅基于我找到的一些文档.为了让内联运new算符随处可见,您应该将其定义放在头文件中,然后/FI detect_bare_new.h在编译器中使用该选项.*根据这个答案,Visual Studio不会为未使用的inline函数生成代码(例如g++).但是,您应该检查是否存在需要为该行为启用的优化级别.

*g++有一个类似的编译器选项:-include detect_bare_new.h.

这假设您打算将自己的分配器传递给标准C++库中的C++模板和类.如果不这样做,那么调用默认分配器(将调用new)的标准头中的内联代码也将触发链接错误.如果您希望允许标准C++库使用默认值new,那么使其工作的简单方法(以更长的编译时间为代价)就是添加您想要包含在detect_bare_new.h文件顶部的所有标准C++头文件.

您声明解决方案的可移植性对您来说并不重要.但为了完整起见,我应该强调Ben Voigt正确指出的问题:C++标准不保证不为未使用的inline函数生成代码的行为.因此,即使未使用该功能,也可能会出现链接错误.但是,如果代码除了在存根new实现中之外没有对未实现函数的其他引用,则错误将在new定义本身内.例如,g++可能会生成如下错误:

/home/QixX3R/cczri4AW.o: In function `operator new(unsigned int)':
prog.cpp:(.text+0x1): undefined reference to `bare_new_erroneously_called()'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

如果您的系统是为未使用的inline功能生成代码的系统,您可能仍然有一个解决方法.如果链接器将报告对未定义函数的所有错误引用,则解决方法将起作用.在这种情况下,如果观察到的唯一链接错误是由于new运算符本身的定义,则没有意外调用裸new.在验证代码只有该单个错误之后,您可以更改链接行以包含具有适当定义的对象或库bare_new_erroneously_called()将引发运行时异常.

  • 由于`operator new`不是模板,因此无论是否调用都会导致错误.http://ideone.com/3KwAya (4认同)
  • @jxh:在一个特定的工具链和一组编译器和链接器选项上,也许,但这不是标准行为. (2认同)