C++中operator new 和 new operator小結以及對new 和 delete初步理解

最近在看C++ Prime,注意到new 和 delete 並沒有原來想象的那麼簡單。記得上學期學C語言時,學習的sizeof和strlen區別時,有一條爲sizeof不是函數在這裏,和sizeof類似,new 和delete也不是函數,它們都是C++定義的關鍵字,通過特定的語法可以組成表達式。我們先看看上面提到的operator new 和 operator delete在C++語言標準庫的原型

void *operator new(size_t);     //allocate an object
void *operator delete(void *);    //free an object
這兩個均是 C++ 標準庫函數!C++ Primer 一書上說new 和 delete是不允許重載的,那麼這裏就不是重載 new 和 delete 表達式(如 operator=就是重載 = 操作符),那麼
operator new 和 new operator到底有什麼區別呢?

new operator:

string *ps=new string("linux");
它完成了兩件事情
第一,分配足夠的內存,用來放置某類型的對象。
第二,調用一個構造函數,爲剛纔分配的內存中的那個對象設定初始值。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("linux");
可以這樣理解
void* memory=operator new(sizeof(string));   //取得原始內存,用於放置一個string對象
call string::string("linux") on *memory;//將內存中對象初始化
string *ps=static_cast<string*>(memory);   //讓ps指向新完成的對象
但是在查資料時,還發現了另一個迷惑的術語 placement new 有時候想直接調用一個構造函數,針對一個已經存在的對象調用其構造函數,並無意義,因爲構造函數用來對象初始化,而對象只能只能初始化一次。但是當需要在一些分配好的原始內存上構建對象時,placement new,允許這麼做。
例如:
class Test
{
  public:
   Test(int Size);
   ......
};
Test* constructTestInBuffer(void *buffer,int size)
{
  return new (buffer) Test(size);
}
此函數返回指針,指向一個Test對象,它被構造於傳遞給此函數的一塊內存緩存區上。當程序運行到共享內存或者內存I/O映射。這類函數可能是有用的,
因爲在那樣運用中,對象必須置於特定的地址,或者置於特殊函數分配出來的內存上。
分析函數內部
Test* constructTestInBuffer 只有一個表達式new (buffer) Test(size),這是new operator的用法之一,其中所指定一個額外的自變量(buffer)作爲
new operator "隱式調用operator new "時所用。於是,被調用的operator new除了接受"一定要有size_t自變量"之外,還接受了一個void* 參數,指向一
塊內存,準備用來接受構造好的對象。這樣的operator new 就是所謂的placement new 
類似還有:
void * operator new(size_t size,void* location)
{
return location;
}
operator new 的目的是要爲對象找到一塊內存,然後返回一個指針指向它,在placement new 的情況下,調用者已經知道指向內存的指針了,因爲調用
者知道對象應該放在哪裏。因此placement new 唯一需要做的就是將它獲得的指針再返回。至於沒有用到(但一定得有)的size_t參數,之所以不賦予
名稱,爲的是避免"編譯器某物未被使用"的警告。
另外注:placement new 是C++標準程序庫的一部分,要使用placement new
得用#include<new>,舊式編譯器用 #include<new.h>
瞭解了placement new 後,我們便能瞭解new operator和operator new之間的關係。

如果你希望將對象產生於heap,就是要用new operator,它不但分配內存而爲該對象調用一個構造函數。
如果你只是打算分配內存,請用operator new,就沒有構造函數被調用。
如果你打算在heap object產生自己決定的內存分配方式,請寫一個自己的operator new。並使用new operator,它將會自動調用你所寫的
operator new。
如果你打算在已經分配(並擁有指針)的內存構造對象,使用placement new 。

delete 與內存釋放

爲了避免資源泄露,每一個動態分配行爲都必須匹配一個相應的釋放動作。
函數 operator delete對於內建的delete operator(操作符)就好像 
operator new 對於new operator一樣。
string *ps;
...
delete ps; //使用delete operator.
內存釋放動作是由operator delete執行的,通常聲明如下:
void operator delete(void* );
因此 delete ps;
執行的步驟如下:
ps->~string();//調用析造函數
operator delete(ps);//釋放對象所佔用的內存
如果只打算處理原始的、未設初值的內存,直接operator delete 歸還給系統。
即調用operator new取得內存並以operator delete歸還系統。
如:
void* buffer=operator new (50*sizeof(char));//分配內存,放置50個char,沒有調用構造函數
...
operator delete(buffer); //釋放內存,而沒有直接調用析構函數。
類似與C語言中malloc和free。
但是如果使用了placement new ,在某塊內存中產生對象,你應該避免那塊內存使用delete operator(操作符)。
因爲delete operator會調用operator delete來釋放內存,但是該內存所含的對象最初並不是由operator new 分配來的。
placement new只是返回它接收的指針而已。爲了抵消該對象的構造函數的影響,使用placement new 時應該直接調用該對象的析構函數。
請看示例:

void * mallocShared(size_t size);//申請分配內存
void freeShared(void * momery);//釋放內存
void* sharedMemory=mallocShared(sizeof(Test));
Test *pw=constructTestBuffer(sharedMemory,10);//使用前面Test類的placement new
...
delete pw;//無定義,因爲sharedMemory來自mallocShared,不是來自new
pw->~Test();//OK,析構函數pw所指Test對象,但並釋放Test所佔用內存。
freeShared(pw);//OK,釋放pw所指的內存,不調用任何析構函數。
如上述所示,如果交給placement new的原始內存(raw memory)本身是動態分配而得的,那麼最終得釋放那塊內存,以避免內存泄露
基於單一對象上的已經瞭解了,但如果是一組對象呢?
string *ps=new string[10];//分配一個對象數組
1.這裏的new 與前面的new 行爲類似,但略有不同,這裏不能再operator new分配內存,而是以operator new[]負責分配。和operator new 一樣,operator new[]也可以被重載。
2.數組的new 與單一對象的new所調用的構造函數不同,數組的new 必須針對數組中每一個對象調用一個構造函數。
string *ps=new string[10];//調用operator new[]以分配足夠容納10個string對象的內存,然後針對每個元素調用string的默認構造函數。

同樣的,當使用了delete,它也會針對數組中每一個元素調用析構函數,然後再調用operator delete[]釋放內存。如:

delete []ps;//爲數組中的每一個元素調用string 析構函數,然後再調用 operator delete[] 釋放內存。跟operator delete一樣 operator delete[]也可以被重載。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章