当前位置: 首页 > news >正文

Effective C++读书笔记——item49(了解new-handle的行为)

在 C++ 中,当 operator new 无法满足内存分配请求时,它会有特定的处理机制,了解 new-handler 的行为对于处理内存分配失败的情况至关重要。下面结合代码详细介绍相关内容。

1. new-handler 基础概念

当 operator new 无法满足内存分配请求时,会先调用用户指定的 new-handler 错误处理函数,之后才抛出异常。用户可以通过 set_new_handler 函数指定 new-handler

#include <iostream>
#include <new>

// 自定义 new-handler 函数
void outOfMem() {
    std::cerr << "Unable to satisfy request for memory\n";
    std::abort();
}

int main() {
    // 设置 new-handler
    std::set_new_handler(outOfMem);
    int *pBigDataArray = new int[100000000L];
    // 其他代码...
    return 0;
}

在上述代码中,如果 operator new 无法为 100000000 个整数分配空间,outOfMem 函数将被调用,程序会输出错误信息并中止。

2. 设计良好的 new-handler 应做的事情

一个设计良好的 new-handler 函数必须做到以下事情之一:

  • 释放更多内存:在程序启动时分配一大块内存,在 new-handler 第一次被调用时释放它供程序使用。
  • 安装不同的 new-handler:如果当前 new-handler 无法使更多内存可用,可安装另一个 new-handler
  • 卸载 new-handler:将空指针传给 set_new_handler,内存分配失败时 operator new 抛出异常。
  • 抛出异常:抛出 bad_alloc 或其派生类型的异常。
  • 不再返回:典型情况是调用 abort 或 exit
3. 类特定的 new-handler 实现

C++ 没有直接支持类特定的 new-handler,但可以自己实现。通过为每个类提供 set_new_handler 和 operator new 的自定义版本来实现。

#include <iostream>
#include <new>

// 资源管理类,用于管理 new-handler
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&);
};

// 支持类特定 new-handler 的模板基类
template <typename T>
class NewHandlerSupport {
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;
};

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) {
    NewHandlerHolder h(std::set_new_handler(currentHandler));
    return ::operator new(size);
}

template <typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;

// Widget 类,继承自 NewHandlerSupport
class Widget : public NewHandlerSupport<Widget> {
    // 其他成员...
};

// 自定义 new-handler 函数
void outOfMem() {
    std::cerr << "Widget memory allocation failed\n";
    std::abort();
}

int main() {
    // 设置 Widget 类的 new-handler
    Widget::set_new_handler(outOfMem);
    Widget *pw1 = new Widget;
    // 其他代码...
    return 0;
}

在这个例子中,NewHandlerSupport 是一个模板基类,Widget 类继承自 NewHandlerSupport<Widget>,通过这种方式为 Widget 类提供了类特定的 new-handler

4. 奇特的递归模板模式(CRTP)

NewHandlerSupport 模板使用了奇特的递归模板模式(CRTP)。模板参数 T 仅用于区分不同的继承类,模板机制会为每个实例化的 NewHandlerSupport<T> 生成一个 currentHandler 的拷贝。

5. nothrow new

直到 1993 年,C++ 要求 operator new 分配失败时返回空指针。现在标准规定抛出 bad_alloc 异常,但为了兼容旧代码,提供了 nothrow 形式的 operator new

#include <iostream>
#include <new>

class Widget {
    // 类定义...
};

int main() {
    Widget *pw1 = new Widget; // 分配失败抛出 bad_alloc 异常
    if (pw1 == 0) {
        // 此测试必然失败
    }

    Widget *pw2 = new (std::nothrow) Widget; // 分配失败返回 0
    if (pw2 == 0) {
        // 此测试可能成功
    }
    return 0;
}

需要注意的是,nothrow new 只能保证 operator new 不会抛出异常,但后续的构造函数调用仍可能抛出异常。

总结要点

  • set_new_handler 的使用set_new_handler 允许指定一个在内存分配请求无法满足时调用的函数,通过自定义 new-handler 函数可以处理内存分配失败的情况。
  • 类特定的 new-handler:通过自定义 set_new_handler 和 operator new 以及使用 CRTP 模板基类,可以为每个类提供类特定的 new-handler
  • nothrow new 的局限性nothrow new 仅适用于内存分配,后续的构造函数调用可能依然会抛出异常,其作用有限。

相关文章:

  • 深度学习:从技术突破到未来展望
  • Linux系统 -- 环境安装,xshell和多用户,基本的Linux指令和Linux的用处
  • OpenCV中的边缘检测
  • 从低清到4K的魔法:FlashVideo突破高分辨率视频生成计算瓶颈(港大港中文字节)
  • Tomcat如何处理Http请求
  • 白话概念模型、逻辑模型与物理模型
  • ubuntu 安装 Redis
  • Java和JavaScript当中的json对象和json字符串分别讲解
  • Weather Regimes(WRs)方法介绍
  • 股指期货是什么?股指期货日内拐点有什么特征?
  • 备战蓝桥杯:贪心算法之货仓选址
  • 存储引擎---数据库
  • spring的核心配置
  • 什么是DNS?DNS解析的过程是怎样的?
  • Zookeeper分布式锁实现
  • rust学习笔记1-window安装开发环境
  • 上线了一个微软工具(免费),我独自开发,本篇有源码
  • python类方法名加前缀下划线
  • vue3的响应式的理解,与普通对象的区别(一)
  • 非docker安装open-webui连接ollama实现deepseek本地使用,集成其他openai模型,常见启动报错问题解决。
  • 武汉楼市新政:二孩、三孩家庭购买新房可分别享受6万元、12万元购房补贴
  • 原国家有色金属工业局副局长黄春萼逝世,享年86岁
  • 第二部以“法典”命名的法律!生态环境法典编纂迈出“关键步”
  • 珠海市香洲区原区长刘齐英落马,此前已被终止省人大代表资格
  • 欢迎回家!日本和歌山县4只大熊猫将于6月底送返中国
  • 为博流量编造上海车展谣言,造谣者被公安机关依法行政处罚