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;
}

  

  

  

  

 

posted @ 2021-03-28 16:05  min_zhi  阅读(150)  评论(0编辑  收藏  举报