天天看點

Effective C++ 條款八 了解各種不同意義的new與delete

我們常見的 new運算符

用起來大概是這樣的:

Class A{

    A(){......;}

};

A* ptr = new A;

上面這種new其實可以分解為兩步:

1.配置設定記憶體

2.在該段記憶體上執行A的構造函數

如下所示:

void * memory = operator new(sizeof(string));        //配置設定記憶體

call string:: string("Memory Management") on *memory;    //在配置設定好的記憶體上調用構造函數

string* ps = static_cast<string*>(memory);        //将其由void強制轉換為想要的類型

C++新标準中,我們雖然不能夠改變new,但是可以改變這個“operator new”,即如何配置設定記憶體:比如在剛剛配置設定記憶體後還沒調用構造函數的時候就給這片記憶體一個初值之類的操作。

我們就可以這樣調用

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

同時,一旦我們重載了operator new,那麼我們在進行動态變量建立(即使用new運算符進行變量建立時),在配置設定記憶體階段,編譯器會調用我們所重載的那個記憶體配置設定函數,而不是原始的那種。。。

另一個需要注意的時Placement new,即在一塊已知的記憶體上去調用構造函數建構對象,而不是使用傳統的new方法先配置設定記憶體(此時記憶體的位址時系統配置設定的,我們不造)再調用構造函數。

用Placement new的好處是,如果我們的對象被要求必須位于特定的位址,或者是置于以特殊函數配置設定出來的記憶體上的話,我們必須先手工配置設定一塊我們期望的記憶體,然後調用Placement new在這塊記憶體上建構對象

用法如下:

Class Widget{

public:

    Widget(int widgetSize);

};

Widget * constructWidgetInBuffer(void* buffer, int widgetSize)

{

    return new (buffer) Widget(widgetSize);        

}

總結:

1.如果你希望在堆上建立對象,最好使用new operator(即原生的new),它不但配置設定記憶體而且為該對象調用一個constructor;

  此種情況下,若要進行收尾工作,則可直接調用 delete 操作符(我們常見的delete),它首先會析構掉對象,然後釋放掉記憶體

2.如果你隻打算配置設定一塊記憶體,那麼最好調用operator new,它會傳回一個指向所配置設定記憶體區域的指針。

  此種情況下,若要進行收尾工作,則需要調用operator delete,這樣會收回由operator new配置設定的記憶體,而不會調任何類的析構函數

3.如果你打算在堆上建立記憶體時自己決定記憶體配置設定方式,那麼你需最好寫一個自己的operator new版本,并使用new operator(即我們常說的new),它會自動調用你所寫的operator new來配置設定記憶體。

  此種情況下,若要進行收尾工作,則直接調用原生的delete

4.如果你打算在已配置設定的一段記憶體裡建構對象,那麼就是用placement new,将指向該段記憶體的指針以void*的類型,以new (ptr) A();的形式建構對象,這将會在ptr所指向的記憶體區域調用A();

  此種情況下,若要進行收尾工作,則隻需要調用析構函數就好,不能調用任何釋放記憶體的操作數(因為這裡ptr所指向的記憶體不歸收尾工作管,那片記憶體在placement new之前就已經存在了)

總結:

在動态配置設定數組的情況下會稍微複雜些。對應的操作符為operator new[] 與 operator delete[],這裡不詳細展開。

總之,new operator 和 delete operator都是内建操作符,無法為你所控制,但是它們所調用的記憶體配置設定/釋放函數則不然。當你向要定制new operator和delete operator的行為,記住,你其實無法真正辦到。

你可以修改它們完成任務的方式(如指定初始記憶體的值),至于它們的任務,已經被C++語言規範固定死了。