《Effective C++》读书笔记 条款49:了解new-handler的行为

当operator new无法满足某一内存分配需求时,它就会抛出异常,但在抛出异常前,它会先调用一个客户指定的错误处理函数,一个所谓的new-handler。为了指定这个"用以处理内存不足"的函数,客户必须调用set_new_handler,那是声明与<new>的一个标准库函数:

namespace std{

    typedef void (*new_handler)();
    new_handler set_new_handler(new_handler p)throw();
}

new_handler是一个指向函数的指针,该函数没有返回值和参数。set_new_handler是一个获得一个new_handler并返回一个new_handler的函数,其参数是一个指针,指向operator new无法分配足够内存时该被调用的函数,其返回值指向set_new_handler 被调用前正在执行(马上要被替换 )的那个new_handler函数。可以这样使用

void outOfMem()
{
	std::cerr << "Unable to satisfy request for memory\n" << std::endl;
	std::abort();
}
int main()
{
	std::set_new_handler(outOfMem);
	int* pBigDataArry = new int[100000000L];
	
	return 0;
}

如果上述无法为100000000个数整数分配足够的空间,outOfMem就会被调用。

一个设计良好的new_handler必须

1.让更多的内存可被使用

2.安装另一个new_handler

3.卸除new_handler 就是将null指针传给set_new_handler。一旦没有安装任何new_handler,operator new会在内存分配不成功时抛出异常

4.抛出bad_alloc(或派生自bad_alloc)异常

5.不返回,通常调用abort或exit

 

C++不支持class专属之new_handler。我们可以自己实现这种行为。假设处理Widget class内存分配失败的情况

class Widget{
public:
	static std::new_handler set_new_handler(std::new_handler p) throw();//throw()表示该函数不会抛出任何异常
	static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
	static std::new_handler currentHandler;

};
std::new_handler Widget::currentHandler = 0;
//将获取的new_handler储存起来,返回先前的
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
	std::new_handler oldHandler = currentHandler;
	currentHandler = p;
	return oldHandler;
}

为了防止资源泄露,运用资源管理对象 ,这样会自动调用析构函数,以及将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&);
};

 

Widget在内存分配时的操作如下:

void* Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
	NewHandlerHolder h(std::set_new_handler(currentHandler));
	return ::new_handler(size);
}

 可以这样使用:

void outOfMem();

int main()
{
	Widget::set_new_handler(outOfMem);
	Widget* p1 = new Widget;
	return 0;
}

为了能加以复用,可以写成模板的形式

template<typename T>
class NewHandlerSupport{
public:
	static std::new_handler set_new_handler(std::new_handler p) throw();//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>::currentHandler = 0;
//将获取的new_handler储存起来,返回先前的
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 ::new_handler(size);
}

 然后让你希望有自己专属之new-handler的类继承自该模板对类的具现化,如:

class Widget: public NewHandlerSupport<Widget>

请记住

1.set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用

2.Nothrow new 是一个颇为局限的工具,因为它只适用于内存分配;后继的构造函数调用还是可能抛出异常。(具体内容参照书本)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值