new内存分配失败后的new_handler
转载链接: https://blog.csdn.net/Nick_Zhang_CSDN/article/details/99672534
1、new_handler/ set_new_handler/get_new_handler
(1)c++使用new分配内存,当operator new无法满足某一内存分配需求时,它会抛出异常,但c++允许在抛出异常前调用一个自定义函数(new_handler),用来给客户自定义指定“内存分配不足”这一行为的处理方式。
(2)加入我们自定义函数需要调用接口set_new_handler,位于头文件<new>, 接口形参也就是我们的自定义函数为一个函数指针,函数参数和返回值都是void, 接口返回值也是一个函数指针,返回前一个自定义行为 (用新的行为替换老的行为)
(3)get_new_handle 为c++11加入的,返回当前安装好的处理行为。
// 函数原型
typedef void(*new_handler)(); // 自定义处理函数类型
new_handler set_new_handler(new_handler) throw(); // 安装新行为,返回的是老行为
new_handler get_new_handler() noexcept; // 获取当前安装的行为
// eg:
class Base {
public:
Base() {
cout << "Base()" << endl;
}
~Base() {
cout << "~Base()" << endl;
}
void * operator new(size_t p) { // 在构造函数的时候调用,默认是static的。
cout << "operator new" << endl;
return ::operator new(p); // 调用全局的operator new
}
static void operator delete(void *p) { // 在构造函数的时候调用,默认是static的。
cout << "operator delete" << endl;
return ::operator delete(p); // 调用全局的operator delete
}
static void NoMemroy();
};
void Base::NoMemory() {
printf("内存不够呀内存不够\n");
}
int main()
{
auto pppp = set_new_handler(Base::NoMemory); // 可以安装一个类成员函数作为新行为
auto ptr = get_new_handler(); // 获取当前安装的行为
ptr(); // 相当于调用 NoMemory()
auto ptr2 = set_new_handler(OutOfMemory); // 安装新行为OutOfMemory, 返回老行为NoMemory
ptr2();
// auto pb = new Base[333388336633](); // 内存分配不足,调用new_handler自定义处理函数 NoMemory
return 0;
}
2. 一个设计良好的new_handler必须做以下事情:
(参考:effective C++ 条款 49:了解new-handler的行为)
1.让更多内存可使用。造成operator new内的下一次内存分配动作可能成功。一个做法是,程序开始执行就分配一大块内存, 在new-handler第一次被调用,将它们释还给程序使用。
2.安装另一个new-handler。如果这个new-handler无法取得更多可用内存,或许它知道另外有个new-handler有此能力。目前这个就可以安装另外那个以替换自己(只需调用set_new_handler)。下次当operator new调用那个new-handler,调用的将是最新安装的那个。(这个旋律的变奏之一就是让new-handler修改自己的行为,于是当他下次被调用就会做些不同的事。为达此目的,做法之一就是令new-handler修改“会影响new-handler行为”的static数据、namespace数据、或global数据。)
3.卸除new-handler。就是将null指针传给set_new_handler。一旦没有安装任何new_handler,operator new会在内存分配不成功时抛出异常。
4.抛出bad_alloc(或派生自bad_alloc)的异常。这样的异常不会被operator new捕获,因此会传到内存索求处。
5.不反回。通常调用abort或exit。
3、类级别的new_handler行为
之前调用接口set_new_handler来替换新行为的方式是针对全局new范围的,如果想要对每个类调用new分配内存失败后 做不同的行为处理, 需要为每个类都加上自定义处理方式,方法是为每个类重写 operator new 和 set_new_handler
代码示例:
#include<iostream>
#include <new>
using namespace std;
// 在类中重写operator new和set_new_handler行为可以解决类级别的new内存失败处理,
// 如果每个类都要处理,则需要对所有类重写,比较麻烦,
// 可以将重写行为提取出来单独成一个类,通过继承方式来简化重写行为,不用再每个类中都重写。
// 抽取类
// 1、提取重写部分operator new和set_new_handler形成单独工具类
// 2、目标类继承这个工具类(不能包含这个工具类,因为重写动作是在工具类中,必须有继承关系才能实现目标类的重写动作)
class BaseHandler {
public:
// 重写operator new和 set_new_handler
static std::new_handler newHandler_; // 辅助 set_new_handler, 保存处理本类内存分配失败后的新行为
static std::new_handler set_new_handler(std::new_handler newHandler);
static void *operator new(std::size_t size) throw(std::bad_alloc); // operator new默认是static的
static void *operator new[](std::size_t size) throw(std::bad_alloc); // operator new默认是static的
~BaseHandler() {
newHandler_ = nullptr;
}
};
class Base : public BaseHandler {
public:
Base() {
cout << "Base()" << endl;
}
~Base() {
cout << "~Base()" << endl;
}
};
class Base2 : public BaseHandler {
public:
Base2() {
cout << "Base2()" << endl;
}
~Base2() {
cout << "~Base2()" << endl;
}
};
std::new_handler BaseHandler::newHandler_ = nullptr; // 初始化类默认行为
std::new_handler BaseHandler::set_new_handler(std::new_handler newHandler) // 重写set_new_handler
{
std::new_handler newHandler1 = newHandler_;
newHandler_ = newHandler; // 保存新行为
return newHandler1; // 返回老行为
}
void *BaseHandler::operator new(std::size_t size) throw(std::bad_alloc) // 重写operator new
{
cout << "Base::operator new" << endl;
auto ptr = ::set_new_handler(newHandler_); // 设置新的new_handler,返回老的new_handler为ptr
void* pResult = ::operator new(size); // 这里有个弊端,当分配异常的时候,new_handler得不到置位
::set_new_handler(ptr); // 将老的new_handler重新设置回去,所以此处只会影响这种类对象的new_handler
return pResult; // 最终要将new的结果返回
}
void *BaseHandler::operator new[](std::size_t size) throw(std::bad_alloc) // 重写operator new
{
cout << "Base::operator new[]" << endl;
auto ptr = ::set_new_handler(newHandler_);
void* pResult = ::operator new[](size); // 这里有个弊端,当分配异常的时候,new_handler得不到置位
::set_new_handler(ptr);
return pResult; // 最终将new的结果返回
}
void NoMemory()
{
cout << "NoMemory" << endl;
::set_new_handler(nullptr); // 当new_handler为nullptr时,分配内存时候后直接抛出异常bad_alloc
}
void NoMemoryGlobal()
{
cout << "NoMemoryGlobal" << endl;
::set_new_handler(nullptr); // 当new_handler为nullptr时,分配内存时候后直接抛出异常bad_alloc
}
int main()
{
Base2::set_new_handler(NoMemoryGlobal); // 安装NoMemoryGlobal
try {
//Base2 *p = new Base2[33333333333333]();
Base2 *p = new Base2[33333333]();
}
catch (exception &e) {
cout << e.what() << endl;
}
//Base::set_new_handler(NoMemory); // 安装类的行为NoMemory
// 这里Base并没有安装新行为,会使用Base2的行为
try {
//Base *p = new Base[33333333333333]();
Base *p = new Base[333333333]();
}
catch (exception &e) {
cout << e.what() << endl;
}
cout << "endl" << endl;
}

浙公网安备 33010602011771号