`
java-mans
  • 浏览: 11344125 次
文章分类
社区版块
存档分类
最新评论

《认清C++语言》---new and delete

 
阅读更多

1) new操作符(new operator)和operator new函数的区别:

下面的代码:

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

使用的newnew操作符,这个操作符就像sizeof一样是内置于语言本身的。它所完成的功能分成两部分:首先分配足够的内存以便容纳所请求类型的对象(如上面的string对象);其次它会调用构造函数初始化刚刚分配的内存中的对象。new操作符总是做这两件事,我们不能以任何方式改变它的行为。

new操作符函数调用的那个用来完成必需的内存分配的函数就是operator new函数,我们可以重写或重载的就是这个函数。

函数operator new函数通常这样声明:

void *operator new(size_t size);

返回值的类型是void*,因为这个函数返回一个指针,这个指针指向原生的、未经初始化的一块内存;参数size指定要分配的内存的大小(字节为单位)。我们可以通过增加额外的参数重载operator new函数,但第一个参数的类型必须是size_t

operator new函数的调用方式和普通函数一样:

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

就像mallocoperator new的唯一职责就是分配内存。把operator new函数返回的未经处理的指针转换成一个对象时new 操作符的工作,当编译器遇到下面的语句:

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);

上面第二步包含了构造函数的调用,程序员是不允许这样直接调用构造函数的,但编译器不受这个限制。

2) 有时我们确实想直接调用构造函数(尽管这是行不通的)。

在一个已存在的对象上调用构造函数是没有意义的,因为构造函数是用来初始化对象的,而一个对象只有在给它赋初值的时候初始化一次。

有时有些内存已经被分配但是尚未初始化,而我们需要在这块内存中构造一个对象,此时我们可以使用operator new函数的一个标准的重载版本:placement new函数。placement new允许我们在一个特定的位置“放置”对象,起到了调用一个构造函数的效果。

class ACEWidget

{

public :

ACEWidget(int widgetSize);

....

};

ACEWidget *constructWidgetInBuffer(void *buffer, int widgetSize)

{

return new (buffer) ACEWidget(widgetSize);

}

上面代码中的new操作符需要一个额外的变量buffer,当new操作符隐式调用operator new函数时,把这个变量传给它。被调用的operator new函数除了带有强制的参数size_t外,还必须接受void*指针参数,指向构造对象占用的内存空间,此时,这个operator new就是placement new

void *operator new(size_t, void *location)

{

return location;

}

placement new的情况下,调用者很清楚指针location是什么类型的指针,因为他知道对象应该放在哪里。placement new必须做的就是返回传递给它的指针。size_t参数在这里没有用到,因为它是强制性的参数,所以没有名字,以防止编译器发出警告说它没有被使用。(也就是说,placement new的实现忽略了表示大小的实参,直接返回其第二个参数。)

placement new是标准C++库的一部分,为了使用placement new,需要包含#include <new>

总结:如果想在堆上建立一个对象,那就使用new操作符,它既分配内存又为对象调用构造函数;如果仅仅想分配内存,那就调用operator new函数,它不会调用构造函数;如果想定制在堆对象被建立时的内存分配过程,就写一个自己的operator new函数,然后使用new操作符,new操作符会调用定制的operator new函数;如果想在一块已经有指针指向的内存里建立一个对象,就是用placement new

注意:placement new函数不允许重写。

3) 函数operator deletedelete操作符的关系与operator new函数和new操作符的关系一样。

string *ps;

//.......

delete ps;

中的delete ps,编译器的理解差不多是:

ps->~string();

operator delete(ps);

因此,如果只想处理原始的、未被初始化的内存,可以调用operator new函数获得内存和operator delete函数释放内存:

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

//.........

operator delete(buffer);

而这与C中调用mallocfree等同。

如果使用placement new函数在内存中创建对象,应该避免针对这块内存使用delete操作符。因为delete操作符隐式调用operator delete函数来释放内存,但是包含对象的内存最初不是operator new函数分配的,placement只是返回传递给它的指针。此时应该显式调用对象的析构函数来销毁构造函数所建立的对象。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics