《Effective C++》精简总结——49~55条款



八、定制 new 和 delete

  • Java和.NET都有所谓的内置“垃圾回收能力”,C++对内存管理的纯手工管理方法仍以其手工可管理性而有其存在的价值;
  • C++内存管理例程的行为主要是分配例程和归还例程(allocation and deallocation routines,也即是 operator new 和 operator delete),辅以 new-handler(operator new 无法满足客户内存需求时调用);
  • 条款16:new 和 delete 要成对使用且形式相同
  • STL 容器所使用的 heap 内存是由容器所拥有的分配器对象(allocator object)管理,而不是 new 和 delete 直接管理;

49. 了解 new-handler 的行为

Understand the behavior of the new-handler.

  • 当 operator new 抛出异常以反映一个未获满足的内存需求之前,会先调用一个客户指定的错误处理函数,即所谓的 new-handler 函数。为指定该处理内存不足的函数,客户必须调用 set_new_handler 函数(声明于<new>的一个标准库函数);
  • 一个设计良好的 new-handler 应当做到以下事情:
    ① 让更多内存可被使用。程序一开始执行就分配一大块内存,而后当 new-handler 第一次被调用,就将其释还给程序使用;
    ② 安装另一个 new-handler。若是当前的 new-handler 无法取得更多可用内存,且知道另外哪个 new-handler 有此能力,则安装另外的 new-handler 以替换当前的 new-handler,也即是自我修改;
    ③ 卸载 new-handler,也就是将 null 指针传给 set_new_handler。若是没有安装任何 new-handler, operator new会在内存分配不成功时抛出异常;
    ④ 抛出 bad_alloc(或派生至bad_alloc)的异常。这样的异常不会被 operator new 捕捉,因此会被传播到内存处求索;
    ⑤ 不返回,通常调用 abort 或 exit;
  • new-handler 应当被视作一种系统资源,在原先的 global new-handler 被更改且新的使用过后应当恢复至最初的 global new-handler,即对 global new-handler 运用资源管理对象(resource-managing object)以防止资源泄漏,见 条款13: 以对象管理资源
  • 令每个 class 提供自己的 set_new_handler 和 operator new,则可实现 class 的 new-handler 的定制。即在类定义中重写 set_new_handler 函数?,并搭配资源管理对象实现。同时,实现方案并不因 class 的不同而不同;
  • class/struct 的 static 成员必须在类型的定义式外被定义;
  • nothrow new 是一个颇为局限的工具,因为其只适用于内存分配;后继的函数调用仍可能抛出异常;
  • 这里内容与 c++内存管理原始工具(primitives) 内容有些许重叠,特别是对 new handler 和 set_new_handler

50. 了解 new 和 delete 的合理替换时机

Understand when it makes sense to replace new and delete.

  • 为什么需要替换编译器所提供的 operator new 或 operator delete:
    ① 用来检测运行错误(对heap);
    ② 提高效能,如改善内存破碎(fragmentation)问题。增加分配和归还的速度;降低缺省内存管理其带来的空间额外开销;改善内存对齐alignment问题;
    ③ 获得非传统的行为,如收集使用上的统计数据(对heap));
    ④ 将相关对象成簇集中;
  • overruns(写入点在分配区块尾端之后);
  • underruns(写入点在分配区块起点之前);

51. 编写 new 和 delete 须遵守规则

Adhere to convention when writing new and delete.

  • operator new 内含一个无限循环,当分配内存成功或是返回 bad_alloc 时跳出该循环,否则一直寻找更换 global new_handler,调用 set_new_handler。在多线程环境下,global new_handler 应当加入锁机制。它也应该有能力处理 0bytes 申请, class 专属版本则还应该处理 比正确大小更大(派生类大小一般大于基类)的错误申请;
  • operator delete 应该在收到 null 指针时不做任何事。 class 专属版本则还应该处理 比正确大小更大(派生类大小一般大于基类)的错误申请;

52. 写了 placement new 也要写 placement delete

Write placement delete if you write placement new.

  • 运行期若是抛出异常,将先前的操作必须取消并复原,若是涉及内存分配的 new 操作,则恢复规则为:
    ① 提供placement new 运算,也即接受额外参数的运算,多数为一个void*指针;
    ② 如果一个带额外参数的 operator new(称为placement new) 没有带相同额外参数的 operator delete(也称为placement delete),那么当 new 的内存分配动作需要取消并复原时就不会有任何 operator delete 被调用;
  • 对于抛出异常的情况,如上条所述,须提供 placement new 和相同 parameter list 的 placement delete。但是对于正常情况下,无异常抛出的情形,仍要提供正常版本的 operator new 和 operator delete,以保证用户可以对内存进行手动管理。即,如果要解决与 placement new 相关的内存泄漏问题,须同时提供正常版本的 operator delete和一个 placement 版本(用于构造期间有异常抛出)
  • 还须注意的是,派生类中的 operator news 会掩盖 global 版本和继承而得的 operator new 版本(详见 条款33:避免遮掩继承而来的名称)。一般 std 提供的 global news 有 normal new, placement new(with void* extra para) 和 nothrow new(void* operator new(std::size_t, const std::nothrow_t&) throw();)。若是相依自定形式扩展标准形式的 operator new(即写 placement new),可以利用 继承机制 和 using声明式


九、杂项讨论


53. 要注意编译器的警告

Pay attention to compiler warinings.

  • 严肃对待编译器发出的警告信息。努力在你的编译器的最高(最严苛)警告级别下争取无任何警告的编译;
  • 不能过度依赖编译器的警告能力,因为不同的编译器对待事情的态度并不相同,不同的编译器可能会导致相关信息没有警告,导致程序出现纰漏;

54. 让自己熟悉包括 TR1 在内的标准程序库

Familiarize yourself with the standard library, including TR1.

  • TR1(Technical Report 1):可以将其理解为 C++0x正式版本的非正式版本;
  • C++ 标准程序库的主要机能由 STL、iostreams、locales 组成,并包含 C99 标准程序库;
  • TR1 添加了智能指针(tr1::share_ptr(用于非环形指),tr1::weak_ptr(环形指向))、一般化函数指针(tr1::function)、hash-based容器、正则表达式以及另外的10个组件的支持;
  • TR1 自身只是一个规范,Boost 就是一份其实物;

55. 让自己熟悉 Boost

Famliarize yourself with Boost.

  • Boost 是一个社群,网站。提供了许多TR1组件实现品和其它程序库。
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值