条款49:了解new-handler
Typedef vod (*new_handler)(); New_handler set_new_handler(new_handler p) throw(); 传入:operator new无法分配足够内存时被调用的函数。 返回:被调用前正在执行的那个函数。 例如: Void outOfMem() { Cerr << “unable to get enough memory” << endl; Std::abort(); } Int main() { Std::set_new_handler(outOfMem); } |
new-handler:
1:让更多内存可被使用。
2删除new-handler。当将null传入给set_new_handler时。一旦没有安装任何new-handler,operator new会在内存分配不成功时抛出异常。
Class Widget{ Public: Static std::new_handler set_new_handler(std::new_handler p) throw(); Static void * operator new(std::size_t size) throw(std::bad_alloc); Private: Static std::new_handler currentHandler; }; Static 成员必须在class定义式外被定义(除非是const而且是整数型) Std::new_handler Widget::currentHandler = 0;
Static std::new_handler set_new_handler(std::new_handler p) throw() { Std::new_handler oldHandler = currentHandler; currentHandler = p; return oldHandler; } |
operator new:
1:调用标准的set_new_handler,告知Widget的错误处理函数。将widget的new-handler安装为globalnew-handler.
2:调用global operatornew,执行内存分配。如果失败,global operator new调用Widget的new-handler。如果new最终无法分配足够的内存,抛出bad_alloc异常。在此情况下,widget的operator new必须回复原本的new-handler,然后传播异常。
3:如果new能够分配足够内存,new会返回一个指针。Widget的析构函数会管理new-handler,它会自动将new-handler恢复。
Widget* pw1 = new widget; If (pw1 == 0) // 失败,抛出bad_alloc异常 Widget* pw2 = new (std::nothrow) Widget; // 如果分配失败,返回0 If (pw2 == 0) // 这个测试可能成功,也不绝对 |
nothrow new只适用于内存分配,后继的构造函数则还是有可能抛出异常。
条款50:new、delete的合理替换时机
1:用来检测运用上的错误。如果将new出的内存,delete时失败,则内存泄露。
如果new的内存,调用了多次delete,导致不确定行为。
2:做自己想做的事。个性化定制。
3:收集统计数据。内存分配的不同运用形态(先进先出、后进先出、随机分配、随机归还?)任何时候使用的最大内存分配量是多少?
Static const int signature = 0xDEADBEEF; Typedef unsigned char Byte; Void * operator new(size_t size) throw (bad::alloc) { Using namespace std; Size_t realSize = size + 2 * sizeof(int); Void *pMem = malloc(realSize); If (!pMem) throw bad_alloc(); // 将signature写入内存的最前端和最后端 *(static_cast<int*>(pMem)) = signature; *(reinterpret_cast<int*>(static_cast<Byte*>(pMem) + realSize-sizeof(int))) = signature; // 返回位于第一个signature后面的内存位置 Return static_cast<Byte*>(pMem) + sizeof(int); } |
对齐:
开源库中的内存管理器。例如boost库中的pool对于分配大量小型对象很有帮助。
需要考虑:可移植性、对齐、线程安全。
条款51:编写new、delete时需要固守常规
Void *operator new(size_t size) throw(bad_alloc) { Using namespace std; If (size == 0) Size = 1; While (true) { 分配size个内存块 If (分配成功) Return 指针 // 分配失败,找到new-handler函数,调用 New-handler globalHandler = set_new_handler(0); Set_new_handler(globalHandler); If (globalHander) (*globalHandler)(); Else Throw std::bad_alloc; } } |
Class Base { Static void * operator new(size_t size) throw(bad_alloc); Static void operator delete(void * rawMemory, size_t size) throw(); }; void Base::operator delete(void * rawMemory, size_t size) throw() { If (rawMemory == 0) Return; If (size != sizeof(Base) { ::operator delete(rawMemory); // 调用全局delete函数 Return; } // 归还内存 Return; } |
如果析构函数不是virtual的,operator delete可能无法正常工作
条款52:placement new、placement delete
Widget *pw = new Widget; // operator new、Widget的default构造函数 |
假设new成功而构造函数抛出异常,必须恢复,否则造成内存泄露
系统会自动调用new对应的delete
Class Widget { Static void * operator new(size_t size, ostream& logStream) throw(bad_alloc); Static void operator delete(void * rawMemory, size_t size) throw(); Static void * operator delete(void *rawMemory, ostream& logStream) throw(bad_alloc);
}; void Base::operator delete(void * rawMemory, size_t size) throw() { If (rawMemory == 0) Return; If (size != sizeof(Base) { ::operator delete(rawMemory); // 调用全局delete函数 Return; } // 归还内存 Return; } Void *operator new(size_t size, void *pMemory) throw(); Widget *pw = new(std::cerr) Widget;// 会在调用构造函数抛出异常时造成内存泄露 // 运行期编译器希望找到参数个数和类型都与operator new相同的某个delete,如果找到,就直接调用,否则,什么也不做(不会调用全局delete) Delete pw;// 调用正常的delete。placement delete只有伴随placement new调用而触发的构造函数出现异常时才被调用。对一个指针调用delete不会调用placement delete |
在#include<new>中。用途之一:负责在vector的未使用空间上创建对象。一个特定位置上的new。
提供两个版本的delete:operator delete 和placement delete
Class Base { Static void * operator new(size_t size, ostream& logStream) throw(bad_alloc); Static void operator delete(void * rawMemory, size_t size) throw(); }; Base*pb = new Base; // 错误!正常形式的operator new被掩盖。 Base *pb = new (std::cerr) Base; // 调用Base的placement new |
// 缺省的global下的new: void * operator new(size_t size) throw(bad_alloc); void * operator new(size_t size, void *) throw(bad_alloc); void * operator new(size_t size, const std::nothrow t&) throw(bad_alloc); |
如果在class内声明任何new,则会掩盖上述global的new。
对于每一个可用的new,确定对应有一个delete。
Class Base { Static void * operator new(size_t size, ostream& logStream) throw(bad_alloc) { Return ::operator new(size); } Static void operator delete(void * rawMemory, size_t size) throw() { Return ::operator delete(rawMemory); } }; |
9 杂项讨论
条款53:不要轻易忽视编译器的警告
条款54:熟悉标准程序库
TR1:
1:智能指针
tr1::shared_ptr tr1::weak_ptr
2:tr1::function
3:tr1::bind
4:Hash tables:
实现set、multiset、map、multimap
5:正则表达式
6:Tuples:变量组
tr1::array
tr1::mem_fn
tr1::reference_wrapper
随机数生成
书序特殊函数
type traits:
tr1::result_of: