C++之写了placement new也要写placement delete(52)---《Effective C++》

5 篇文章 0 订阅
5 篇文章 0 订阅

条款52:写了placement new也要写palcement delete

问题:

Widget* pw=new Widget;

这条语句的执行导致两个函数被使用:一个是用以分配内存的operator new,另一个是Widget的default构造函数。假设其中第一个函数调用成功,第二个函数抛出异常。这样第一步中operator new的内存分配所得必须取消并恢复旧观,否则会造成内存泄漏,这个时候,客户没有能力归还内存,因为如果Widget构造函数抛出异常,pw当前尚未被赋值,客户手上就没有相应的指针执行指向该被归还的内存,取消第一步并恢复旧观的重任就变成了C++运行期系统的重任。

运行期系统会调用第一步中operator new的相应operator delete版本,前提是它必须知道哪一个operator delete被调用:

//正常的operator new
void * operator new(std::size_t) throw(std::bad_alloc);
//正常的operator delete
//1、global作用域中的正常签名式
void operator delete(void* rawMemory) throw();
//2、class作用域中典型的签名式
void operator delete(void* rawMemory,std::size_t size) throw();

然而当我们声明一个非正常形式的operator new,也就是带有附加参数的operator new时,那么该调用那个operator delete呢?

class Widget{
public:
    ...
    static void* operator new(std::size_t,std::ostream& logStream) throw(std::bad_alloc);//非正常形式new
static void operator delete(void* pMemory std::size_t size) throw()//正常的class专属delete
    ...
};

1、placement new/delete的提出

如果operator new接受的参数除了一定会有的那个size_t之外还有其他,这便是所谓的placement new,众多的placement new版本中有一个“接受一个指针指向对象该被构造之处”,这种placement new已经被纳入C++标准程序库,调用#include <new>就可以取用它。这种operator new的样子如下所示:

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

上面我们针对Widget已经声明了一种placement new版本,如何使用它呢?下面举一个例子便于大家理解:

Widget* pw=new (std::cerr)Widget;//调用operator new并传递cerr作为其ostream实参,这个动作会在Widget构造函数抛出异常时泄漏内存

那么此时如果抛出异常的话怎样取消operator new的分配并恢复旧观,由于运行期间系统无法知道真正被调用的那个operator new如何运作,因此它无法取消分配并恢复旧观,取而代之得失C++运行期系统寻找“参数个数和类型都与operator new相同的”operator delete。如果找到,就是它的调用对象。那么Widget的这种形式的operator new对应的operator delete版本应该是:

void operator delete(void*,std::ostream&) throw();

这也就引出了placement delete的定义,如果operator delete如果接受额外参数,便称为placement delete

下面我们写一个class专属的operator new,要求接受一个ostream,用来志记相关分配信息,同时又洗了一个正常形式的class专属operator delete:

class Widget{
public:
    ...
    static void* operator new(std::size_t size,std::ostream& logStream) throw(std::bad_alloc);
    static void operator delete(void* pMemory) throw();
    static void operator delete(void* pMemory,std::ostream& logStream) throw();
    ...
};

2、placement new的继承

class Base{
public:
    ...
    static void* operator new(std::size_t size,std::ostream& logStream) throw(std::bad_alloc);//这个new会遮掩正常的global形式
    ...
};
Base* pb=new Base;//错误!因为正常形式的operator new被遮掩
Base* pb=new (std::cerr)Base;//正确,调用Base的placement new
class Derived:public Base{
public: 
    ...
    static void* operator new(std::size_t size);
    ...
};
Derived* pd=new (std::clog)Derived;//错误,因为Base中的placement new被遮掩了
Derived* pd=new Derived;//正确

要解决遮掩问题的话,我们需要家里一个Base class,内含所有正常形式的new和delete:

class StandardNewDeleteForms{
public: 
    static void* operator new(std::size_t size) throw(std::bad_alloc){
        return ::operator new(size);
    }
    static void opearator delete(void* pMemory)throw(){
        ::operator delete(pMemory);
    //placement new/delete
    static void* operator new(std::size_t size,void*ptr)throw(){
        return ::operator new(size,ptr);
    }
    static void operator delete(void* pMemory,void* ptr)throw(){
        return ::operator delete(pMemory,ptr);
    }
    static void* operator new(std::size_t size,const std::nothrow_t& nt) throw(){
        return ::operator new(size,nt);
    }
    static void operator delete(void* pMemory,const std::nothrow_t&nt) throw(){
        ::operator delete(pMemory);
    }
};  
class Widget::public StandardNewDeleteForms{
public:
    using StandardNewDeleteForms::operator new;
    using StandardNewDeleteForms::operator delete;
    static void* operator new(std::size_t size,std::ostream& logStream)throw(std::bad_alloc);
    static void operator delete(void* pMemory,std::ostream& logStream)throw();
    ...
};

PS:
C++中operator new/delete或者operator new[]/delete[]的解析方式:http://blog.csdn.net/hazir/article/details/21413833
总结:
1)当你写了一个placement operator new,请确定也谢了对应的placement operator delete,如果没有这样做,程序可能会发生隐蔽而时断时续的内存泄漏;
2)当你声明了placement new和placement delete,请确定不要无意识地遮掩了它们的正常版本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值