Effective C++ 3nd 读书摘要(八、定制new和delete)Item49 - 52

八、定制new和delete
Item49. 了解new-handler的行为
当然operator new抛出异常以反映内存需求出错之前,会先调用一个客户指定的错误处理函数(new-handler)。
namespace std {
  typedef void (*new_handler)();
  new_handler set_new_handler(new_handler p) throw();
}

以下是使用示例:

当operator new无法满足内存申请是,它会不断调用new-handler函数,直到找到足够内存。一个设计良好的new-handler函数必须做以下事情:
1. 让更多内存可被使用。
2. 安装另一个new-handler。获悉另一个有能力得到更多内存的new-handler。
3. 卸除new-handler。即设为null指针,这时operator new会在内存分配不成功时抛出异常。
4. 抛出bad_alloc(或其子类)的异常。这样的异常不会被operator new捕捉,因此会被传播到内存索求处。
5. 不返回。通常调用abort或exit。

 

如果希望new-handler属于一个class,可以如下:

有了以上的class template,为Widget添加set_new_handler支持能力就轻而易举了:
class Widget: public NewHandlerSupport<Widget> {
  ...                          // as before, but without declarations for
};                             // set_new_handler or operator new


几点说明:
1. 类型参数T从未被使用。它只是用来区分不同的derived class。
2. Widget继承自一个templatized base class,而后者又以Widget作为类型参数,这种技术叫curiously

recurring template pattern (CRTP)。(Meyers建议用Do It For Me)

 

Item50. 了解new和delete的合理替换时机
三个常见的理由:
1. 用来检测运用上的错误。可以自定一个operator new, 可以超额分配内存,以额外空间放置特定的byte

patterns(signature)。operator delete得以检查上述签名是否原封不动。
2. 为了强化效能。默认的只能对大部分程序合适。Boost的Pool分配器就对于最常见的“分配大量小型对象”很有帮助。
3. 为了收集使用上的统计数据。


以下是一个global ooperator new,促进并协助检测“overruns”和“underruns”,

这段代码没有考虑齐位,C++要求所有operator new返回的指针都有适当的对齐。TR1支持各类型特定的对齐条件。

 

小结:何时可以在“全局性的”或“class专属的”基础上合理替换缺省的new和delete——
1. 为了检测运用错误
2. 为了收集动态分配内存之使用统计信息
3. 为了增加分配和归还的速度
4. 为了降低缺省内存管理器带来的空间额外开销
5. 为了弥补缺省分配器中的非最佳齐位
6. 为了将相关对象成簇集中(placement版本)
7. 为了获得非传统的行为

(thy:记住80-20原则)

 

Item52. 编写new和delete时需固守常规
1. operator new——
 必须返回正确的值。有能力就返回指针,没能力就抛出bad_alloc(实际上operator new在每次失败后调用

new-handling函数,只有当new-handling函数的指针为null时,operator new才抛出异常)。C++标准规定即使客户要求0 bytes,也得返回一个合法指针

但是,针对class X设计的operator new,其行为很典型地只为大小刚好为sizeof(X)的对象而设计。然而一旦被继承下去:

所以为了考虑这种情况:

注意C++规定所有非附属(独立式)(freestading)对象必须有非零大小,所以sizeof(Base)不可能为0。

 

operator new[]要复杂得多,因为如果你申请一块内存,你不能保证它能为size_t个对象所用,因为可能被继承。

 

2. operator delete——
要简单得多,全局版本:

member版本:

记住:如果base class没有virtual析构函数,则继承后C++传给operator delete的size_t数值可能不正确(Item7)。

 

Item52. 写了placement new也要写placement delete
如果你有一个正常的void* operator new(std::size_t) throw(std::bad_alloc);
系统会找到相应的delete。
void operator delete(void *rawMemory) throw();  // normal signature at global scope
void operator delete(void *rawMemory,           // typical normal
                   std::size_t size) throw(); // signature at class scope

但如果你有了非正常的new:

对应的delete应该是:
void operator delete(void *, std::ostream&) throw();
上面的Widget中没有,所以如果Widget构造函数抛出异常,则不会有任何operator delete被调用。
正确设计如下:

不过如果客户调用delete pw;    // invokes the normal operator delete
则只有正常形式的operator delete调用,因为placement delete只有在“伴随placement new调用而触发的构造
函数”出现异常时才会被调用。

 

这意味着我们要同时提供正常的版本和placement版本。后者的额外参数必须和operator new一样。
最终,为了避免掩盖base class和global中的new和delete,我们建立一个base class:

在使用时:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值