本來想自己總結下operator new相關的,看到周星星同學已經總結得挺全了,直接拿來記錄下(后面綠色部分新加的)。
轉自http://blog.vckbase.com/bruceteen/archive/2009/05/27/37427.html
1:C++標準說:An allocation function shall be a class member function or a global function; a program is ill-formed if an allocation function is declared in a namespace scope other than global scope or declared static in global scope.
必須是全局函數或類成員函數,而不能是全局之外的名字空間或static全局函數。
2:new operator的行為
Foo* p = new Foo;
delete p;
我們知道,上面的代碼,也就是C++中的new操作符(new operator)大致會做下面的事情:
a.調用operator new, 給對象分配內存
b.調用Foo的構造函數
c.返回指針
...
d. 調用Foo的析構函數~Foo()
e. 調用operator delete釋放內存
更具體的, new operator的行為如下:
Foo* p = new(arg1,arg2,…… ) Foo(para1, para2, ...);
...
delete p;
編譯器將生成如下代碼:
調用 p = operator new( size_t 需要的大小,arg1,arg2,…… ); // 分配內存,這里有可能拋出std::bad_alloc,但無須在new operator中捕捉
如果構造Foo沒有拋出異常 // 即Foo的構造函數后面顯式的聲明了 throw()
在p指向處構造foo(para1,para2,……); // 調用Foo的構造函數
return p;
否則
try
{
在p指向處構造Foo(para1,para2,……);
return p;
}
catch(...)
{
調用 operator delete( void* p, arg1,arg2,…… );
throw;
}
...
調用 Foo的析構函數~Foo();
調用 operator delete( void* p );
從上面的步驟可以看出:
(1)對于operator new, 我們只要確保第一參數是表示申請內存的大小, 其他參數可以自己隨意重載
(2)只有Foo構造失?。嬙旌瘮祪葤伋霎惓#?,我們的operator delete( void* p, arg1,arg2,…… )才會被調用,否則只會調用operator delete( void* p )
3:全局形式的operator new偽代碼:
void* operator new( size_t size ) // 包括其他形式
{
if( 0 == size ) // 須要注意
size = 1;
while(1)
{
分配size字節內存;
if(分配成功)
return 指向內存的指針;
new_handler globalhandler = set_new_handler(0);
set_new_handler(globalhandler);
if( globalhandler )
(*globalhandler)();
else
throw std::bad_alloc();
}
}
void operator delete( void* raw )
{
if( 0 == raw ) // 須要注意
return;
...
}
須要說明的是,編譯器本身就隱含著一個 void* operator new( size_t ),所以重載全局operator new必須加其他參數以示區別。
一般重載分配函數時都會重載三個,分別是 void* operator new( size_t, …… ),void operator delete( void*, …… ),以及一般形式的 void operator delete( void* )。
4. set_new_handler的作用
set_new_handler設置一個函數,此函數將在分配內存失敗時被調用,見3中的代碼。
從3中的代碼還能看得出,new_handler必須有主動退出的功能,否則就會導致operator new內部死循環。因此newhandler的一般形式是:
void mynewhandler()
{
if( 有可能使得operator new成功(比如釋放部分內存) )
{
做有可能使得operator new成功的事
return;
}
// 主動退出
或 abort/exit 直接退出程序
或 set_new_handler(其他newhandler);
或 set_new_handler(0)
或 throw bad_alloc()或派生類 // 這一種比較好,不粗魯的關閉程序,也不更改其他設置
}
須要說明的是,沒有類形式的set_new_handler,但這也無所謂,你可以自己寫。(見《Effective C++ 2e》條款7)
5. 類形式的operator new偽代碼:
struct base
{
...
static void* operator new( size_t size );
static void operator delete( void* raw );
};
void* base::operator new( size_t size )
{
if( sizeof(base) != size ) // 須要注意
return ::operator new(size);
類似于3 // 注意“沒有類形式的set_new_handler”
}
void base::operator delete( void* raw )
{
if( sizeof(base) != size ) // 須要注意
{
::operator delete(raw);
return;
}
同3
}
void *operator new(std::size_t count, const std::nothrow_t&) throw(); //兼容早版本, new內存分配失敗不會拋出異常(nothrow new)
void *operator new(std::size_t count, void *ptr) throw(); //placement版本(placement new)
上面的方法我們可以這樣調用:
Foo* p = new Foo;
delete p;
Foo* p1 = new(std::nothrow) Foo;
delete p1;
Foo f;
Foo* p2 = new(&f) Foo;
針對數組則是:
void *operator new[](std::size_t count, const std::nothrow_t&) throw();
void *operator new[](std::size_t count, void *ptr) throw();
可以看到上面函數第一個都是對象空間大小,除了重載C++中operator new的標準類型,另外我們也可以重載其他類型的operator new, 比如
然后就可以這樣調用了: string str("abc"); Foo* p = new(str) Foo;
當然,如果我們自己重寫了operator new, 最好我們也重寫operator delete,這樣如果我們的構造函數里拋出異常,我們自己重寫的operator delete會被調用。(當然,如果構造對象成功,最后delete時只會調用operator delete( void* p ))
比如針對上面新加的operator new函數,新加operator delete如下:
void operator delete(void* p, const string& s) throw();
new operator就象sizeof一樣是語言內置的,我們不能改變它的含義,它的功能總是一樣的。它要完成的功能分成兩部分。第一部分是分配足夠的內存以便容納所需類型的對象。第二部分是它調用構造函數初始化內存中的對象。new operator總是做這兩件事情,你不能以任何方式改變它的行為。
我們所能改變的是如何為對象分配內存。new operator調用一個函數來完成必需的內存分配,你能夠重寫或重載這個函數來改變它的行為。new operator為分配內存所調用函數的名字是operator new。
與new operator/operator new相對應的是delete operator/operator delete, 當我們調用delete operator時,實際上包含析構函數調用和通過operator delete釋放內存2個階段。
我們可以單純的通過operator new 和 operator delete來分配和釋放內存:
void *buffer = operator new(50*sizeof(char)); // 內存以容納50個char, 沒有調用構造函數
operator delete(buffer); // 釋放內存, 沒有調用析構函數


