operator new之placement new/delete

29 篇文章 1 订阅

如果operator new接受的参数除了一定会有的那个std::size_t 之外还有其他,这便是所谓的placement new

 

众多placement new版本中特别有用的一个是“接受一个指针指向对象该被构造之处”,如下所示:

void* operator new(std::size_t size, void* pMemory) throw();

 

此版本的operator new已经在#include <new>中定义,功能之一就是:在vector的未使用空间上创建对象。这也正是placement new的命名规则:在一个特定位置上进行new

 

有一点很重要的:当class中定义了placement new时,且使用了placement new进行了new class对象,你如果没有定义对应的placement new的话,当palcement new进行堆上创建class 对象时,构造函数抛出异常,此时!如果你没有定义placement delete,则会出现内存泄露!====>>>>>placement new为存放对象而申请的堆空间将由于没有placement delete可调用,将会出现内存泄漏!

placement new operator new的调用时机区别:

1)用placement new进行类的堆上对象创建时,当new成功申请到存放对象的堆内存后,在调用构造函数建立类对象来初始化堆内存时,如果构造函数出现错误抛出异常时,调用和placement new对应的placement delete,就是placement delete没有定义也不会调用operator new

2)delete pobj;时只会调用operator new,不会调用placement delete

所以没有定义placement delete导致的内存泄漏只会出现在:用placement new创建类的堆上对象过程中调用构造函数错误抛出异常时。

以下就是出现以上定义了placement new同时又定义了对应的placement delete,在创建堆上对象时不会出现内存泄漏的参照类测试代码。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <string>
#include <iostream>

#define PRINT_LINE_INFO \
	do {\
		printf("%s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__); \
	}while(0);

class CDog
{
public:
	CDog()
	{
		PRINT_LINE_INFO;
	}
	~CDog()
	{
		PRINT_LINE_INFO;
	}
	
	//< placement new
	//< CDog *pdog = new(std::cerr) CDog();将会调用此placement new
	void* operator new(std::size_t size, std::ostream& logstream) throw(std::bad_alloc)
	{
		printf("%s:%d===>placement new!\n", __FILE__, __LINE__);
		std::size_t len = size == 0 ? 1 :size;
		void* buf = malloc(len);
		if (buf == NULL)
		{
			throw std::bad_alloc();
		}
		
		return buf;
	}
	
	//< placement delete
	//< 只有!!!在调用placement new创建堆上对象时调用构造函数错误抛出异常时才会调用此placement delete
	void operator delete(void* buf, std::ostream& logstream) throw()
	{
		printf("%s:%d===>placement delete!\n", __FILE__, __LINE__);
		if (buf != NULL)
		{
			free(buf);
		}

		return;
	}
	
	//< operator new
	//< CDog *pdog = new CDog();会调用此operator new
	void* operator new(std::size_t size) throw(std::bad_alloc)
	{
		printf("%s:%d===>operator new!\n", __FILE__, __LINE__);
		std::size_t len = size == 0 ? 1 : size;
		void* buf = malloc(len);
		if (buf == NULL)
		{
			throw std::bad_alloc();
		}
		
		return buf;
	}
	
	//< operator delete
	//< 任何的delete ptr;都会调用此operator delete
	//< 注意和placement delete的调用时机区别
	void operator delete(void* buf) throw()
	{
		printf("%s:%d===>operator delete!\n", __FILE__, __LINE__);
		if (buf != NULL)
		{
			free(buf);
		}
		
		return;
	}
	
};

int main()
{
	printf("***************CDog *dog = new(std::cerr) CDog();************************\n");
	CDog *dog = new(std::cerr) CDog();	//由于多了一个入参std::cerr, 将会调用placement new
	delete dog;//调用正常的operator delete

	printf("\n*************CDog *dog = new CDog();***********************************\n");
	CDog *bdog = new CDog();	//调用正常的operator new
	delete bdog;//调用正常的operator delete
	
	return 0;
}

测试结果如下所示:



本文章的核心内容:

1)什么是placement new/delete

2)placement delet只有在“伴随placement new调用而触发的构造函数”出现异常时才会被调用。

3)对着一个指针ptr实行delete,绝对!!!不会调用placement delete,而是调用的时operator delete。

4)为了杜绝由于使用placement new导致出现内存泄漏,必须提供一个相对应的placement delete用于构造函数抛出异常,且placement delete的额外参数必须和    placement new的额外参数一样!!!!

5)为了杜绝由于使用placement new导致出现内存泄漏,也必须提供一个正常的operator delete用于构造函数无异常时。

如果遵守了4)和5)就不会再使用placement new创建类的堆上对象时而担心内存泄漏,可以保证构造函数是否抛出异常都能够有delete函数被调用来释放已经申请的存放类对象的堆内存buf。


(完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值