new operator,這個操作符是由語言內建的,不能被改變意義,總是做相同的事情。它的動作分為兩方面:
第一,它分配足夠的記憶體,用來放置某型別的物件。
第二,它呼叫乙個constructor,為剛才分配的記憶體中的那個物件設定初值。
你能夠改變的是用來容納物件的那塊記憶體的分配行為。
new operator呼叫某個函式,執行必要的記憶體分配動作,你可以重寫或過載那個函式,改變其行為。這個函式的名稱叫做operator new。
函式operator new通常宣告如下:
void * operator new(size_t size);
其返回值型別是void*。此函式返回乙個指標,指向一塊原始的、未設初值的記憶體。函式中的size_t引數表示需要分配多少記憶體,你可以將operator new過載,加上額外的引數,但第一引數的型別必須總是size_t。
你可以像呼叫任何其他函式一樣地呼叫它:
void *rawmemory=operator new(sizeof(string));
這裡的operator new將返回指標,指向一塊足夠容納乙個string物件的記憶體。
和malloc一樣,operator new的唯一任務就是分配記憶體。取得operator new返回的記憶體並將之轉換為乙個物件,是new operator的責任。當你的編譯器看到這樣乙個句子:
string *ps=new
string("memory management");
它必須產生一些**,或多或少會反映以下行為:
void *memory=operator
new(sizeof(string)); //取得原始記憶體(raw memory)用來放置乙個string物件
call string::string("memory management") on *memory; //將記憶體中的物件初始化
string *ps=static_cast
(memory); //讓ps指向新完成的物件
placement new(特殊版本的operator new)
下面示範如何使用placement new:
class widget
wdiget * constructwidgetinbuffer(void *buffer,int widgetsize)
對於表示式new (buffer) widget(widgetsize)
這是new operator的用法之一,其中指定乙個額外自變數(buffer)作為new operator「隱式呼叫operator new」時所用。於是,被呼叫的operator new除了接受「一定得有的size_t自變數」之外,還接受了乙個void*引數,指向一塊記憶體,準備用來接受構造好的物件。這樣的operator new就是所謂的placement new,看起來像這樣:
void * operator
new(size_t, void *location)
看起來很簡單,畢竟operator new的目的是要為物件找到一塊記憶體,然後返回乙個指標指向它。在placement new的情況下,呼叫者已經知道指向記憶體的指標了,因為呼叫者知道物件應該放在**。因此placement new唯一需要做的就是將它獲得的指標再返回。至於沒有用到(但一定得有)的size_t引數,之所以不賦予名稱,為的是避免編譯器發出「某物未被使用」的警告。placement new是c++標準程式庫的一部分。欲使用placement new,你必須用#include 。
如果你希望將物件產生於heap,請使用new operator。它不但分配記憶體而且為該物件呼叫乙個constructor。
如果你只是打算分配記憶體,請呼叫operator new,那就沒有任何constructor會被呼叫。
如果你打算在heap objects產生時自己決定記憶體分配方式,請寫乙個自己的operator new,並使用new operator,它將會自動呼叫你所寫的operator new。
如果你打算在已分配(並擁有指標)的記憶體中構造物件,請使用placement new。
刪除與記憶體釋放
為了避免資源洩漏,每乙個動態分配行為都必須匹配乙個相應但相反的釋放動作。函式operator delete對於內建的delete operator,就好像operator new對於new operator一樣。
當你寫出這樣的**:
string *ps;
...delete ps;
它必須既能夠析構ps所指物件,又能釋放被該物件占用的記憶體。
記憶體釋放動作是由函式operator delete執行,通常宣告如下:
void
operator
delete(void *memorytobedeallocated)
因此,下面這個動作:delete ps;
會造成編譯器產生近似這樣的**:
ps->~string();
operator
delete(ps);
這裡呈現的乙個暗示就是,如果你只是打算處理原始的、未設初值的記憶體,應該完全迴避new operator和delete operator,改呼叫operator new取得記憶體並以operator delete歸還給系統。
這組行為在c++中相當於呼叫malloc和free。
如果你使用placement new,在某記憶體塊中產生物件,你應該避免對那塊記憶體使用delete operator。因為delete operator會呼叫operator delete來釋放記憶體,但是該記憶體內含的物件最初並非是由operator new分配得來的。畢竟placement new只是返回它所接受的指標而已,誰知道那個指標從**來呢?所以為了抵消該物件的constructor的影響,你應該直接呼叫該物件的destructor:
//以下函式用來分配及釋放shared memory中的記憶體
void
* mallocshared(size_t size);
void freeshared(void
*memory);
void
*sharedmemory=mallocshared(sizeof(widget));
widget *pw=constructwidgetinbuffer(sharedmemory,10);
...delete pw; //無定義!
//因為sharedmemory來自mallocshared,不是來自operator new
pw->~widget(); //可以!析構pw所指的widget物件,
//但並未釋放widget占用的記憶體。
freeshared(pw); //可以!釋放pw所指的記憶體,不呼叫任何destructor
如此例所示,如果交給placement new的原始記憶體本身是動態分配而得,那麼你最終還是得釋放那塊記憶體,以免遭受記憶體洩漏之苦。
陣列
string *ps=new
string[10]; //分配乙個物件陣列
由於誕生的是陣列,所以new operator的行為與先前產生單一物件的情況略有不同,由乙個名為operator new的函式負責分配。和operator new一樣,operator new也可以被過載。
「陣列版」與「單一物件版」的new operator的第二個不同是,它所呼叫的constructor數量。陣列版new operator必須針對陣列中的每個物件呼叫乙個constructor。
同樣道理,當delete operator被用於陣列,它會針對陣列中的每個元素呼叫其destructor,然後再呼叫operator delete釋放記憶體。
就好像你可以取代或過載operator delete一樣,你也可以取代或過載operator delete。不過兩者的過載有著相同的限制。
M8 了解各種不同意義的new和delete
1 首先考慮new operator,new operator 可以認為做了三件事情 a 呼叫operator new分配一塊記憶體 b 在這塊記憶體上呼叫構造方法構造物件 返回指標。2 operator new的宣告如下 void operator new size t size 和c中的mall...
條款49 了解new handle行為
多執行緒下的記憶體管理與單執行緒下是完全不同的,因為heap是乙個可以被全域性改動的資源,所以所有的執行緒都有可能去訪問這一資源,這回導致很多的race conditions。當operator new未取得想要的記憶體的時候,會呼叫乙個使用者指定的處理函式,new handler。這個函式可以使用...
條款42 了解typename的雙重意義
條款42 了解typename的雙重意義 includeusing namespace std templatevoid print2nd const c container templatevoid f const c container,不允許使用typename c並不是巢狀 從屬型別名稱,解...