本篇笔记包含 Term 49~55。

Term 49:了解 new-handler 的行为

当 operator new 无法满足内存分配需求时,会抛出异常,且在此之前,会先调用用户设置的错误处理函数(即 new-handler)。通过 std::set_new_handler 可以设置 new-handler,要使其设计良好,需要满足以下条件:

  1. 让更多内存可被使用
  2. 安装另一个 new-handler:设置调用另一个可能解决问题的 new-handler
  3. 卸除 new-handler:将 null 指针传给 set_new_handler。没有安装 new-handler 时 operator new 分配失败抛出异常
  4. 不返回:通常调用 abort 或 exit

C++ 没有直接为 class 定制 new-handler 的语法,但我们仍然可以办到:

template <typename T>
class NewHandlerSupport {  // mixin 风格的 base class,用以支持 class 专属的 set_new_handler
 public:
  static std::new_handler set_new_handler(std::new_handler) throw();
  static void* operator new(std::size_t size) throw(std::bad_alloc);

 private:
  static std::new_handler currentHandler;
};
template <typename T>
std::new_handler NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw() {
  std::new_handler oldHandler = currentHandler;
  currentHandler = p;
  return oldHandler;
}
template <typename T>
void* NewHandlerSupport<T>::operator new(std::size_t size) throw(std::bad_alloc) {
  NewHandlerSupport h(std::set_new_handler(currentHandler));
  return ::operator new(size);
}
template <typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;  // 将 currentHandler 初始化为 null
// 使用方式如下
class Widget: public NewHandlerSupport<Widget> { ... } // 以自身类型继承这样一个模板类就足够

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

使用自定义的 operator new 和 operator delete 的合理使用场景:

  1. 用来检测运用错误
  2. 收集动态分配内存的统计数据
  3. 增加分配和归还的速度
  4. 降低默认内存管理器带来的额外开销
  5. 实现内存地址对齐(alignment)
  6. 将相关的对象成簇集中,降低缺页中断的概率,因为它们很可能同时被调用
  7. 实现一些特定行为,例如在归还内存时将内容抹除

Term 51:编写 new 和 delete 时需固守常规

operator new 分配成功时返回指针,不成功则调用 new-handler,可能会再尝试分配,所有的 new-handler 失败,则抛出 bad_alloc 异常。在设计自定义的 operator new 和 operator delete 过程中,需要注意:

  1. 即使请求的是 0 byte, operator new 也要返回一个合法的指针,则可以简单地将 0 视为 1
  2. operator new 应该内含无穷循环来实现上面那段话
  3. class 专属版本的 operator new 应该处理错误大小的分配请求,比如被派生类继承之后,对派生类使用(基类的) new 会造成大小错误
  4. operator delete 应该在收到 null 指针时不做任何事
  5. class 专属版本的 operator delete 应该处理错误大小的分配请求

Term 52:写了 placement new 也要写 placement delete

  1. 编写了 placement new 也要写 placement delete,否则可能会造成内存泄漏
  2. 声明 placement new 和 placement delete 时,要注意不要遮蔽了它们的正常版本

Term 53:不要轻忽编译器的警告

  1. 认真考虑编译器的警告
  2. 不同编译器针对相同代码的警告处理可能不一样

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

如题。

Term 55:让自己熟悉 Boost

在 Modern C++ 的时代,Boost 的应用意义已经不如从前。但在熟悉标准程序库之后,Boost 仍是很好的学习对象,它包含了可能在未来会纳入标准程序库的一些东西。