C++中各种不同的new和delete

    前言:众所周知,C++中的操作符new和delete是用来动态分配内存初始化对象和手动回收内存析构对象的。但这只是最基本最常用的用法。new和delete其实还有更内涵的东西。

1 区分各种不同的new

    new操作符其实分为三种:new operator ,operator new ,以及placement new。

 

1.1 new operator

    当我们写出这样的代码:

string*ps = new string("Memory Management");

     这时所用的new正是newoperator。这个操作符是由语言内建的,就像sizeof那样,不能被改变意义,总是做相同的事情。它的动作分为两方面。第一,它分配足够的内存,用来放置某类型的对象。以上例而言,它分配足够放置一个string对象的内存。第二,它调用constructor,为刚才分配的内存中的那个对象设定初值。newoperator总是做这两件事。

 

1.2 operator new

    呵呵,C++的述语好像是故意让人难以理解似的。没看错,就是operatornew.

    operator new 通常声明如下:

    void * operatornew (size_t size);

    其返回值类型是void*.此函数返回一个指针,指向一块原始的,未设初值的内存。size表示分配多少内存。你可以将operatornew重载,加上额外的参数,但第一参数的类型必须总是size_t;或许你从未想到要直接调用operatornew,但如果你要,你可以像调用任何其他函数一样调用它:

    void* rawMemory= operator new(sizeof(string));

    这里的operatornew 将返回指针,指向一块足够容纳一个string对象的内存。

和malloc一样,operatornew的唯一任务就是分配内存。它不知道什么是constructor,operator new 只负责内存分配。取得operatornew 返回的内存并将之转换为一个对象,是new operator的责任。当你的编译器看到这样一个句子:

    string *ps = newstring("Memory Management");

    它必须产生一些代码,或多或少会反映以下行为:

    void *memory =operator new(sizeof(string));

   callstring::string("Memory Management") on *memory ;

   string *ps =static_cast<string*>(memory);

也就是说,newoperator 其实先是调用operator new 来分配内存的,接着才是它自己要完成的初始化操作。

 

1.3 placement new

     有时候你真的会想直接调用一个constructor。针对一个已存在的对象调用其constructor并无意义,因为constructor用来将对象初始化,而对象只能被初始化一次。但是偶尔你会有一些分配好的原始内存,你需要在上面构建对象。有一个特殊版本的operatornew,称为placement new,允许你这么做。

    下面示范如何使用placementnew:

class Widget{

public:

Widget(int widgetSize)

......

};

Widget *constructWidgetInBuffer(void *buffer,int widgetSize);

{

returnnew(buffer) Widget(widgetSize);

}

     此函数返回指针,指向一个Widgetobject,它被构造于传递给此函数的一块内存缓冲区上。当程序运行到sharedmemory或memory-mapped I/O,这类函数可能是有用的,因为在那样的运用中,对象必须置于特定地址,或是置于以特殊函数分配出来的内存上。

     在constructorWidgetInBuffer函数内部,唯一一个表达式是:

     new(buffer)Widget(widgetSize);

    乍见之下有点奇怪,其实不足为奇,这只是newoperator 的用法之一,其中指定一个额外自变量(buffer)作为newoperator "隐式调用operator new "时所用。于是,被调用的operatornew除了接受一个"一定得有的size_t自变量"之外,还接受一个void*参数,指向一块内存,准备用来接受构造好的对象。这样的operatornew就是所谓的placement new。

      总之:如果你希望将对象产生于heap,请使用newoperator。它不但分配内存而且为该对象调用一个constructor。如果你只是打算分配内存,请调用operatornew ,那就没有任何constructor会被调用。如果你打算在heap objects产生时自己决定内存分配方式,请写一个自己的operatornew ,并且使用new operator,它将会自动调用你所写的operatornew。如果你打算在已分配的(并拥有指针)的内存中构造对象,请使用placement new。

 

2区分各种delete

     为了避免资源泄漏,每一个动态分配行为都必须匹配一个相应但相反的释放动作。操作符operatordelete对于内建的delete operator,就好像operatornew 对于new operator一样。当你写出这样的代码:

string *ps;

...

delete ps;

 

     你的编译器必须产生怎么样的代码?它必须既能够析构出ps所指对象,又能够释放被该对象占用的内存。

    内存释放动作是由operatordelete执行,通常声明如下:

    void operatordelete(void *memoryToBeDeallocated);

    因此,下面这个动作,会造成编译器产生近似这样的代码:

    ps->~string();                           //调用对象的dtoroperator

    operatordelete(ps);                  //释放对象占用的内存。

    这里呈现一个暗示就是,如果你只打算处理原始版的,未设初值的内存,应该完全回避newoperator和delete operator,改调用operatornew 取得内存并以operator delete 归还给系统:

 

    void * buffer =operator new (50*sizeof(char));       

    //分配足够的内存,放置50  个chars;没有调用任何ctors 

    ....

    operatordelete(buffer);    

    //释放内存,没有调用任何dtors

    这组行为相当于C中的malloc和free。

     如果你使用了placementnew ,在某内存块中产生对象,你应该避免对那块内存使用delete operator。因为deleteoperator会调用operator delete 来释放内存,但是该内存内含的对象最初并非是由operatornew分配来的。毕竟placement new只管返回它所接收的指针而已,谁知道那个指针从哪里来呢?所以为了抵消该对象的constructor的影响,你应该直接调用该对象的destructor:

     //以下函数用来分配及释放sharedmemeory 中的内存。

void *mallocShared(size_t size);

voidfreeShared(void *memory);

void*shareMemory = mallocShared(sizeof(Widget));

Widget *pw =constructWidgetInBuffer(shareMemory,10);

    //和先前相同,这里运用placementnew。

   delete pw;

   //无定义!因为shareMemory来自mallocShared,不是来自operatornew.

    pw->Widge();

    //可!析构ps所指的Widget对象,但并未释放Widget占用的内存。

    freeShared(pw);

    //可!释放pw所指的内存

    //不调用任何destructor.

    如此例所示,如果交给placementnew 的原始内存(raw memory)本身是动态分配而得(通过某种非传统做法),那么你最终还是得释放那块内存,以免遭受内存泄漏之苦。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值