您的位置:首页 > 编程语言 > C语言/C++

C++中的new和delete

2012-09-02 23:08 141 查看
扒一扒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正是new operator。这个操作符是由语言内建的,就像sizeof那样,不能被改变意义,总是做相同的事情。它的动作分为两方面。第一,它分配足够的内存,用来放置某类型的对象。以上例而言,它分配足够放置一个string对象的内存。第二,它调用constructor,为刚才分配的内存中的那个对象设定初值。new operator总是做这两件事。

1.2 operator new

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

operator new 通常声明如下:

void * operator new (size_t size);

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

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

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

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

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

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

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

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

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

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

1.3 placement new

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

下面示范如何使用placement new:

class Widget{

public:

Widget (int widgetSize)

......

};

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

{

return new(buffer) Widget(widgetSize);

}

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

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

new (buffer)Widget(widgetSize);

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

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

2区分各种delete

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

string *ps;

...

delete ps;

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

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

void operator delete(void *memoryToBeDeallocated);

因此,下面这个动作:

会造成编译器产生近似这样的代码:

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

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

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

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

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

....

operator delete(buffer);

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

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

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

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

void * mallocShared(size_t size);

void freeShared(void *memory);

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

Widget *pw = constructWidgetInBuffer(shareMemory,10);

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

delete pw;

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

pw->Widge();

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

freeShared(pw);

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

//不调用任何destructor.

如此例所示,如果交给placement new 的原始内存(raw memory)本身是动态分配而得(通过某种非传统做法),那么你最终还是得释放那块内存,以免遭受内存泄漏之苦。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: