Effective C++ 规则49:了解 new-handler 的行为

瑞星  金牌会员 | 2025-1-26 13:37:48 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 990|帖子 990|积分 2970

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1、背景

在 C++ 中,new 运算符用于动态分配内存。然而,当内存分配失败时,程序默认会抛出一个 std::bad_alloc 非常。为了更灵活地处置惩罚这种情况,C++ 提供了一种机制,允许开发者自定义内存分配失败时的行为。这就是 new-handler 机制的作用。new-handler 是一种回调函数,当 new 操作符无法分配内存时,运行时系统会调用这个回调函数。通过设置自定义的 new-handler,开发者可以在内存分配失败时执行一些特定的操作,比方:


  • 释放未利用的内存。
  • 记录错误日志。
  • 实验再次分配内存。
  • 终止程序或采取其他恢复措施。
    new-handler 的设计使程序可以大概优雅地应对内存分配失败,而不是直接瓦解。
2、怎样利用new-handler

C++ 提供了两个与 new-handler 相干的标准函数:


  • std::set_new_handler,用于设置全局的 new-handler 函数,定义在头文件 中。
  • std::new_handler,是一个函数指针,指向当前的 new-handler,以下是 std::set_new_handler 的函数署名:
  1. #include <new>
  2. std::new_handler std::set_new_handler(std::new_handler new_p) noexcept;
复制代码


  • 参数:new_p 是指向新的 new-handler 函数的指针。
  • 返回值:返回之前设置的 new-handler。
  • 非常保证:此函数不抛出任何非常。
3、示例代码

以下是一个完整示例,展示怎样利用 std::set_new_handler 设置自定义的 new-handler。
  1. #include <iostream>
  2. #include <new> // std::set_new_handler
  3. #include <cstdlib> // std::abort
  4. // 自定义 new-handler 函数
  5. void myNewHandler() {
  6.     std::cerr << "Memory allocation failed. Attempting to recover..." << std::endl;
  7.     // 可以选择释放资源或记录日志
  8.     // 示例:直接终止程序
  9.     std::abort();
  10. }
  11. int main() {
  12.     // 设置全局的 new-handler
  13.     std::set_new_handler(myNewHandler);
  14.     try {
  15.         // 故意尝试分配超大内存
  16.         size_t size = static_cast<size_t>(-1);
  17.         int* p = new int[size];
  18.         delete[] p;
  19.     } catch (const std::bad_alloc& e) {
  20.         std::cerr << "Caught exception: " << e.what() << std::endl;
  21.     }
  22.     return 0;
  23. }
复制代码
输出效果:Memory allocation failed. Attempting to recover…
由于,当 new 运算符无法分配内存时,运行时调用了自定义的 myNewHandler,打印错误信息并终止程序。如果未设置 new-handler,程序默认会抛出 std::bad_alloc 非常。
有时,我们可能盼望为某个特定的类设置专属的 new-handler,而不是全局见效。这可以通过类的静态成员函数实现,代码如下:
  1. #include <iostream>
  2. #include <new>
  3. #include <cstdlib>
  4. class Widget {
  5. public:
  6.     // 设置类专属的 new-handler
  7.     static void setNewHandler(std::new_handler p) noexcept {
  8.         currentHandler = p;
  9.     }
  10.     // 重载 operator new,使用类专属的 new-handler
  11.     static void* operator new(size_t size) {
  12.         std::new_handler oldHandler = std::set_new_handler(currentHandler);
  13.         void* mem;
  14.         try {
  15.             mem = ::operator new(size);
  16.         } catch (...) {
  17.             std::set_new_handler(oldHandler); // 恢复旧的 handler
  18.             throw; // 继续抛出异常
  19.         }
  20.         std::set_new_handler(oldHandler); // 恢复旧的 handler
  21.         return mem;
  22.     }
  23. private:
  24.     static std::new_handler currentHandler;
  25. };
  26. // 定义静态成员
  27. std::new_handler Widget::currentHandler = nullptr;
  28. // 自定义 new-handler
  29. void customNewHandler() {
  30.     std::cerr << "Widget allocation failed. Cleaning up..." << std::endl;
  31.     std::abort();
  32. }
  33. int main() {
  34.     Widget::setNewHandler(customNewHandler);
  35.     try {
  36.         // 尝试分配超大内存
  37.         Widget* w = new Widget[static_cast<size_t>(-1)];
  38.         delete[] w;
  39.     } catch (const std::bad_alloc& e) {
  40.         std::cerr << "Caught exception: " << e.what() << std::endl;
  41.     }
  42.     return 0;
  43. }
复制代码
4、利用new-handler的注意事项



  • 确保 new-handler 是无非常的:new-handler 必须是 noexcept 函数。如果 new-handler 抛出非常,标准库会调用 std::terminate。
  • 处置惩罚恢复逻辑:在 new-handler 中,实验释放未利用的内存或其他资源,尽量制止直接终止程序。
  • 制止递归调用:如果在 new-handler 中实验再次分配内存,可能导致递归调用 new-handler,最终导致程序瓦解。
  • 线程安全性:std::set_new_handler 会影响全局的 new-handler,因此在多线程情况下利用时须要格外小心。
5、总结

new-handler 是 C++ 提供的一种强盛的机制,允许开发者在内存分配失败时定制处置惩罚逻辑。通过全局或类专属的 new-handler,程序可以优雅地应对内存不敷的情况。只管如此,new-handler 的利用须要谨慎。公道地释放资源、制止递归调用以及处置惩罚非常是实现一个可靠 new-handler 的关键。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

瑞星

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表