Effective C++ 条款49、50

条款49 了解new-handler的行为

1、当operator new抛出异常以反映一个未获满足的内存需求之前邮寄费会先调用一个客户指定的错误处理函数,一个所谓的new-handler。为了指定这个“用以处理内存足”的函数,客户必须调用set_new_handler。关于<new>的一个标准程序库函数:

namespace std {
	typedef void(*new_handler)();
	new_handler set_new_handler(new_handler p)throw();
}

set_new_handler的用法:set_new_handler的参数是个指针,指向operator new无法分配足够内存时该被调用的函数。其返回值也是个指针,指向set_new_handler被调用前正执行(但马上就要替换)的那个new_handler函数。

set_new_handler用法举例:

//以下是operator new无法分配足够内存时,该被调用的函数
void outOfMen() {
	std::current_exception << "Unable to satisfy request for memory\n";
	std::abort();
}
int main() {
	std::set_new_handle(outOfMem);
	int* pBigDataArray = new int[1000000000L];
}

当operator new无法满足内存申请时,它会不断调用new-handler函数,直至找到足够内存。

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

i、让更多内存可被使用

ii、安装另一个new-handler

iii、卸除new-handler

iv、抛出bad_alloc,也就是将null指针传给set_new_handler

v、不返回,通常调用abort或exit

上述事情,一眼看去是懵B的,每隔三五天回过头看也是懵B,我也不知道为什么。

下面是一个Widget class内存分配失败情况的举例:

首先登陆“当operator new无法为一个Widget对象分配足够内存时”调用的函数,所以需要声明一个类型为new_handler的static成员,用以指向class Widget的new-handler。

class Widget {
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;
};

//static成员必须在class定义式之外被定义,见下
std::new_handler Widget::currenthandler = 0;//在class实现文件内初始化为null
Widget内的set_new_handler函数会将它获得的指针存储进来,然后返回先前(在此调用之前)存储的指针,即标准版set_new_handler所作的:
std::new_handler Widget::set_new_handler(std::new_handler p)throw() {
	std::new_handler oldHandler = currenthandler;
	currenthandler = p;//保存获取指向函数的指针
	return oldHandler;//返回之前存储的指针
}

最后,Widget的operator new做以下事情:

i、调用标准set_new_handler,告知Widget的错误处理函数。这会将Widget的new_handler安装为global new-handler

ii、调用global operator new,执行内存分配。

iii、如果global operator new能够分配足够一个Widget对象所用的内存,Widget的operator new会返回一个指针,指向分配所得。

2、从资源处理类(RAII)角度再次阐述:

class NewHandlerHolder {
public:
	explicit NewHandlerHolder(std::new_handler nh)://取得目前new-handler
		handler(nh){}
	NewHandlerHolder() {      //释放它
		std::set_new_handler(handler);
	}
private:
	std::new_handler handler;//记录下来
	NewHandlerHolder(const NewHandlerHolder&);//阻止copying
	NewHandlerHolder& operator=(const NewHandlerHolder&);
};

//对类Widget内的new操作符进行定义
void* Widget::operator new(std::size_t size) throw(std::bad_alloc) {
	NewHandlerHolder h(std::set_new_handler(currenthandler));//安装Widget的new-handler.
	return ::operator new(size);                             //分配内存或抛出异常。恢复global new-handler
}
于是Wiget的客户可以如下使用其new-handling:
void outOfMem();//函数声明。此函数在Widget对象分配失败时被调用

Widget::set_new_handler(outOfMem);//设定outOfMem为Widget的new-handling函数

Widget* pw1 = new Widget;//如果内存分配失败,调用outOfMem

std::string* ps = new std::string;//如果内存分配失败,调用global new-handling函数

Widget::set_new_handler(0);//设定Widget专属的new-handling函数为null  0表明关闭outOfMem函数的调用

Widget* pw2 = new Widget;//如果内存分配失败,立刻抛出异常。

3、Nothrow new是一个颇为局限的工具,因为它只适合内存分配;后继的构造函数调用还是可能抛出异常

用法举例:

Widget* pw2 = new (std::nothrow) Widget;
上述语句能抛出异常可从两部分入手分析,i、一个new过程可能发生异常;ii、一个Widget构造函数,可能发生异常。而std::nothrow只能保证i过程发生异常返回NULL,而ii过程不能保证。

条款50 了解new和delete的合理替换时机

替换编译器提供的operator new或operator delete的常见三个理由:

i、用来检测动用上的错误;(内存泄漏不确定行为)

ii、为了强化效能;(内存碎片,无法满足大区块内存要求,但却有足够但散为许多小区块的自由内存)

iii、为了收集使用上的统计数据。

有许多理由需要写个自定义的new和delete,包括改善效能、对heap运用错误进行调试、收集heap使用信息。

以上内容均来自Scott Meyers大师所著Effective C++ version3,如有错误地方,欢迎指正!相互学习,促进!!

猜你喜欢

转载自blog.csdn.net/tt_love9527/article/details/81057658