💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
## 条款49:了解 new-handler 的行为 Understand the behavior of the new-handler. ### new-handler 当 `operator new` 无法满足某一内存分配需求时,它会抛出异常(以前它会返回一个 null 指针)。当 `operator new` 抛出异常以反映一个未获满足的内存需求之前,它会先调用一个客户指定的错误函数,一个所谓的 new-handler。客户可以通过调用 `set_new_handler` 去指定这个用于处理内存不足的函数: ```cpp namespace std { // 声明于标准程序库函数; typedef void (*new_handler) (); new_handler set_new_handler(new_handler p) throw(); } void outOfMem() { std::cerr << "Unable to satisfy request for memory\n"; std::abort(); } int main() { std::set_new_handler(outOfMem); ... } ``` 当 `operator new` 无法满足内存申请时,它会不断调用 new-handler 函数,直到找到足够内存。所以设计良好的 new-handler 非常有必要,它必须做以下事情: * 让更多内存可被使用:使得下一次 `operator new` 可能成功; * 安装另一个 new-handler:如果当前 new-handler 无法获得更多内存,或许它知道哪里可以获取; * 卸除 new-handler:将 null 指针传给 `set_new_handler`,如果没有安装任何 new-handler,在申请内存失败时则会抛出异常; * 抛出 bad\_alloc 的异常; * 不返回; ### class 专属 new-handler C++ 并不支持 class 专属之 new-handler,不过可以自己实现,只需令 class 提供自己的 `set_new_handler` 和 `operator new`即可: ```cpp class Widget { public: static std::new_handler set_new_handler(std::new_handler p) throw(); static void* operator new(std::size_t size) throw(std::bad_alloc); private: static std::new_handler currentHandler; }; std::new_handler Widget::set_new_handler(std::new_handler p) throw() { std::new_handler oldHandler = currentHandler; currentHandler = p; return oldHandler; } ``` 标准版 `set_new_handler`会将它获得的指针储存起来,然后返回先前存储的指针。而 `operator new` 中通常使用资源处理类来管理 new-handler: ```cpp class NewHandlerHolder { public: explicit NewHandlerHolder(std::new_handler nh): handler(nh) {} ~NewHandlerHolder() { std::set_new_handler(handler); } private: std::new_handler handler; NewHandlerHolder(const NewHandlerHolder&); NewHandlerHolder& operator=(const NewHandlerHolder&); }; void* Widget::operator new(std::size_t size) throw(std::bad_alloc) { NewHandlerHolder h(std::set_new_handler(currentHandler)); return ::operator new(size); } ``` ### 新旧 operator new 旧版本的 `operator new` 在分配失败时返回 null,而新版的则会抛出异常。若想在新版标准中使用旧版的 `operator new` ,需要使用 `nothrow` 形式: ```cpp Widget* p = new (std::nothrow) Widget; ``` 使用 `nothrow new` 只能保证 `operator new` 不抛出异常,不保证 `Widget* p = new (std::nothrow) Widget`这样的表达式不出异常,异常可能出现在类的构造函数中。