关闭

编写高质量代码:改善C++程序的150个建议(连载17)

发表于:2012-4-26 09:35

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:李健    来源:51Testing软件测试网采编

  建议33:小心翼翼地重载operator new/ operator delete

  虽然C++标准库已经为我们提供了new与delete操作符的标准实现,但是由于缺乏对具体对象的具体分析,系统默认提供的分配器在时间和空间两方面都存在着一些问题:分配器速度较慢,而且在分配小型对象时空间浪费比较严重,特别是在一些对效率或内存有较大限制的特殊应用中。比如说在嵌入式的系统中,由于内存限制,频繁地进行不定大小的内存动态分配很可能会引起严重问题,甚至出现堆破碎的风险;再比如在游戏设计中,效率绝对是一个必须要考虑的问题,而标准new与delete操作符的实现却存在着天生的效率缺陷。此时,我们可以求助于new与delete操作符的重载,它们给程序带来更灵活的内存分配控制。除了改善效率,重载new与delete还可能存在以下两点原因:

  检测代码中的内存错误。

  获得内存使用的统计数据。

  相对于其他的操作符,operator new具有一定的特殊性,在多个方面上与它们大不相同。首先,对于用户自定义类型,如果不重载,其他操作符是无法使用的,而operator new则不然,即使不重载,亦可用于用户自定义类型。其次,在参数方面,重载其他操作符时参数的个数必须是固定的,而operator new的参数个数却可以是任意的,只需要保证第一个参数为size_t类型,返回类型为void *类型即可。所以operator new的重载会给我们一种错觉:它更像是一个函数重载,而不是一个操作符重载。

  关于operator new重载函数的形式,在C++标准中有如下规定:

  分配函数应当是一个类的成员函数或者是全局函数;如果一个分配函数被放于非全局名空间中,或者是在全局名空间被声明为静态,那这个程序就是格式错误的。

  也就是说,重载的operator new必须是类成员函数或全局函数,而不可以是某一名空间之内的函数或是全局静态函数。此外,还要多加注意的是,重载operator new时需要兼容默认的 operator new的错误处理方式,并且要满足C++的标准规定:当要求的内存大小为0 byte时也应该返回有效的内存地址。

  所以,全局的operator new重载应该不改变原有签名,而是直接无缝替换系统原有版本,如下所示:

  1. void * operator new(size_t size)  
  2. {  
  3.      if(size == 0)  
  4.       size = 1;  
  5.      void *res;  
  6.      for(;;)  
  7.      {  
  8.        //allocate memory block  
  9.        res = heap_alloc(size);  
  10.        //if successful allocation, return pointer to memory  
  11.        if(res)  
  12.            break;  
  13.       //call installed new handler  
  14.     if (!CallNewHandler(size))  
  15.         break;  
  16.     //new handler was successful -- try to allocate again  
  17.  }  
  18.  return res;  
  19. }

  如果是用这种方式进行的重载,再使用时就不需要包含new头文件了。“性能优化”时通常采用这种方式。

  如果重载了一个operator new,记得一定要在相同的范围内重载operator delete。因为你分配出来的内存只有你自己才知道应该如何释放。如果你偷懒或者是忘记了,编译器就会求助于默认的operator delete,用默认方式释放内存。虽然程序编译可以通过,但是这将导致惨重的代价。所以,你必须时刻记得在写下operator new的同时写下operator delete。相对于operator new,重载operator delete要简单许多,如下所示:

  1. void operator delete(void* p)  
  2. {  
  3.       if(p==NULL)  
  4.          return;  
  5.       free(p);  
  6. }

  唯一要注意的一点就是,须遵循C++标准中要求删除一个NULL指针是安全的这一规定。在全局空间中重载void * operator new(size_t size)函数将会改变所有默认的operator new的行为方式,所以一定要小心使用。

  如果使用不同的参数类型重载operator new/delete,则请采用如下函数声明形式:

  1. //返回的指针必须能被普通的 ::operator delete(void*) 释放  
  2. void* operator new(size_t size, const char* file, int line);  
  3. //析构函数抛异常时被调用  
  4. void operator delete(void* p, const char* file, int line);

21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号