关于operator new的异常处理机制

operator new

1.对于new和delete,大家都有一定的了解,他是C++下用来申请内存和释放内存的操作符,而在底层,new是通过调用operator new这个全局函数来实现内存的申请和释放的,而operator new这两个全局函数是系统提供的。
2.底层operator new的实现如下:

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
		{
			// try to allocate size bytes
			void *p;
			while ((p = malloc(size)) == 0)
			if (_callnewh(size) == 0)//其中这个函数的作用是当申请内存不成功的时候,编译器其实会自己在重新申请,这块在下面我们会讲。
			{
				// report no memory
				// 如果再次申请内存失败了,这里会抛出bad_alloc 类型异常
				static const std::bad_alloc nomem;
				_RAISE(nomem);
			}
			return (p);
		}

可以看到,对于operator new的操作,它的底层还是通过malloc来实现的,可是与malloc不同的是,当他申请失败的时候,他会继续在调用一个_callnewh这个函数去继续申请内存,然后如果继续申请内存不成功,那么就bad_allloc抛出异常,这个异常会被捕获后程序就退出了。

new-handler和set_new_handler

1.首先,我们先看以下代码:

int main()
{
	int* ptr = new int[536870911];
	return 0;
}

当我们运行以上代码的时候,程序肯定是会报错的,而如果用malloc去申请空间,就不会报错,因为上述的代码申请的字节数太大,内存不能给予足够的内存,所以会做一些事情,在new中,程序会抛出一个异常bad_alloc这个异常(这个我们会在下面去显示),而在malloc中,会直接给想要分配这些内存的指针赋值为0,然后就直接退出了。
这是new中的操作:

int main()
{
	try
	{
		int* ptr = new int[536870911];
	}
	catch (bad_alloc &e)
	{
		cout << "bad_alloc error." << endl;
	}
	return 0;
}

这个程序运行完后,会打印一个bad_allic error.这一串数据,这是为什么呢,这就与我们要讲的set_new_handler有关了,但是在set_new_handler之前,我们必须了解new_handler机制。
2.new_handler机制:
①:new_handler是什么:
对于new_handler,它其实是一个被定义的指针函数,它的操作与set_new_handler是密不可分的。
②:new_handler的底层定义:

typedef void (* new_handler) ();//这为new_handler的底层定义。

所以由此可以看出来,new_handler其实就是一个被typedef出来的一个函数指针。
3.set_new_handler
①:对于set_new_handler的介绍,我们先看以下代码:

void OutOfMemory()
{
	cout << "bad_alloc error." << endl;
}
int main()
{
	set_new_handler(OutOfMemory);
	int* ptr = new int[536870911];
	return 0;
}

如果运行以上代码,就会出现这样的情况:
在这里插入图片描述
他会对bad_alloc error.进行无休止的打印,所以,对于set_new_handler我们了解到,它是一个当用new去分配内存,如果失败了去调用的一个函数,并且这个函数会被一直调用。
②:set_new_handler的底层定义:

new_handler set_new_handler(new_handler p)throw();

通过底层的定义,我们可以看出来new_handler和set_new_handler为什么息息相关了,对于set_new_handler,它是一个函数,它的参数是一个new_handler函数指针类型,它的返回值也是一个new_handler函数指针类型,并且它后面有throw(),证明这个函数不抛出任何异常。
所以,这个函数的作用是接收一个函数并且返回一个函数。

operator new内存开辟失败异常处理

首先:对于上面讲的还留下了一个问题,为什么在new出现错误的时候,就会一直调用new_handler函数?而set_new_handler到底做了什么?

1.operator new在内存开辟失败做了什么?
首先,在operator new内存开辟失败的时候,他不会像malloc一样给指针一个null然后当没事了,继续走程序,而对于operator new,它在申请内存空间失败后,它会想着去释放一些不必要的其他内存空间,所以在此期间会不断地去调用new_handler函数,然后释放了这些空间之后,再试着去申请内存空间,如果申请成功了,就将这些内存空间返回,如果没有申请成功,那么最后抛出一个bad_alloc异常。

2.所以,对于一个好的new_handler函数,会具有以下优点:

  • 让更多的内存可被使用:就是在一开始申请空间的时候,就是申请一大块空间,当第一次new_handler函数被调用的时候,将这些多余的空间释放回去给程序去使用。这样可以让operator new 的下一次申请空间内存的成功概率会提高。
  • 安装另一个new_handler:程序申请空间内存失败后,去调用new_handler函数,但是程序知道本来的new_handler函数是无法完成任务的,但是新的new_handler可以完成任务,它就可去安装新的new_handler函数去替换原来的new_hanlder函数,这个操作可以通过set_new_hander去完成。(这样当operator new的下一次申请内存空间失败后,就会调用的是新的new_handler函数)
  • 卸载new_handler:将null分配给set_new_handler的参数,一旦没有安装任何new_hander,operator new 会在内存分配不成功的时候会直接抛出异常。
  • 抛出bad_alloc异常:这样的异常不糊被operator new捕捉,而是被系统里面的内存检索捕捉。
  • 不返回:调用abort()或者exit()。

3.对于new_handler函数的操作,需要set_new_handler的不断帮助,set_new_handler会帮助其安装新的new_handler,而set_new_handler的底层实现是这样的:

new_handler set_new_handler(new_handler p)threow()
{
   new_handler oldHandler = currentHandler;
   currentHandler = p;
   return oldHandler;
}

一级空间配置器的模拟实现

class new_alloc
{
public:
	void* operator new(size_t n)
	{
		void* ptr = malloc(n);
		if (ptr == NULL)
		{
			ptr = alloc(n);
		}
		return ptr;
	}
	static new_handler set_new_handler(new_handler p) //去更改new_handler函数的。
	{
		new_handler oldhandler = currentHandler;
		currentHandler = p;
		return oldhandler;
	}
private:
	static void* alloc(size_t n);//当申请内存失败了,就要用这个函数。
	static new_handler currentHandler;
};
new_handler new_alloc::currentHandler = 0;//在类外声明
void* new_alloc::alloc(size_t n)
{
	new_handler p;//首先定义一个函数指针
	void* ptr;
	for (;;)
	{                
		p = currentHandler;//让他指向我们定义的这个可以解决问题的函数
		if (p == 0)//如果最后问题没有解决,那么就只能退出了
		{
			throw bad_alloc();
		}
		(*p)();//使用这个函数去释放那些不需要的内存。
		ptr = malloc(n);//然后重新申请
		if (ptr)//如果申请成功,那么就返回指针。
			return ptr;
	}
}

用法:由于我们在使用这个一级空间配置器的时候,我们如果设置了出错函数了,那么我们会在申请空间的上面写出来set_new_handler,其中参数为我们设置的内存处理函数,如果没有设置,那么就默认为0去处理,默认为0处理的结果就是,如果申请内存失败,那么就会报错,并终止程序的运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值