C++ new new[]詳解
例子是:
#ifndef __PLACEMENT_NEW_INLINE #define __PLACEMENT_NEW_INLINE inline void *__cdecl operator new(size_t, void *_P) {return (_P); } #if _MSC_VER >= 1200 inline void __cdecl operator delete(void *, void *) {return; } #endif #endif
- A* s = new(p) A(XXX);
A* a = new A;
operator new的三種形式:
throwing (1) |
void* operator new (std::size_t size) throw (std::bad_alloc); |
---|---|
nothrow (2) |
void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw(); |
placement (3) |
void* operator new (std::size_t size, void* ptr) throw(); |
class A { public: A() { std::cout<<"call A constructor"<<std::endl; } ~A() { std::cout<<"call A destructor"<<std::endl; } void* operator new(size_t size) { std::cout<<"call A::operator new[] size:"<<size<<std::endl; return malloc(size); } void operator delete[](void* p) { std::cout<<"call A::operator delete[]"<<std::endl; free(p); } void operator delete(void* p) { free(p); } };
//cpp
#include <iostream>
#include "A.h" void* operator new[](size_t size) { std::cout<<"call global new[] size: "<<size<<std::endl; return malloc(size); } void operator delete[](void* p) { std::cout<<"call global delete[] "<<std::endl; } int _tmain(int argc, _TCHAR* argv[]) { std::cout<<"sizeof A "<<sizeof(A)<<std::endl; A* p1 = new A[3]; delete []p1; system("pause"); return 0; }
1 void * Time::operator new[](size_t size)//重載new[]()運算符,以分配數組 2 { 3 4 std::cout<<"operator new[]() is called.Object size is "<<size<<std::endl; 5 6 return malloc(size);//?//在自由存儲中分配內存 7 8 }
- (size - 4)/(那多出來四字節的值)
1 #include<iostream> 2 void* operator new[](size_t size) 3 4 { 5 6 std::cout<<"call global new[] size: "<<size<<std::endl; 7 8 return malloc(size); 9 10 } 11 12 class Time 13 14 { 15 16 private: 17 18 int hrs,mins,secs;//時,分,秒 19 20 public: 21 22 Time(int hrs=19,int mins=35,int secs=20);//默認參數的帶參構造函數 23 24 ~Time();//析構函數 25 26 void showTime()const; 27 28 Time operator ++();//重載前綴遞增運算符,++x 29 30 Time operator ++(int);//重載後綴遞增運算法,x++ 31 32 bool operator ==(const Time &)const;//重載相等性運算符 33 34 Time & operator =(const Time &);//重載賦值運算符 35 36 void * operator new(size_t size);//重載new()運算符,如:int * pInt=new int(0); 37 38 void operator delete(void * ptr);//重載delete()運算符,如:delete pInt; 39 40 void * operator new[](size_t size);//重載new[]()運算符,以分配數組 41 42 void operator delete[](void * ptr);//重載delete[]()運算符,以去配數組,釋放數組所佔內存 43 44 }; 45 46 Time::Time(int hrs,int mins,int secs) 47 48 { 49 50 this->hrs=hrs; 51 52 this->mins=mins; 53 54 this->secs=secs; 55 56 std::cout<<"Time類默認參數的帶參構造函數 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl; 57 58 } 59 60 61 62 Time::~Time() 63 64 { 65 66 std::cout<<"Time類析構函數 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl; 67 68 } 69 70 71 72 void Time::showTime()const 73 74 { 75 76 std::cout<<"Time類showTime()const函數 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl; 77 78 } 79 80 Time Time::operator ++()//重載前綴遞增運算符,++x 81 82 { 83 84 secs++; 85 86 if(secs>=60) 87 88 { 89 90 secs=0; 91 92 mins++; 93 94 if(mins>=60) 95 96 { 97 98 mins=0; 99 100 hrs++; 101 102 if(hrs>=24) 103 104 { 105 106 hrs=0; 107 108 } 109 110 } 111 112 } 113 114 return Time(hrs,mins,secs);//返回無名臨時對象 115 116 } 117 118 Time Time::operator ++(int)//重載後綴遞增運算法,x++ 119 120 { 121 122 Time temp(hrs,mins,secs);//生成臨時對象,並進行初始化 123 124 ++secs; 125 126 if(secs>=60) 127 128 { 129 130 secs=0; 131 132 mins++; 133 134 if(mins>=60) 135 136 { 137 138 mins=0; 139 140 hrs++; 141 142 if(hrs>=24) 143 144 { 145 146 hrs=0; 147 148 } 149 150 } 151 152 } 153 154 return temp; 155 156 } 157 158 bool Time::operator ==(const Time & aTime)const//重載相等性運算符 159 160 { 161 162 return ((hrs==aTime.hrs)&&(mins==aTime.mins)&&(secs==aTime.secs)); 163 164 } 165 166 Time & Time::operator =(const Time & aTime)//重載賦值運算符 167 168 { 169 170 hrs=aTime.hrs; 171 172 mins=aTime.mins; 173 174 secs=aTime.secs; 175 176 std::cout<<"Time類賦值運算符函數 "<<(this->hrs)<<':'<<(this->mins)<<':'<<(this->secs)<<std::endl; 177 178 return (*this);//返回當前對象的引用 179 180 } 181 182 void * Time::operator new(size_t size)//重載new()運算符,如:int * pInt=new int(); 183 184 { 185 186 std::cout<<"operator new() is called.Object size is "<<size<<std::endl; 187 188 return malloc(size);//?//在自由存儲中分配內存 189 190 } 191 192 void Time::operator delete(void * ptr)//重載delete()運算符,如:delete pInt; 193 194 { 195 196 std::cout<<"operator delete() is called"<<std::endl; 197 198 free(ptr);//在自由存儲中釋放內存 199 200 } 201 202 void * Time::operator new[](size_t size)//重載new[]()運算符,以分配數組 203 204 { 205 206 std::cout<<"operator new[]() is called.Object size is "<<size<<std::endl; 207 208 return malloc(size);//?//在自由存儲中分配內存 209 210 } 211 212 void Time::operator delete[](void * ptr)//重載delete[]()運算符,以去配數組,釋放數組所佔內存 213 214 { 215 216 std::cout<<"operator delete[]() is called"<<std::endl; 217 218 free(ptr);//在自由存儲中釋放內存 219 220 } 221 222 223 224 int main() 225 226 { 227 228 Time * pTime; 229 230 pTime=new Time;//重載new()運算符,調用默認構造函數 231 232 pTime->showTime(); 233 234 delete pTime;//重載delete()運算符 235 236 pTime=new Time[3];//重載new[]()運算符,以分配數組,調用默認構造函數 237 238 delete [] pTime;//重載delete[]()運算符,以去配數組,釋放數組所佔內存 239 240 getchar(); 241 242 return 0; 243 244 }
對new的幾種形式的一些認識
答:當用在內置數據類型或者結構時,malloc能滿足我們的需要,但用在類類型時不能,這就需要引用new,
new既分配堆內存,又自動調用類的構造函數來創建對象。
是本教科書都有提到上面這一點,可是這些公司爲何還樂此不疲的考到這到道題呢?事情不是這麼簡單,我獻
醜說這幾句。
一、new 與 opeartor new
首先問大家一個問題:new 與 opeartor new有何區別?
如果你回答:operator new就是new的重載運算符唄!
回答錯誤,new是C++內部定義的一種操作符,總像sizeof一樣是一種操作符,而operator new是實作者定義的
一個全局函數。不信,你可以寫下面的語句:
int *p = operator new( //這時候,用的是VC的話,就會自動提示該函數的原型。嗯,有七個版本之多。好
,現在我們弄清楚了,一個是操作符,一個是全局函數,而不是原來所以爲的是重載的關係。正同我們看看他
們各有什麼用?
new操作符作了三件事.語句:
MyClass * p = new MyClass;
的僞碼大至如下:
void *memory = operator new(sizeof(MyClass)); //1調用全局函數: operator new
MyClass::MyClass(); //2調用構造函數創建對象,如帶參數的話
//調用的就是帶參構造函數
MyClass * p = static_cast<MyClass*>(memory); //3轉換指針類型,並賦給p
我們可以猜測到真正的內存分配是在operator new函數中完成的。
operator new函數其之的原型是:
void* operator new(size_t size) throw(std::bad_alloc)
返回類型是void* , 參數類型是:size_t ,是一個在系統頭文件 <cstddef>中定義的typedef。sizeof()的
操作結果就是該類型的。
void* operator new(size_t size)大至如下:
void *p ;
while((p = malloc(size)) == 0)
{
//內存分配失敗。
if ( _callnewh(size) == 0)
{
_Nomemory();
}
}
return p;
如果內存分配成功則直接返回所分配的內存的首地址。不然就循環調用if語句,首先判斷 _callnewh(size)調
用是否成功,不成功則調用_Nomemory(),_Nomemory()實際上作用是拋出一個異常,所拋出的是一個標準庫中
定義的std::bad_alloc對象。_callnewh()函數首先會判斷一個全局變量 _pnhHeap是否爲零, _pnhHeap存放的
是一個函數指針,我們可以爲new操作指定一個出錯處理函數,也就是說當new分配內存失敗時就會調用我們剛
才指定的函數,出錯處理函數原型必須是無參且返回類型是void的函數。可以通過全局函數set_new_handler
(new_handler pnew)來設定我們的出錯處理函數,而他又是通過調用_PNH _set_new_handler(_PNH pnh)來實現
設定我們的出錯處理函數的。下面是_PNH _set_new_handler(_PNH pnh)的定義:
_PNH __cdecl _set_new_handler(
_PNH pnh
)
{
_PNH pnhOld;
_mlock(_HEAP_LOCK);
pnhOld = _pnhHeap; //把原來在起作用的出錯處理函數的指針賦給pnhOld
_pnhHeap = pnh; //我們新設定的出錯處理函數的指針。
_munlock(_HEAP_LOCK);
return(pnhOld); //返回原來舊的出錯處理函數的指針。
}
回到我們的_callnewh()函數,如果在MyClass * p = new MyClass;語句之前設定了出錯處理函數,那麼這裏的
_pnhHeap就不爲零,接着就會調用(*_pnhHeap)()即我們的出錯處理函數,否則返回零。接着調用_Nomemory()
拋出異常。這個出錯處理函數是個重要的函數,設計的好的話可以做很多事情,因爲他是在while中被調用的有
多次被調用的機會。在我們的出錯處理函數中如果沒有使用exit(1)等退出程序的語句,也沒有拋出異常的話,
執行完我們的出錯處理函數後,又回到while((p = malloc(size)) == 0),再次分配內存並判斷,還是失敗的
話,再次調用我們的出錯處理函數,當然這個出錯處理函數和上面的那個出錯處理函數不一樣了,因爲我們可
以在上面那個出錯處理函數中調用set_new_handler(_PNH pnh)重新設定一個出錯處理函數,也就是我們第二次
調用的這個出錯處理函數。還不行的話,可以繼續循環,直到你滿意爲止。如果你覺得累了,不玩了,最後就
會調用_Nomemory()拋出異常,函數返回到new調用的地方。
好,現在我們清楚了操作符new作了三件事,首先調用全局operator new函數, 後者通過調用傳統的malloc
函數分配內存,如果成功直接返回,不然,判斷出錯處理函數是否爲零,不爲零的話,調用我們的出錯處理函
數。否則調用_Nomemory()拋出異常。如果p = malloc(size)成功,new接着做第二件事,創建對象,最後轉換
指針類型並返回。
我們可以重寫operator new函數。當編譯器看到語句MyClass * p = new MyClass;
首先會檢查我們的類定義看是否提供有operator new函數,如有,則調用該函數,接着調用構造函數,轉換類
型並返回。如果沒有重寫operator new函數,則new操作符會調用全局中的那個operator new函數,也就是我們
上面說的這個函數。但是如果我們在new操作符前面限定了::即這樣寫 ::new MyClass則編譯器不會去檢查我們
的類的定義而直接調用全局的operator new函數。
操作符new不可以重載,就像sizeof操作符一樣是不可以重載的。我們重載的是operator new函數。所以有
一些限定,我們重載的operator new函數的返回類型必須是void*,第一個參數必須是size_t類型的。下面是一
個自定義的operator new函數:
class MyClass
{
public:
MyClass()
{
cout << "MyClass::MyClass()" << endl;
}
static void* operator new(size_t size);
~MyClass()
{
cout << "MyClass::~MyClass()" << endl;
}
};
void* MyClass::operator new(size_t size)
{
//在這裏可以對類的靜態成員數據做些控制。我們在這裏有一句輸出語句代替。
cout << "MyClass::operator new" << endl;
void* p = new MyClass;
return p;
}
這樣寫是行不通的,因爲在MyClass::operator new中的void* p = new MyClass的new是操作符new,他做三件
事,第一件就是調用MyClass::operator new(size_t size),所以這裏
是遞歸調用了。把程序改成:
void* MyClass::operator new(size_t size)
{
//在這裏可以對類的靜態成員數據做些控制。我們在這裏有一句輸出語句代替。
cout << "MyClass::operator new" << endl;
void* p = operator new(size); //已修改。
return p;
}
這樣還是不行的,這樣是直接遞歸(自己調用自己)。剛纔是間接遞歸。應該改成:void* p = ::operator new
(size); OK,使用的是全局中的operator new,或者寫成:void* p = malloc( size),只是這樣一來,出錯後
不會自動調用出錯處理函數了,只會簡單的返回NULL,所以在使用new操作符的地方要注意先檢測返回值是否爲
零,所以最好不用malloc,還是用:: operator new(size)好,這裏還可以用void* p = new char[size],用的
是new[]操作符,不會兩次調用構造函數,也不會造成遞歸。只是要注意在我們重寫的operator delete函數中
要調用delete[] 後釋放。一般情況下,我們重寫了operator new函數,都要重寫operator delete函數,而且
後者中的釋放資源的函數要與前者分配資源的函數的形式要搭配。
另外,要想把自己重寫的operator new函數設計得好,還是有好些地方需要注意的。好在需要我們重寫這個函
數的情況不多,真正需要重寫時,還是先參考些這方面的資料纔行,<effective c++>一書中就有相關的知識介
紹。在這裏我只是提到一下,讓大家知道有這麼一回事,應付一下這道公司們樂此不疲的考題。洋洋灑灑寫上
上千字,小樣,看你還敢不敢考這樣的考題。
二、new[] 與 operator new[]
new[]操作符與new差不多,同樣做三件事:調用operator new[]函數,歷遍一個vector調用構造函數。轉換
指向首地址的指針類型並返回。
operator new[]函數通過把操作符new A[number]中的A與number進行計算:size_t count = number * sizeof
(A), 然後調用全局函數operator new(count).
三 new(void*) Myclass 與 operator new(size_t, void*)
指定位置創建操作符new()同樣做三件事,第一件就是調用operator new(size_t, void*)函數,下面兩件和
new操作符的最後二件事是一樣的。讓我們來看看vs.net中operator new(size_t, void*)的定義:
inline void *__cdecl operator new(size_t, void *_Where) _THROW0()
{ // construct array with placement at _Where
return (_Where);
}
和operator new相比好簡單哦,我們看到了,他並沒有調用malloc函數,也沒有調用operator new函數,他怎
麼分配的內存啊?!對於operator new函數,他通過循環調用malloc函數來分配一塊內存,最好把這塊分配好的
內存return p返回給操作符new,讓他在上面做第二,第三件事。我們這裏return (_Where);按此推理,_Where
6應該指向一塊已分配的可使用的內存。_Where從那裏來的啊?答案是使用操作符new(void* _Where) MyClass
時所指定的。這就是指定位置創建操作符new()的用法,先在別處分配好一塊內存,然後把這塊內存的首地址做
爲參數調用new(),new()就會在這塊指定位置上創建對象,然後再把這塊指定的內存的首地址自制一份給p,接
着轉換類型並返回。這樣子操作符new()並沒有真正分配內存,所以不能調用delete來釋放內存。當程度使用共
享內存或者memory-mapped I/O指定位置創建就比較有用,因爲在這樣的程序裏對象必須放置在一個確定的地址
上或者一塊被例程分配的內存裏。下面看個例子。
#include <iostream>
#include <new> //要使用指定位置創建操作符發佈包含該頭文件。
using namespace std;
void* mallocShared(size_t size); //用於分配共享內存,該函數是別的程序員寫的,你只知道通過
//調用他可以獲得一塊已分配而未初始化的內存。
class A
{
public:
A()
{
cout << "A::A()" << endl;
m_n = 0;
}
int Get()
{
return m_n;
}
~A()
{
cout << "A::~A()" << endl;
}
private:
int m_n;
};
int main()
{
void* p = mallocShared(sizeof(A)); //該句也有可能是在別的地方調用的,
//然後把p傳過來。這裏爲了簡化而放在此調用
A* pA = new(p) A;
cout << pA->Get() << endl;
delete pA;
return 0;
}
void* mallocShared(size_t size)
{
void* p = malloc(size);
if ( p == NULL)
{
cerr << "mallocShared(size_t size) failed!" << endl;
exit(1);
}
return p;
}
這個程序有問題嗎?拋開設計上的好壞不說,就說這個程序能否通過編譯?如能,運行結果如何?請先思考三
分鐘再往下看。
有疑問的可能是這一句delete pA; 函數mallocShared不是你寫的,你不知道其內部是通過什麼形式分配的內存
,就調用delete來釋放能行嗎?假如又讓我們知道他內部是通過malloc函數來分配的內存,用delete來釋放能
行嗎?答案是:能通過編譯,並能得出正確結果,通過malloc函數來分配的內存用delete來釋放是沒問題的,
new操作符不也是通過malloc來分配的內存,同樣可以用delete來釋放啊。如果mallocShared是下面這樣子,情
況又怎麼樣呢?
void* mallocShared(size_t size)
{
return (new char[size]);
}
結果是和上面是一樣的,delete p沒問題,只要用new[]來分配的內存塊的大小和用delete釋放的內存的大小是
一樣的就沒問題。回想一下看,我們用new A來分配內存時,實際上是通過operator new(size)來分配的,這裏
的size = sizeof(A),這種情況下我們可以用 delete p來釋放,只要p 的類型是A*,因爲delete 是通過調用
operator delete(size)函數來釋放的內存,這裏的size也是等於sizeof(P),當我們調用new char[number]時
,先調用operator new[]函數,後者實際上也是調用 operator new(number* sizeof(char))來分配的內存,既
然都是通過調用operator new來分配的內存,所以調用delete 來釋放應該也是沒問題的。因爲這
裏:mallocShared(sizeof(A)) ----> size == sizeof(A) == size* sizeof(char) == sizeof(P) 大小一樣。
改成:
void* mallocShared(size_t size)
{
return (operator new(size));
}
結果也是一樣的! 當然這裏是假設沒有提供自己的operator new函數的情況下,如果重寫了operator new函數
就要改成:
void* mallocShared(size_t size)
{
return (::operator new(size)); //用的是全局函數
}
這個程序目前沒問題,但是存在很多的安全隱患,很容易就出錯,一不小心就陰溝裏翻船,有“未定義”行爲
產生,結果是啥事都有可能發生。應該謝絕寫這樣的程序。
上面說了mallocShared(sizeof(A));可能不是在你的程序中調用的,而是在別人那裏調用的,然後別人給你傳
來一個指針讓你把這個指針作爲參數調用你自己的A* pA = new(p) A,這樣子你調用delete p來釋放,別人那裏
或者還需要用,因爲這是共共享的內存,又或者別人那裏在做完想要做的工作之後,調用delete ,這樣子就出
問題了。同一塊內存不能釋放兩次。就算mallocShared(sizeof(A));是在你這裏調用的,那麼你可以算是內存
的分配者,你有權利and義務把他釋放,可是你也要先確定別人還需不需要用到這塊內存,需要的話,你就不能
馬上delete,又或者呆會你自己也還需要用到,再次在這塊內存上指定位置創建。所以就不必再調用
mallocShared(sizeof(A));來分配內存。把主函數改成下面這樣,結果又如何,能通過編譯嗎?
int main()
{
void* p = mallocShared(sizeof(A)); //就限定是在這裏調用的。
A* pA = new(p) A;
cout << pA->Get() << endl;
A* pA1 = new(p) A; //再次指定位置創建。
delete pA;
return 0;
}
答案是:能通過編譯,運行結果如下:
A::A()
0
A::A()
~A::A()
原來的那塊內存是確實被釋放掉了的,只是這裏構造函數A::A()調用了兩次而析構函數A::~A()只調用一次,
這顯然不太好,如果你的類在其他地方分配了資源,需要通過析構函數來釋放,這樣子你少調用了一次析構函
數就會造成內存泄漏或者別的問題了。所以應該把程序改成下面這樣:
int main()
{
void* p = mallocShared(sizeof(A)); //就限定是在這裏調用的。
A* pA = new(p) A;
cout << pA->Get() << endl;
A* pA1 = new(p) A; //再次指定位置創建。
pA->~A(); //這裏顯式調用析構函數來析構對象,但是內存並沒有釋放,還可以再次使用。
A* pA1 = new(p) A;
//在這裏判斷別的程序是否還需要用到該內存
delete pA; //當別人不再需要,自己也不會再用到,可以釋放!
return 0;
}
這句delete p總讓我擔心受怕,最好調用和mallocShared函數相對應的函數來釋放內存,你寫了mallocShared
函數來分配資源就有義務寫一個freeShared函數來釋放資源,分配資源函數和釋放資源函數是一對的,一起提
供給別人使用。因爲只有你自己最清楚你的mallocShared函數是怎麼分配的資源,你的freeShared就應該做相
應的工作。比如在mallocShared中除了分配內存,還用到其他資源,如果直接調用delete p來釋放那就成問題
了。應該調用freeShared來釋放。如果你是老闆而你的員工只寫一個mallocShared函數卻沒有提供相應的
freeShared函數,建議你讓他走人! 不然遲早會出問題的。如果void* p = mallocShared(sizeof(A)); 語句不
是在這裏調用的,你既不能使用delete p,也不能使用freeShared(p),或者其他一切釋放資源的函數。不是你
分配的資源你無權釋放。當然你對整個程序把握得比較好,一切盡在你控制中,而你又和別人有協議由你來釋
放的情況除外。
void * operator new(size_t, void *_Where)同樣可以提供自己的版本,這時候第二個參數可以是別的類型,
_Where也不一定是指向一塊已分配而未使用的內存,可以是一個指向可以分配內存的函數的指針,然後在
operator new(size_t, void *_Where)內部通過該指針來調用函數從而分配內存。也可以是其他東西,不一定
要是指針。總之可以傳遞你想傳遞的東西。下面來看個例子,這個例子來自<Bjarne Stroustrup的FAQ:C++的
風格與技巧>我懶得寫了,就用他寫的這個.
以下是原文:
有沒有“指定位置刪除”(placement delete)?
沒有,不過如果你需要的話,可以自己寫一個。
看看這個指定位置創建(placement new),它將對象放進了一系列Arena中;
class Arena {
public:
void* allocate(size_t);
void deallocate(void*);
// ...
};
void* operator new(size_t sz, Arena& a)
{
return a.allocate(sz); //梁楊注:這裏第二個參數傳遞的是一個引用,
//然後通過其成員函數來分配內存.
}
Arena a1(some arguments);
Arena a2(some arguments);
這樣實現了之後,我們就可以這麼寫:
X* p1 = new(a1) X;
Y* p2 = new(a1) Y;
Z* p3 = new(a2) Z;
// ...
但是,以後怎樣正確地銷燬這些對象呢?沒有對應於這種“placement new”的內建的
“placement delete”,原因是,沒有一種通用的方法可以保證它被正確地使用。在C++的
類型系統中,沒有什麼東西可以讓我們確認,p1一定指向一個由Arena類型的a1分派的對象
。p1可能指向任何東西分派的任何一塊地方。
然而,有時候程序員是知道的,所以這是一種方法:
template<class T> void destroy(T* p, Arena& a)
{
if (p) {
p->~T(); // explicit destructor call
a.deallocate(p);
}
}
現在我們可以這麼寫:
destroy(p1,a1);
destroy(p2,a2);
destroy(p3,a3);
如果Arena維護了它保存着的對象的線索,你甚至可以自己寫一個析構函數,以避免它發生
錯誤。
這也是可能的:定義一對相互匹配的操作符new()和delete(),以維護《C++程序設計語
言》15.6中的類繼承體系。參見《C++語言的設計和演變》10.4和《C++程序設計語言》
19.4.5。
///
C++中有指定位置創建操作符:new(),但沒有指定位置刪除操作符:delete()。
我們可以寫:
X* p1 = new(a1) X; //指定位置創建
但是不可以寫:
delete(a1) p1 //這句不能通過編譯。
他上面寫的這個程序其實是有點問題的(靠,梁楊你也太牛B了吧,C++之父寫的你也敢說有問題?!),用事實說話
,如果只提供 void* operator new(size_t sz, Arena& a)而不提供void operator delete(void* , Arena&)
函數,vs.net的編譯器會提出一條警告: “void *operator new(size_t,Arena &)” : 未找到匹配的刪除運
算符;如果初始化引發異常,則不會釋放內存。
對於new操作符會作三件事,在第一件分配內存順利完成之後,接着會調用構造函數,如果在調用構造函數中發
生異常,他就會調用operator delete(void*)函數來釋放在第一件事中通過operator new(size_t)來分配的內
存,從而保證不會發生內存泄漏。同樣定位創建操作符new()也做三件事:第一件調用我們重寫的void*
operator new(size_t sz, Arena& a)來分配內存,這步成功之後接着調用構造函數,那麼如果在調用構造函數
中發生異常時,怎麼辦呢?沒有相應的釋放函數來給編譯器調用,所以我們必須自己提供一個void operator
delete(void* , Arena&)來釋放內存。這個函數第一個參數的類型必須是void*, 第二個類型必須和operator
new()中的相同,不然當發生異常時,編譯器不會調用該函數的。當我們提供了void operator delete(void* ,
Arena&)後還是不能寫:delete(a1) pA1這樣的語句的。可以寫operator delete(pA1, a1),但是最好不要這樣
調用,該函數是專爲編譯器寫的,當發生構造異常時調用的。當我們成功的創建一個對象,做完一些事之後,
應該還象他上面寫的那樣,通過調用destroy(p1,a1);來釋放。
注意這裏內存分配是在指定創建函數operator new()中調用其他函數來分配的,所以我們需要提供一個相應的
delete( )來預防構造失敗時來釋放資源。但是在我們前面寫的那個共享內存的例子,定位創建函數new()並沒
有分配內存,內存分配是在其他地方完成的,這時候我們就不需要提供定位刪除函數delete()來釋放資源,你
構造函數失敗就失敗唄,內存又不是你分配的,你無權釋放,不可能說你創建一個對象失敗了,連內存都釋放
了,呆會有權釋放內存者(分配者有權釋放)再釋放一次,那問題就大了。也就是說當我們重寫定位創建函數
new()時,如果內存是在其中分配的,那麼就要提供相應的delete()函數給編譯器專用。反之則不必提供
delete()函數。
四、new(nothrow) MyClass操作符
從字面就可以看出來了,這個版本的new不會拋出異常。他也做三件事,其中第一件調用的就是不拋出異常的
operator new()函數,其原型是:
void *__cdecl operator new(size_t, const std::nothrow_t&)
_THROW0();
看到了,其後面的異常規範是空的,即不拋出任何異常。與常用的那個operator new()函數不同
void *__cdecl operator new(size_t) _THROW1(std::bad_alloc); ,這個可以拋出std::bad_alloc異常。
不允許拋出異常不併代其內部也不會發生異常,當內部內存分配失敗時發生異常,因爲規範中不允許拋出異常
,這就會拋出一個意外的異常,如果用戶不對這個意外異常進行捕捉的話,默認處理是調用abort函數。
五、 new const MyClass
這個版本的new表示在堆內存中創建後個const對象,創建完之後就不能再修改,所以必須在創建時初始化(有無
參構造函數的類除外),並且返回的指針是一個指向const對象的指針,即要寫:const int* p = new const
int(1); 而不能寫int* p = new const int(1).