定制new和delete
主角是operator new 和operator delete,配角是new-handler,当operator new无法满足客户的内存需求时所调用的函数。
多线程环境下的内存管理,比单线程复杂的多。由于heap是一个可悲改动的全局性资源,因此多线程系统充斥这发狂访问这一类资源的race conditions出现机会。
了解new-handler的行为
namespace std{
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}
一个设计良好的new-handler函数必须做以下事情:
1.让更多内存可被使用。实现此策略的做法是,程序一开始执行就分配一大块内存,而后当new-handler第一次调用时,将它们释放还给程序使用。
2.安装另一个new-handler。就是说目前这个new-handler无法取得更多的内存,如果另外一个new-handler有这个能力,应该调用另外一个new-handler来完成这个任务。实现方法:令new-handler修改会影响new-handler行为的static数据,namespace数据和global数据。
3.卸除new-handler。将null指针传给set_new_handler。一旦没有安装任何new-handler,operator new会在内存分配不成功时抛出异常
4.抛出bad_alloc的异常。这样的异常不会被operator new 捕捉,因此会被传播到内存索求处。
5.不返回。通常调用abort或exit。
了解new和delete的合理替换时机
替换编译器提供的operator new或operator delete的理由:
1. 用来检测运用上的错误。
2. 为了强化效能。
3. 为了收集使用上的统计数据。
4. 为了增加分配和归还的速度。
5. 为了降低缺省内存管理器带来的空间额外开销
6. 为了弥补缺省分配器中的非最佳齐位
7. 为了将相关对象成簇集中。
8. 为了获得非传统行为
编写new和delete时需固守常规
实现一致性operator new必须返回正确的值,内存不足时必得调用new-handling函数,必须有对付零内存需求的准备,还需避免不慎遮盖正常形式的new–虽然这比较偏近class的接口要求而非实现要求。
void* operator new(std::size_t size) throw(std::bad_alloc){
using namespace std;
if(size == 0){
size = 1;
}
while(true){
//尝试分配size bytes
if(分配成功)
return (一个指针,指向分配得来的内存)
//分配失败,找出目前的new-handling函数
new_handler globalHandler = set_new_handler(0);
set_new_handler(globalHandler);
if(globalHandler)
(*globalHandler)();
else
throw std::bad_alloc();
}
}
继承operator new成员函数的derived class会导致怪异的问题。
class Base{
public:
void* operator new(std::size_t size) throw(std::bad_alloc);
};
class Derived : public Base{ //...}
Derived *p = new Derived;
void* Base::operator new(std::size_t size) throw(std::bad_alloc){
if(size != sizeof(Base)) //针对继承class出现问题
return ::operator new(size);
//...
}
如果决定写个operator new[],唯一需要做的就是分配一块未加工的内存,因为无法对array内迄今尚未存在的元素对象做任何事情。
operator delete,需要保证删除null指针永远安全。
void operator delete(void *rawMemory) throw(){
if(rawMemory == 0) return ;
//.... 归还rawMemory所指的内存
}
class Base{
public:
static void* operator new(std::size_t size) throw(std::bad_alloc);
static void operator delete(void* rawMemory, std::size_t size) throw();
//...
};
void Base::operator delete(void* rawMemory, std::size_t size) throw(){
if(rawMemory == 0 ) return;
if(size != sizeof(Base)){ //如果大小错误,调用标准版operator delete处理
::operator delete(rawMemory);
return;
}
//归还rawMemory所指内存
return;
}
写了placement new也要写placement delete
如果一个带额外参数的operator new没有带相同额外参数的对应版operator delete,那么当new的内存分配动作需要取消并恢复旧观时就没有任何operator delete会被调用。
class Widget{
public:
static void* operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc);
static void operator delete(void* memory) throw(); //必须的
static void operator delete(void* memory, std::ostream& logStream) throw(); //必须的
};
Widget* pw = new(std::cerr) Widget; //抛出异常时,不会导致内存泄漏,可以恢复如初
如果你在class内声明任何operator news,它会遮蔽上述的标准形式,实现所有自定义的new和标准的new都可以用的简单做法就是,建立一个base class,内含所有正常形式的new和delete
class StandardNewDeleteForms{
public:
static void* operator new(std::size_t size) throw(std::bad_alloc){
return ::operator new(size);
}
static void operator delete(void* memory) throw(){
::operator delete(memory);
}
static void* operator new(std::size_t size, void* ptr) throw(){
return ::operator new(size,ptr);
}
static void operator delete(void* memory, void* ptr) throw(){
return ::operator delete(memory,ptr);
}
static void* operator new(std::size_t size, const std::nothrow_t& nt) throw(){
return ::operator new(size,nt);
}
static void operator delete(void* memory,const std::nothrow_t&) throw(){
::operator delete(memory);
}
};
然后可以通过using声明式来获取标准形式
class Widget: public StandardNewDeleteForms{
public:
using StandardNewDeleteForms::operator new;
using StandardNewDeleteForms::operator delete;
static void* operator new(std::size_t size, std::ostream& logStream) throw(std::bad_alloc);
static void operator delete(void* memory, std::ostream& logStream) throw();
//...
};