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++标准不能保证未使用的代码>内联< /代码>函数不生成代码的行为。因此,即使未使用该函数,也可能会出现链接错误。但是,如果代码除了在stubbednew
实现中没有其他对未实现函数的引用,那么错误将出现在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;
};