本篇笔记包含 Term 49~55。
Term 49:了解 new-handler 的行为
当 operator new 无法满足内存分配需求时,会抛出异常,且在此之前,会先调用用户设置的错误处理函数(即 new-handler)。通过 std::set_new_handler
可以设置 new-handler,要使其设计良好,需要满足以下条件:
- 让更多内存可被使用
- 安装另一个 new-handler:设置调用另一个可能解决问题的 new-handler
- 卸除 new-handler:将 null 指针传给 set_new_handler。没有安装 new-handler 时 operator new 分配失败抛出异常
- 不返回:通常调用 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 的合理使用场景:
- 用来检测运用错误
- 收集动态分配内存的统计数据
- 增加分配和归还的速度
- 降低默认内存管理器带来的额外开销
- 实现内存地址对齐(alignment)
- 将相关的对象成簇集中,降低缺页中断的概率,因为它们很可能同时被调用
- 实现一些特定行为,例如在归还内存时将内容抹除
Term 51:编写 new 和 delete 时需固守常规
operator new 分配成功时返回指针,不成功则调用 new-handler,可能会再尝试分配,所有的 new-handler 失败,则抛出 bad_alloc 异常。在设计自定义的 operator new 和 operator delete 过程中,需要注意:
- 即使请求的是 0 byte, operator new 也要返回一个合法的指针,则可以简单地将 0 视为 1
- operator new 应该内含无穷循环来实现上面那段话
- class 专属版本的 operator new 应该处理错误大小的分配请求,比如被派生类继承之后,对派生类使用(基类的) new 会造成大小错误
- operator delete 应该在收到 null 指针时不做任何事
- class 专属版本的 operator delete 应该处理错误大小的分配请求
Term 52:写了 placement new 也要写 placement delete
- 编写了 placement new 也要写 placement delete,否则可能会造成内存泄漏
- 声明 placement new 和 placement delete 时,要注意不要遮蔽了它们的正常版本
Term 53:不要轻忽编译器的警告
- 认真考虑编译器的警告
- 不同编译器针对相同代码的警告处理可能不一样
Term 54:让自己熟悉包括 TR1 在内的标准程序库
如题。
Term 55:让自己熟悉 Boost
在 Modern C++ 的时代,Boost 的应用意义已经不如从前。但在熟悉标准程序库之后,Boost 仍是很好的学习对象,它包含了可能在未来会纳入标准程序库的一些东西。