`
阿尔萨斯
  • 浏览: 4109292 次
社区版块
存档分类
最新评论

重载new/delete要遵循的规则

 
阅读更多
<iframe align="center" marginwidth="0" marginheight="0" src="http://www.zealware.com/csdnblog.html" frameborder="0" width="728" scrolling="no" height="90"></iframe>

条款8: 写operator new和operator delete时要遵循常规

自己重写operator new时(条款10解释了为什么有时要重写它),很重要的一点是函数提供的行为要和系统缺省的operator new一致。实际做起来也就是:要有正确的返回值;可用内存不够时要调用出错处理函数(见条款7);处理好0字节内存请求的情况。此外,还要避免不小心隐藏了标准形式的new,不过这是条款9的话题。

有关返回值的部分很简单。如果内存分配请求成功,就返回指向内存的指针;如果失败,则遵循条款7的规定抛出一个std::bad_alloc类型的异常。

但事情也不是那么简单。因为operator new实际上会不只一次地尝试着去分配内存,它要在每次失败后调用出错处理函数,还期望出错处理函数能想办法释放别处的内存。只有在指向出错处理函数的指针为空的情况下,operator new才抛出异常。

另外,c++标准要求,即使在请求分配0字节内存时,operator new也要返回一个合法指针。(实际上,这个听起来怪怪的要求确实给c++语言其它地方带来了简便)

这样,非类成员形式的operator new的伪代码看起来会象下面这样:

void*operatornew(size_tsize)//operatornew还可能有其它参数
{

if(size==0){//处理0字节请求时,
size=1;//把它当作1个字节请求来处理
}
while(1){
分配size字节内存;

if(分配成功)
return(指向内存的指针);

//分配不成功,找出当前出错处理函数
new_handlerglobalhandler=set_new_handler(0);
set_new_handler(globalhandler);

if(globalhandler)(*globalhandler)();
elsethrowstd::bad_alloc();
}
}

处理零字节请求的技巧在于把它作为请求一个字节来处理。这看起来也很怪,但简单,合法,有效。而且,你又会多久遇到一次零字节请求的情况呢?

你又会奇怪上面的伪代码中为什么把出错处理函数置为0后又立即恢复。这是因为没有办法可以直接得到出错处理函数的指针,所以必须通过调用set_new_handler来找到。办法很笨但也有效。

条款7提到operator new内部包含一个无限循环,上面的代码清楚地说明了这一点——while (1)将导致无限循环。跳出循环的唯一办法是内存分配成功或出错处理函数完成了条款7所描述的事件中的一种:得到了更多的可用内存;安装了一个新的new-handler(出错处理函数);卸除了new-handler;抛出了一个std::bad_alloc或其派生类型的异常;或者返回失败。现在明白了为什么new-handler必须做这些工作中的一件。如果不做,operator new里面的循环就不会结束。

很多人没有认识到的一点是operator new经常会被子类继承。这会导致某些复杂性。上面的伪代码中,函数会去分配size字节的内存(除非size为0)。size很重要,因为它是传递给函数的参数。但是大多数针对类所写的operator new(包括条款10中的那种)都是只为特定的类设计的,不是为所有的类,也不是为它所有的子类设计的。这意味着,对于一个类x的operator new来说,函数内部的行为在涉及到对象的大小时,都是精确的sizeof(x):不会大也不会小。但由于存在继承,基类中的operator new可能会被调用去为一个子类对象分配内存:

classbase{
public:
staticvoid*operatornew(size_tsize);
...
};

classderived:publicbase//derived类没有声明operatornew
{...};

derived
*p=newderived;//调用base::operatornew

如果base类的operator new不想费功夫专门去处理这种情况——这种情况出现的可能性不大——那最简单的办法是把这个“错误”数量的内存分配请求转给标准operator new来处理,象下面这样:

void*base::operatornew(size_tsize)
{
if(size!=sizeof(base))//如果数量“错误”,让标准operatornew
return::operatornew(size);//去处理这个请求
//

...
//否则处理这个请求
}

“停!”我听见你在叫,“你忘了检查一种虽然不合理但是有可能出现的一种情况——size有可能为零!”是的,我没检查,但拜托下次再叫出声的时候不要这么文绉绉的。:)但实际上检查还是做了,只不过融合到size != sizeof(base)语句中了。c++标准很怪异,其中之一就是规定所以独立的(freestanding)类的大小都是非零值。所以sizeof(base)永远不可能是零(即使base类没有成员),如果size为零,请求会转到::operator new,由它来以一种合理的方式对请求进行处理。(有趣的是,如果base不是独立的类,sizeof(base)有可能是零,详细说明参见"my article on counting objects")。

如果想控制基于类的数组的内存分配,必须实现operator new的数组形式——operator new[](这个函数常被称为“数组new”,因为想不出"operator new[]")该怎么发音)。写operator new[]时,要记住你面对的是“原始”内存,不能对数组里还不存在的对象进行任何操作。实际上,你甚至还不知道数组里有多少个对象,因为不知道每个对象有多大。基类的operator new[]会通过继承的方式被用来为子类对象的数组分配内存,而子类对象往往比基类要大。所以,不能想当然认为base::operator new[]里的每个对象的大小都是sizeof(base),也就是说,数组里对象的数量不一定就是(请求字节数)/sizeof(base)。关于operator new[]的详细介绍参见条款m8。

重写operator new(和operator new[])时所有要遵循的常规就这些。对于operator delete(以及它的伙伴operator delete[]),情况更简单。所要记住的只是,c++保证删除空指针永远是安全的,所以你要充分地应用这一保证。下面是非类成员形式的operator delete的伪代码:

voidoperatordelete(void*rawmemory)
{
if(rawmemory==0)return;//如果指针为空,返回

释放rawmemory指向的内存;
return;
}

这个函数的类成员版本也简单,只是还必须检查被删除的对象的大小。假设类的operator new将“错误”大小的分配请求转给::operator new,那么也必须将“错误”大小的删除请求转给::operator delete:

classbase{//和前面一样,只是这里声明了
public://operatordelete
staticvoid*operatornew(size_tsize);
staticvoidoperatordelete(void*rawmemory,size_tsize);
...
};

voidbase::operatordelete(void*rawmemory,size_tsize)
{
if(rawmemory==0)return;//检查空指针

if(size!=sizeof(base)){//如果size"错误",
::operatordelete(rawmemory);//让标准operator来处理请求
return;
}

释放指向rawmemory的内存;

return;
}

可见,有关operator new和operator delete(以及他们的数组形式)的规定不是那么麻烦,重要的是必须遵守它。只要内存分配程序支持new-handler函数并正确地处理了零内存请求,就差不多了;如果内存释放程序又处理了空指针,那就没其他什么要做的了。至于在类成员版本的函数里增加继承支持,那将很快就可以完成。

出处:《Effective C++》
转自:http://www.leftworld.net/online/effectivec/file/ch02c.htm




分享到:
评论

相关推荐

    C++动态内存分配(new/new[]和delete/delete[])详解

    C++动态内存分配(new/new[]和delete/delete[])详解 为了解决这个普通的编程问题,在运行时能创建和销毁对象是基本的要求。当然,C已提供了动态内存分配函数malloc( )和free( ),以及malloc( )的变种(realloc:改变...

    17~C++ new和delete运算符重载

    重载 class A的new/delete 以及 new[]/delete[]运算符: 成功返回非空指针,失败返回空指针 #include #include using namespace std; class A { public: A (int var = 0) : m_var (var) { cout &lt;&lt; A构造: ...

    重载new 用法例子

    重载new ,夸dll库使用new delete ,vs2008工程

    source_delete_new.rar_hook delete_内存泄露_重载new

    解决内存泄露的源代码,重载了NEW DELETE 函数,通过获取指针的钩子,截获内存泄露的元凶

    C++中对使用malloc/new引起的内存泄露的检查

    C++中对使用malloc/new引起的内存泄露的检查,重载new/malloc delete/free来进行代码检查

    override_new

    自定义内存池管理内存,并重载new/delete操作符。同时,使用实例进行测试。

    C++ new、delete(new[]、delete[])操作符重载需要注意的问题

    new、delete(new[]、delete[])操作符的重载需要注意: 1.重载的 new、delete(或者 new[]、delete[])操作符必须是类的静态成员函数(为什么必须是静态成员函数,这很好理解,因为 new 操作符被调用的时候,对象还...

    C++中new与delete、malloc与free应用分析

    一般来说,在C/C++的面试时,对于new/delete和malloc/free这两对的使用和区别经常被考查到,如果这种基础的问题都答不上来,估计很难过面试了。本文即是对new/delete和malloc/free这两对的使用和区别较为简单的分析...

    内存泄漏检查类

    C++内存泄漏检查源码,通过重载全局new/delete new[]/delete[]实现,多线程安全。

    高质量C++/C编程指南

    7.8 有了MALLOC/FREE为什么还要NEW/DELETE ? 52 7.9 内存耗尽怎么办? 53 7.10 MALLOC/FREE 的使用要点 54 7.11 NEW/DELETE 的使用要点 55 7.12 一些心得体会 56 第8章 C++函数的高级特性 57 8.1 函数重载的概念 57...

    重载与覆写/重写的区别

    (3) 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。  无法以返回值类型作为重载函数的区分标准。 复制代码 /** * 1. 方法重载只可以通过方法名和方法参数来...

    neicunxielou.rar_内存泄漏_重载new

    游戏高手写的,报告游戏中内存泄漏的代码,重载操作符号new delete

    C++之CNoTrackObject类和new delete操作符的重载实例

    本文实例讲述了C++中CNoTrackObject类和new delete操作符的重载,分享给大家供大家参考。具体如下: 头信息: 代码如下:class CNoTrackObject{  public: //在此出过错,没有加public 默认为类的私有变量,...

    MCast.zip_mcast

    组播 c++ win32控制台 new/delete重载

    全面解析C++中的new,operator new与placement new

    new operator/delete operator就是new和delete操作符,而operator new/operator delete是函数。 new operator(1)调用operator new分配足够的空间,并调用相关对象的构造函数(2)不可以被重载 operator new(1)只...

    自己写的一个内存管理pool

    重载new运算符重载new运算符重载new运算符重载new运算符重载new运算符重载new运算符重载new运算符重载new运算符

    C++内存泄露检测代码

    operator new/delete重载的示例代码。 深度说明了C++类的构造函数、析构函数与operator new、delete之间的关系,相关文章:http://blog.csdn.net/kakaying/archive/2009/08/30/4498563.aspx

    高质量C++编程指南.PDF

    7.8 有了malloc/free为什么还要new/delete ? 7.9 内存耗尽怎么办? 7.10 malloc/free 的使用要点 7.11 new/delete 的使用要点 7.12 一些心得体会 第8章 C++函数的高级特性 8.1 函数重载的概念 8.2 成员函数的重载、...

    高质量C C编程指南

    7.8 有了MALLOC/FREE为什么还要NEW/DELETE ? 7.9 内存耗尽怎么办? 7.10 MALLOC/FREE 的使用要点 7.11 NEW/DELETE 的使用要点 7.12 一些心得体会 第8章 C++函数的高级特性 8.1 函数重载的概念 8.2 成员函数的重载、...

Global site tag (gtag.js) - Google Analytics