《Effective C++》:条款51:编写new和delete时需固守常规
2015-03-20 20:54
531 查看
条款 50已经说明为什么要写自己的operator new和operator delete,本条款解释在编写时遵循什么守则。
从operator new开始。operator new必须返回正确的值,内存不足时必须调用new-handling函数,要有对付零内存需求的准备,避免不慎掩盖正常形式的new–这比较偏近class接口的要求而非实现要求。正常形式的new描述与条款 52。
operator new如果申请内存成功,就返回指向那块内存的指针,失败则遵循条款 49描述,抛出bad_alloc异常。它实际上不知一次尝试内存分配,在每次失败后都调用new-handling函数。这里假设new-handling函数能做某些动作将一些内存释放出来。只有指向new-hangling函数的指针为,才会抛出异常。C++规定,即使客户要求0 byte,operator new也要返回一个合法指针。下面是个non-member operator new的伪码(pseudocode):
如果在多线程环境下,还需要某种锁机制,以便处理new-handling函数背后的global数据结构。
上面包含一个死循环,退出此死循环唯一办法就是内存分配成功(假设new-handling不为null),所以new-handling函数做的事是:让更多内存可用、安装另一个new-handler、卸载new-handler、抛出bad_alloc异常(或其派生类),或承认失败直接return。
上面的operator new成员函数可能会被derived classes继承。注意分配内存大小size,它是函数接收的实参。**条款**50提到,定制内存分配器往往是为了特定的class对象,以此来优化,不是为了该class的derived classes。
如果你打算控制class专属版的arrays内存分配,那么需要实现operator new[]。编写operator new[]时,唯一要做的一件事就是分配一块未加工的内存(raw memory),因为你无法对array之内迄今尚未存在的元素对象做任何事。甚至我们无法知道这个array含有多少个元素对象。可能你不知道每个对象多大,因为base class的operator new[]有可能经由继承被调用,将内存分配给derived class对象的array使用。
所以不能再Base::operator new[]中假设每个元素对象大小是sizeof(Base),这样就是说你不能假设array元素个数是(bytes申请数/sizeof(Base))。此外,传递给operator new[]的size_t参数,其值有可能比将辈填对象的内存大一些,因为条款 16提过,动态分配的arrays可能包含额外空间用来存放元素个数。
operator delete情况就简单很多,但是要记住,C++保证删除指针永远安全。下面是non-member operator delete的伪码(pseudocode):
这个函数的member版本也很简单,只需多加一个检查删除数量。
如果即将删除的对象派生自某个base class而后者没有virtual析构函数,那么C++传给operator delete的size_t数值可能不正确。
总结
operator new应该内涵死循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler。它也应该有能力处理0bytes申请。class专属版本的还应该处理“比正确大小更大的(错误)申请”。
operator delete应该在收到指针时不做任何事。class专属版本则还应该处理“比正确大小更大的(错误)申请”。
从operator new开始。operator new必须返回正确的值,内存不足时必须调用new-handling函数,要有对付零内存需求的准备,避免不慎掩盖正常形式的new–这比较偏近class接口的要求而非实现要求。正常形式的new描述与条款 52。
operator new如果申请内存成功,就返回指向那块内存的指针,失败则遵循条款 49描述,抛出bad_alloc异常。它实际上不知一次尝试内存分配,在每次失败后都调用new-handling函数。这里假设new-handling函数能做某些动作将一些内存释放出来。只有指向new-hangling函数的指针为,才会抛出异常。C++规定,即使客户要求0 byte,operator new也要返回一个合法指针。下面是个non-member operator new的伪码(pseudocode):
void* operator new(std::size_t size) throw(std::bad_alloc) { using namespace std; if(size==0){//处理0-byte申请 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(); } }
如果在多线程环境下,还需要某种锁机制,以便处理new-handling函数背后的global数据结构。
上面包含一个死循环,退出此死循环唯一办法就是内存分配成功(假设new-handling不为null),所以new-handling函数做的事是:让更多内存可用、安装另一个new-handler、卸载new-handler、抛出bad_alloc异常(或其派生类),或承认失败直接return。
上面的operator new成员函数可能会被derived classes继承。注意分配内存大小size,它是函数接收的实参。**条款**50提到,定制内存分配器往往是为了特定的class对象,以此来优化,不是为了该class的derived classes。
class Base{ public: static void* operator new(std::size_t size) throw(std::bad_alloc) …… }; class Derived:public Base {……};//假设为重新定义operator new Derived* p=new Derived;//这里调用了Base::operator new 如果是class专属的operator new,应该改为这样: void* Base::operator new(std::size_t size) throw(std::bad_alloc) { if(size!=sizeof(Base)) return ::operator new(size);//使用标准的operator new …… }
如果你打算控制class专属版的arrays内存分配,那么需要实现operator new[]。编写operator new[]时,唯一要做的一件事就是分配一块未加工的内存(raw memory),因为你无法对array之内迄今尚未存在的元素对象做任何事。甚至我们无法知道这个array含有多少个元素对象。可能你不知道每个对象多大,因为base class的operator new[]有可能经由继承被调用,将内存分配给derived class对象的array使用。
所以不能再Base::operator new[]中假设每个元素对象大小是sizeof(Base),这样就是说你不能假设array元素个数是(bytes申请数/sizeof(Base))。此外,传递给operator new[]的size_t参数,其值有可能比将辈填对象的内存大一些,因为条款 16提过,动态分配的arrays可能包含额外空间用来存放元素个数。
operator delete情况就简单很多,但是要记住,C++保证删除指针永远安全。下面是non-member operator delete的伪码(pseudocode):
void operator delete(void* rawMemory) throw() { if(rawMemory==0) return; 归还rawMemory所指内存; }
这个函数的member版本也很简单,只需多加一个检查删除数量。
void Base::operator delete(void rawMemory, std::size_t size) throw() { if(rawMemory==0) return; if(size!=sizeof(Base)){ ::operator delete(rawMemory); return ; } 归还rawMemory所指内存; return ; }
如果即将删除的对象派生自某个base class而后者没有virtual析构函数,那么C++传给operator delete的size_t数值可能不正确。
总结
operator new应该内涵死循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler。它也应该有能力处理0bytes申请。class专属版本的还应该处理“比正确大小更大的(错误)申请”。
operator delete应该在收到指针时不做任何事。class专属版本则还应该处理“比正确大小更大的(错误)申请”。
相关文章推荐
- effective C++ 条款 51:编写new和delete时需固守常规
- Effective C++ 条款 51:编写new和delete时需固守常规
- Effective C++ -----条款51:编写new 和delete 时需固守常规
- Effective C++ 条款51 编写new和delete时需固守常规
- Effective C++ 3e----new & delete(八)条款51:编写new和delete时需固守常规
- effective C++ 条款 51:编写new和delete时需固守常规
- 条款51:编写new和delete时需固守常规
- 条款51:编写new和delete时需固守常规(Adhere to convention when writing new and delete.)
- 条款51:编写new和delete时需固守常规
- C++之编写new和delete时需要固守常规(51)---《Effective C++》
- 条款51:编写new和delete时需固守常规
- 条款51:编写new和delete需固守常规
- 条款51:编写new以及delete的时候需要固守常规
- 《Effective C++》读书笔记之item51:编写new和delete时需固守常规
- 【51】编写new和delete时需固守常规
- 编写new和delete时需固守常规
- Effective C++学习笔记:条款3:尽量用new和delete而不用malloc和free
- Effective C++:条款16:成对使用new和delete时要采取相同形式
- 《Effective C++》学习笔记条款16 成对使用new和delete时要采取相同形式
- effective C++ 条款 50:了解new和delete的合理替换时机