条款51:编写new和delete时需固守常规
Adhere to convention when writing new and delete.
大家好,前一条款我们已经讨论了你在什么时候想要写个自定义的operator new和operator delete,但并没有解释
当你这么做时必须遵守什么规则.我先来总体说一下,然后再分析这些规则.
要实现一致性operator new必得返回正确的值,内存不足时必得调用new-handling函数(见条款49),必须有对付零
内存需求的准备,还需避免不慎掩盖正常形式的new(虽然这比较偏近class的接口要求而非实现要求).
先来说说关于其返回值,如果它有能力供应客户申请的内存,就返回一个指针指向那块内存.如果没有那个能力,就
遵循49描述的原则,并抛出一个bad_alloc异常.
而实际上operator new不止一次尝试进行内存分配,并在每次失败后调用new-handing函数.这里假设new-
handling函数也许能够做某些动作释放某些内存.只有当当前的new-handling函数指针为null时,operator new才会抛
出异常.
C++规定,即使客户要求0bytes,operator new也得返回一个合法指针.这种诡异的行为其实是为了简化语言的其它
部分.下面是个non-member operator new伪码:
void* operator new(std::size_t size) throw(std::bad_alloc)
{
using namespace std;
if( size == 0 ){
size = 1;
}
while( true ){
...//try to allocate size bytes memory.
if( allocate_succeed ){
return (point_to_allocted_memory);
}
//allocate failed:find current new-handling function(as following)
new_handler global_handler = set_new_handler( 0 );
set_new_handler( global_handler );
if( global_handler ){
( *global_handler )();
} else {
throw std::bad_alloc();
}
}
}
现在我们注意一下这里的一个可能会出现的问题:很多人没有意识到operator new成员函数会被derived classes继
承,那就会出现,有可能base class的operator new被调用用以分配derived class对象:
struct Base{
static void* operator new(std::size_t size) throw( std::bad_alloc );
...
};
struct Derived:public Base{...};
Derived* p = new Derived;//call Base::operator new.
如果Base class专属的operator new并非被设计对付上述情况(实际上往往如此),处理此情势的最佳做法是将'内存
申请量错误'的调用行为改采标准operator new,像这样:
void* Base::operator new(std::size_t size) throw(std::bad_alloc)
{
if( size != sizeof(Base) ){
return ::operator new( size ); //call standard operator new version.
}
...
}
如果你打算控制class专属之'arrays内存分配行为',那么你需要实现operator new的array兄弟版:operator new
[].这个通常被称为"array new".如果你要写这个operator new[],记住,唯一需要做的事情就是分配一块未加工内存.因
为你无法对array之内迄今为止尚未存在的元素对象做任何事情.实际上你甚至无法计算这个array将含有多少个元素.首
先你不知道每个对象多大,因此你不能在Base::operator new[]内假设每个元素对象大小是sizeof(Base),此外传递给
它的参数size的值有可能比'将被填以对象'的内存数量更多.
这就是写operator new时候你需要奉行的规矩.operator delete情况更简单,你需要记住的唯一一件事情就是C++
保证'删除null指针永远安全',所以你必须兑现这项保证.下面就是non-member operator delete的伪码:
void operator delete( void* raw_memory ) thrwo()
{
if( raw_memory == 0 ){
return;
}
...//now,free raw memory block.
}
而对于member版本的也很简单.
void Base::operator delete( void* raw_memory,std::size_t size ) throw()
{
if( raw_memory == 0 ){
return;
}
if( size != sizeof(Base) ){ //if size error, call standard operator delete
::operator delete( raw_memory );
return;
}
...//now,free your raw memory block.
return;
}
如果即将删除的对象派生自某个base class而后者欠缺virtaul析构函数,那么C++传给operator delete的size_t数
值可能不正确.这是'让你的base class拥有virtual析构函数'的一个够好的理由.
好了,今天讨论结束,明天再见.
请记住:
■ operator new应该内含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler.
它也应该有能力处理0bytes申请.class专属版本则还应该处理'比正确大小更大的(错误)申请'.
■ operator delete应该在收到null指针时不做任何事情.class专属版本则还应该处理'比正确大小更大的(错
误)申请'.
条款51:编写new和delete时需固守常规
最新推荐文章于 2025-08-19 19:33:12 发布
本文详细讲解了在C++中如何正确地实现自定义new和delete运算符,包括处理内存分配失败、确保内存请求的安全性以及如何适配不同大小的内存请求。
271

被折叠的 条评论
为什么被折叠?



