是否有可能完全禁用默认的C ++新操作符?
由于我们的应用程序有困难的性能和内存限制,我们的编码标准禁止使用默认的堆 – 即没有malloc
,没有默认的new
。 每个内存分配必须select几个特定的分配器之一; 就像是
// 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();
尽pipe我们已经在代码中保持了良好的纪律,但是一些标准的C ++组件(containers, std::function
)隐藏了对默认new
堆的调用,这非常糟糕。
以某种方式完全禁用默认的new
代码会很好,这样隐含地导致默认分配的任何代码行都会立即引发编译器错误。 这会让我们马上注意到这些事情。
我们显然可以做出new
的运行时错误, 即
void* operator new ( size_t ) { __debugbreak(); return NULL; }
但是在编译时得到这个警告会好得多。 那可能吗?
我们的应用程序是build立在一个固定的平台(与Visual Studio的x64); 便携性是无关紧要的。
你可以实现默认的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(); }
当我在IDEONE上testing它时,我得到这个错误:
/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
在我的testing中,使用g++
,如果在程序中没有引用new
引用,就没有链接错误。 这是因为g++
不会为未使用的inline
函数发出代码。
我的系统上没有安装Visual Studio,因此以下信息仅基于我find的一些文档。 为了让内联的new
操作符在任何地方都能看到,你应该把它的定义放在一个头文件中,然后在编译器中使用/FI detect_bare_new.h
选项。 *根据这个答案 ,Visual Studio将不会为未使用的inline
函数(如g++
)生成代码。 但是,您应该检查是否存在需要为该行为启用的优化级别。
*
g++
有一个类似的编译器选项:-include detect_bare_new.h
。
这假定您打算将自己的分配器传递给标准C ++库中的C ++模板和类。 如果你不这样做,那么在调用默认分配器(将调用new
)的标准头文件中插入代码也会触发链接错误。 如果你希望允许标准的C ++库使用默认的new
,那么使它工作的一个简单的方法是以更长的编译时间为代价,把你想要包含的所有标准C ++头添加到detect_bare_new.h
文件。
您声明解决scheme的可移植性对您并不重要。 但为了完整起见,我应该强调Ben Voigt正确指出的问题:C ++标准不保证不为未使用的inline
函数生成代码的行为。 所以,即使不使用该function,也可能会出现链接错误。 但是,如果代码除了在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
如果您的系统为未使用的inline
函数生成代码,那么您可能仍然有一个解决方法。 如果链接器将报告未定义函数的所有错误引用,解决方法将工作。 在这种情况下,如果观察到的唯一链接错误是由于new
操作符本身的定义所致,则不会有意外的new
调用。 在validation代码只有这个单一的错误之后,你可以改变链接来包含一个对象或库,该对象或库有一个适当定义的bare_new_erroneously_called()
,它会抛出一个运行时exception。
如果你自己的“新”操作符不是被命名为“新”,而是不同的(例如“myNew”),你可以用一个“#define”代替垃圾的“新”
#define new *+-/&
预编译器现在将replace“新”:
x = new mytype;
由垃圾:
x = *+-/& mytype;
与链接时的消息相比,它的优点是它将在编译C ++文件时立即生成编译器消息,而不是在链接时最终生成。 您还可以看到“新”所在的行。
缺点是你必须在项目的所有C ++文件中“#include”包含这个“#define”的文件。
毒药吧!
如果你使用的是GCC,这里有一个附注:
#ifdef __GNUC__ /* poision memory functions */ # pragma GCC poison malloc new #endif