C++ 是否可以完全禁用默认的C++;新接线员?

C++ 是否可以完全禁用默认的C++;新接线员?,c++,new-operator,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 ); } // i

因为我们的应用程序有硬性能和内存限制,我们的编码标准禁止使用默认堆,即,无
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();

尽管我们用代码保持了良好的纪律性,但是一些标准的C++组件(容器,<代码> STD::函数< /C> >偷偷地调用默认的代码>新的< /COD>堆,这非常糟糕。p> 以某种方式完全禁用默认的

new
,这样任何隐式导致默认分配的代码行都会立即抛出编译器错误。这会让我们立刻注意到这些事情

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

但最好在编译时得到有关此的警告。可能吗


我们的应用程序是为固定平台(x64和Visual Studio)构建的;可移植性与此无关。

您可以实现默认的
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();
}
在我的测试中,使用
g++
,如果程序中没有对裸
new
的引用,则不会出现链接错误。这是因为
g++
不会为未使用的
inline
函数发出代码

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

*
g++
有一个类似的编译器选项:
-include detect\u bare\u new.h

假定你打算将自己的分配器传递给标准C++库中的C++模板和类。如果没有,则调用默认分配器(将调用

new
)的标准头中的内联代码也将触发链接错误。如果你希望允许标准C++库使用默认的< <代码> < < /代码>,那么一个简单的方法(以较长的编译时间为代价)是添加所有你想包括在代码顶部的所有标准C++头。 您声明解决方案的可移植性对您并不重要。但是为了完整性,我应该强调Ben Voigt正确指出的问题:C++标准不能保证未使用的代码>内联< /代码>函数不生成代码的行为。因此,即使未使用该函数,也可能会出现链接错误。但是,如果代码除了在stubbed
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
如果您的系统是为未使用的
内联
函数生成代码的系统,您可能仍然有一个解决方法。如果链接器将报告对未定义函数的所有错误引用,则解决方法将起作用。在这种情况下,如果观察到的唯一链接错误是由于
new
运算符本身的定义引起的,则不会出现对裸
new
的意外调用。在验证代码只有一个错误后,您可以更改链接行,以包含一个对象或库,该对象或库具有适当的
bare\u new\u错误地调用()
,这将引发运行时异常。

如果您自己的“new”操作符未命名为“new”,但名称不同(例如“myNew”),则可以使用“定义”以垃圾取代“新”:

#define new *+-/&
预编译器现在将替换“新”:

垃圾:

x = *+-/& mytype;
与链接时间的消息相比,它的优点是,在编译C++文件时,这将立即生成编译器消息,而不是在链接时结束。也可以看到“新”所在的行。

缺点是,你必须“包含”包含在项目中所有C++文件中的这个“*定义”的文件。

BR> 如果您使用的是GCC,则有一个pragma:

#ifdef __GNUC__

/* poision memory functions */
#   pragma GCC poison malloc new

#endif

您只需声明运算符new
=delete
,类似于禁用某些构造函数

struct Foo {
    void* operator new(std::size_t) = delete;
    void* operator new[](std::size_t) = delete;
};

当您尝试在这个类中使用
new
时,这将给您带来编译器错误。请参见

您是否尝试查看链接器输出?我认为您可以从链接器映射输出中判断是否正在调用分配器例程。它没有指向有问题的行,但至少在构建时它能够中断。这里有一个有趣的指针:它是一个选项,可以根据自定义的new/delete实现默认(所有形式的)new/delete吗?我似乎记得,可以使用.def文件导出new/delete,从而生成(整个?)使用导出的版本进行处理。@stijn这主意不错,但不允许在编译时进行检测-time@stijn这正是
void*操作符new(size_t){{u_debugbreak();returnnull;}
是关于:它替换了流程使用的默认新操作符,只需将其链接进去。因此,OP知道该机制,并且认为它不能用于解决他的问题。根据标准,您确定这是合法的吗?。由于
操作符new
不是一个模板,因此无论是否使用它都会导致错误是否被调用。@BenVoigt函数有一个原型,因此编译器不会抱怨。我想你可以用这种方式将参数排序到链接器
x = *+-/& mytype;
#ifdef __GNUC__

/* poision memory functions */
#   pragma GCC poison malloc new

#endif
struct Foo {
    void* operator new(std::size_t) = delete;
    void* operator new[](std::size_t) = delete;
};