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

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

1. new-handler 基础概念

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

cpp 复制代码
#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 或其派生类型的异常。
  • 不再返回 :典型情况是调用 abortexit
3. 类特定的 new-handler 实现

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

cpp 复制代码
#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

cpp 复制代码
#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_handleroperator new 以及使用 CRTP 模板基类,可以为每个类提供类特定的 new-handler
  • nothrow new 的局限性nothrow new 仅适用于内存分配,后续的构造函数调用可能依然会抛出异常,其作用有限。
相关推荐
不枯石1 小时前
Matlab通过GUI实现点云的均值滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab·均值算法
不枯石1 小时前
Matlab通过GUI实现点云的双边(Bilateral)滤波(附最简版)
开发语言·图像处理·算法·计算机视觉·matlab
白水先森3 小时前
C语言作用域与数组详解
java·数据结构·算法
想唱rap3 小时前
直接选择排序、堆排序、冒泡排序
c语言·数据结构·笔记·算法·新浪微博
老葱头蒸鸡4 小时前
(27)APS.NET Core8.0 堆栈原理通俗理解
算法
视睿5 小时前
【C++练习】06.输出100以内的所有素数
开发语言·c++·算法·机器人·无人机
保持低旋律节奏5 小时前
CPP——OJ试题,string、vector、类(题三)初步应用
c++
君生我老5 小时前
C++ string类常用操作
c++
柠檬07115 小时前
matlab cell 数据转换及记录
算法
YuTaoShao6 小时前
【LeetCode 每日一题】2221. 数组的三角和
数据结构·算法·leetcode