日常开发过程中,有想要替换编译器提供的operator new/delete,替换一定是有合理理由的。其中的一个可能就是:用来检测运行上的错误。
new和delete导致的错误:
1)如果对new的内存进行delete时失败,会导致内存泄漏memory leaks。
2)如果对new的内存进行多次delete,则会导致无法想象的结果。
人为编程导致的错误:
1)"overruns":写入点在分配区块尾端之后
2)"underruns":写入点在分配区块起点之前
对于人为编程导致的错误,可以使用signature operator new/delete来解决此问题。下面引出签名式signature的概念。
我们自定义一个operator new,用于要求分配siz大小的内存时,我们可以额外多分配内存,额外多分配的内存空间(位于客户所得区块之前或之后)放置特定的byte patterns(即签名,signature)。我们自定义operator delete时可以通过检查上述签名值是否已经改变,如果改变了则表示在分配区内存的某个生命点发生了overruns或者underruns错误。
这个时候operator delete可以记录这个错误的事实以及使用者对象的指针用于日志记录以便调试。
下面是在没有考虑满足当前platform的字节对齐方式的operator new的自定义伪代码,实现的是signature operator new:
//signature operator new(签名式operator new/delete)伪码
static const int signature = 0xABCDDCBA; //signature value
typedef unsigned char Byte;
void* operator new (std::size_t size) throw (std::bad_alloc)
{
using namespace std;
//比起用户size,额外多申请2个int的空间内存size + 2*sizeof(int)
size_t realSize = size + 2 * sizeof ( int );
//调用malloc分配内存
void* buf = malloc(realSize);
if (!buf)
{
throw bad_alloc();
}
//开头签名的内存地址
void* pOverSig = buf;
//用户的内存地址
void* pUser = static_cast<Byte*>(buf) + sizeof(int);
//尾端签名的内存地址
void* pUnderSig = static_cast<Byte*>(buf) + realSize - sizeof(int);
//在size大小的用户内存区的前面的1个int空间和后面的1个int空间都进行signature
*(<static_cast<int*>(pOverSig)) = signature;
*(reinterpret_cast<int*>(pUnderSig)) = signature;
//返回指针,指向开头的signature之后的紧接着的内存地址
return pUser;
}
我们自定义operator new/delete时上面的signature operator new/delete的用来检测运行上的错误的功能,可能是一个理由。
定制自己的operator new和operator delete时可以参考《Effective C++ 改善程序与设计的55个具体做法(第三版)》给出的理由和规则。
(完)