1、不僅是malloc和freec++
new和delete都是C++中最基本的詞彙,天天的代碼都反覆的與他們打交道,對他們最基本的認識就是分配和釋放內存,最開始我認爲他們就是C++版的malloc和free,可是其實內有玄機,除了分配內存,還能夠用他們實現什麼?函數
2、new和delete的完整過程spa
2.1 new 的完整過程能夠分爲三步,拿A* a=new A(1)這句爲例:對象
operator new(size_t size)的默認實現是:接口
void *ptr = (void *)malloc(size);
return ptr; 內存
operator new( size_t count, void * object)的默認實現是 it
return object;內存管理
前兩個步驟實際上是operator new的不一樣overwrite,可能開始不太容易理解第二步的意義,第二步好像什麼都沒作,爲何還要存在這種形式的operator new呢?實際上是由於這些operator new均可以被程序訪問,例如咱們能夠直接寫出這樣的代碼完成new同樣的操做:class
void * raw=operator new(sizeof(A));object
A* a=new(raw)A(1)
(固然調用operator new要include 「new.h」,由於operator new不是c++原生的操做,定義在一個頭文件中)
其中第一步是分配內存,第二步中的new(raw)部分就要調用operator new( size_t count, void * object)了,new(buffer)class(param)這種形式的new也被稱爲placement new,它的意思是在一個已經存在的內存上構造某個對象,operator new( size_t count, void * object)這個看似無心義的接口實際上是爲了這種寫法而作的。
2.2 delete ,delete 的完整過程也可分爲兩步,拿 delete* a舉例:
因此咱們能夠用下面代碼替換delete a:
a.~A();
operator delete(raw);
2.3 上面是new和delete的完整過程,響應的還有new[]和delete[]的相似實現,這是分配與釋放一個相連的多個實例:
void* operator new[](size_t size){
void *ptr = (void *)malloc(size);
return ptr;
}
void operator delete[](void* p){
free(p);
return;
}
下面兩個是爲了placement new[]和delete[]
void* operator new[](size_t size,void * object){
return object;
}
void operator delete[](void* p,void *p){
return;
}
因此一段傳統的代碼 A* a=new A[10] 和 delete[] a也能夠經過這些operator new[]的調用來實現成以下:
void* raw=operator new(sizeof(A)*10+sizeof(int));
A* a=new(raw)A[10];
for(size_t i=0;i<10;i++){
fl2[i].~leon();
}
operator delete[](raw);
這裏在分配內存的地方有個很特殊的地方,我想建立10個A,理應分配sizeof(A)*10大小的內存,可是對於operator new[](size_t size)的調用,必須多給他分配一個sizeof(int),寫成上面的void* raw=operator new(sizeof(A)*10+sizeof(int));這是爲何呢?
緣由在於這裏是分配連續的N個內存,可是你必須讓c++多用一個int來存儲這個N,這個int多是放在這片內存的首位。
若是很少分配這個空間會怎樣?那麼operator delete[]時會出錯,由於operator delete[]會首先讀取一個int知道有多少個A要釋放,你沒有分配這個int,它可能讀到一個不合理的值N,而後去釋放,發現釋放不了出現內存訪問錯誤,或者N極大一直去釋放,表現上程序永遠結束不了。
3、重載你的new和delete
瞭解了上面的new和delete的過程能夠怎樣?既然C++已經爲咱們封裝好了new和delete,爲何還要把他們赤裸裸的剝開。答案是:爲了重載,爲了定義咱們本身的new和delete。
首先一個問題,你能夠重載new和delete嗎?答案是不能,剛學C++重載時,C++的標準就告訴咱們new和delete操做是不能被重載的,可是有的時候咱們確實須要定製一些咱們本身的new和delete,(最簡單的情形是我想統計我一共new了多少內存又delete了多少)。彷佛沒有解決方法了,可是看到了new和delete的內部,咱們就有辦法了,由於咱們能夠重載組成new和delete的各個關鍵步驟的operator new和operator delete!這是一個使人興奮的消息。
爲了重載咱們要了解operator new 和delete各有幾種形式,查查msdn,嚇了一跳,包括[]形式一共有3*2*2=12種定義好的重載接口:
operator new:
A形式 這是new的第一步,分配內存
void *__cdecl operator new(
size_t count
);
B形式 這是for placement new
void *__cdecl operator new( size_t count, void * object ) throw();
C形式 這是不拋出異常的形式
void *__cdecl operator new(
size_t count,
const std::nothrow_t&
) throw();
operator delete:
void operator delete( void* _Ptr ) throw( ); void operator delete( void *, void * ) throw( ); void operator delete( void* _Ptr, const std::nothrow_t& ) throw( );
operator new[]:
void *operator new[]( std::size_t _Count ) throw(std::bad_alloc); void *operator new[]( std::size_t _Count, const std::nothrow_t& ) throw( ); void *operator new[]( std::size_t _Count, void* _Ptr ) throw( );
operator delete[]:
void operator delete[]( void* _Ptr ) throw( ); void operator delete[]( void *, void * ) throw( ); void operator delete[]( void* _Ptr, const std::nothrow_t& ) throw( );
C++的運算符重載分爲全局重載和局部的類內的重載。
3.1 首先的全局重載,全局重載意味着代碼內全部的new和delete的行爲都被改變(包括你引用的庫),要十分當心,甚至不少人說不要使用全局重載new和delete。
下面是我全局重載operator new和new[]的第一種A形式(C形式雷同),注意在以上這些操做中,在VS中A和C種方法均可以被用戶全局重載,可是B種形式(也就是placement 形式)不容許被重載(它實現成了inline函數),不知道其餘IDE是怎樣的 規定,可是VS的規定是有道理的,由於咱們幾乎想不出有什麼理由要placement new,它本就是一種爲形式而生的形式。。。
//overload global operator new
void* operator new(size_t size){
cout<<"new "<<size<<endl;
void *ptr = (void *)malloc(size);
return ptr;
}
//overload global operator delete
void operator delete(void* p){
cout<<"delete "<<_msize(p)<<endl;
free(p);
return;
}
//overload global operator new[]
void* operator new[](size_t size){
cout<<"new[] "<<size<<endl;
void *ptr = (void *)malloc(size);
return ptr;
}
//overload global operator new[]
void operator delete[](void* p){
cout<<"delete[] "<<_msize(p)<<endl;
free(p);
return;
}
在個人重載中對於每次施放和分配都額外打印當前涉及內存的大小
3.2 類內重載,這種重載更加經常使用一些,這裏要注意,對於類內重載,若是要調用placement new,那麼B形式必須被重載,不然你的代碼編不過,這與全局重載徹底不一樣,如下是我對類內實現的A和B形式的重載:
class leon{
public:
leon(){
m_a=0;
}
leon(int a){
m_a=a;
}
~leon(){
}
//overload operator new
void* operator new(size_t size){
cout<<"new "<<size<<endl;
void *ptr = (void *)malloc(size);
return ptr;
}
void* operator new(size_t size,void* buf){
cout<<"new "<<size<<endl;
return buf;
}
//overload operator delete
void operator delete(void* p){
cout<<"delete "<<_msize(p)<<endl;
free(p);
return;
}
//overload global operator new[]
void* operator new[](size_t size){
cout<<"new[] "<<size<<endl;
void *ptr = (void *)malloc(size);
return ptr;
}
void* operator new[](size_t size,void* buf){
cout<<"new[] "<<size<<endl;
return buf;
}
//overload operator new[]
void operator delete[](void* p){
cout<<"delete[] "<<_msize(p)<<endl;
free(p);
return;
}
3.3 更多形式的重載:
其實除了c++定義好的A、B、C三種operator new()接口,咱們還能夠自定義新的重載形似,如定義一個void* operator new(size_t size,bool b),那麼我就能夠調用A* a=operator(sizeof(A),true)來執行個人一些特殊需求
4、operator new和delete及其重載的應用:
對new和delete重載能夠在不少地方應用到:
最多見的就是一些內存管理和統計功能:你想知道你分配和釋放了多少內存
內存泄露的監測:經過統計能夠知道是否有內存泄露,甚至定位到於某處有泄露
另外經過使用placement new還能夠在某些場合加快對象的建立,好比你能夠事先分配好一片內存,而後利用placement new在上面直接建立,這樣以後的每次建立不會再有反覆的內存分配和釋放操做
利用上面的特性能夠實現某種內存池來加速內存訪問
以上只是拋磚引玉,之後能夠再實踐中多利用C++的這個特性