本來想自己總結(jié)下operator new相關(guān)的,看到周星星同學(xué)已經(jīng)總結(jié)得挺全了,直接拿來記錄下(后面綠色部分新加的)。
轉(zhuǎn)自http://blog.vckbase.com/bruceteen/archive/2009/05/27/37427.html
1:C++標(biāo)準(zhǔn)說: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.
必須是全局函數(shù)或類成員函數(shù),而不能是全局之外的名字空間或static全局函數(shù)。
2:new operator的行為
Foo* p = new Foo;
...delete p;我們知道,上面的代碼,也就是C++中的new操作符(new operator)大致會(huì)做下面的事情:a.調(diào)用operator new, 給對象分配內(nèi)存b.調(diào)用Foo的構(gòu)造函數(shù)c.返回指針...d. 調(diào)用Foo的析構(gòu)函數(shù)~Foo()e. 調(diào)用operator delete釋放內(nèi)存更具體的, new operator的行為如下:對于如下代碼:
Foo* p = new(arg1,arg2,…… ) Foo(para1, para2, ...);
...
delete p;
編譯器將生成如下代碼:
調(diào)用 p = operator new( size_t 需要的大小,arg1,arg2,…… ); // 分配內(nèi)存,這里有可能拋出std::bad_alloc,但無須在new operator中捕捉
如果構(gòu)造Foo沒有拋出異常 // 即Foo的構(gòu)造函數(shù)后面顯式的聲明了 throw()
在p指向處構(gòu)造foo(para1,para2,……); // 調(diào)用Foo的構(gòu)造函數(shù)
return p;
否則
try
{
在p指向處構(gòu)造Foo(para1,para2,……);
return p;
}
catch(...)
{
調(diào)用 operator delete( void* p, arg1,arg2,…… );
throw;
}
...
調(diào)用 Foo的析構(gòu)函數(shù)~Foo();
調(diào)用 operator delete( void* p );
從上面的步驟可以看出:
(1)對于operator new, 我們只要確保第一參數(shù)是表示申請內(nèi)存的大小, 其他參數(shù)可以自己隨意重載
(2)只有Foo構(gòu)造失敗(構(gòu)造函數(shù)內(nèi)拋出異常),我們的operator delete( void* p, arg1,arg2,…… )才會(huì)被調(diào)用,否則只會(huì)調(diào)用operator delete( void* p )
3:全局形式的operator new偽代碼:
void* operator new( size_t size ) // 包括其他形式
{
if( 0 == size ) // 須要注意
size = 1;
while(1)
{
分配size字節(jié)內(nèi)存;
if(分配成功)
return 指向內(nèi)存的指針;
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;
...
}
須要說明的是,編譯器本身就隱含著一個(gè) void* operator new( size_t ),所以重載全局operator new必須加其他參數(shù)以示區(qū)別。
一般重載分配函數(shù)時(shí)都會(huì)重載三個(gè),分別是 void* operator new( size_t, …… ),void operator delete( void*, …… ),以及一般形式的 void operator delete( void* )。
4. set_new_handler的作用
set_new_handler設(shè)置一個(gè)函數(shù),此函數(shù)將在分配內(nèi)存失敗時(shí)被調(diào)用,見3中的代碼。
從3中的代碼還能看得出,new_handler必須有主動(dòng)退出的功能,否則就會(huì)導(dǎo)致operator new內(nèi)部死循環(huán)。因此newhandler的一般形式是:
void mynewhandler()
{
if( 有可能使得operator new成功(比如釋放部分內(nèi)存) )
{
做有可能使得operator new成功的事
return;
}
// 主動(dòng)退出
或 abort/exit 直接退出程序
或 set_new_handler(其他newhandler);
或 set_new_handler(0)
或 throw bad_alloc()或派生類 // 這一種比較好,不粗魯?shù)年P(guān)閉程序,也不更改其他設(shè)置
}
須要說明的是,沒有類形式的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
}
6. operator new的函數(shù)類型:
對我們來說一般有3種是語言要求的標(biāo)準(zhǔn)operator new(plain new, nothrow new, placement new):
void *operator new(std::size_t count) throw(std::bad_alloc); //一般的版本(plain new)
void *operator new(std::size_t count, const std::nothrow_t&) throw(); //兼容早版本, new內(nèi)存分配失敗不會(huì)拋出異常(nothrow new)
void *operator new(std::size_t count, void *ptr) throw(); //placement版本(placement new)
上面的方法我們可以這樣調(diào)用:
Foo* p = new Foo;
delete p;
Foo* p1 = new(std::nothrow) Foo;
delete p1;
Foo f;
Foo* p2 = new(&f) Foo;
p2->~Foo();
針對數(shù)組則是:
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();
可以看到上面函數(shù)第一個(gè)都是對象空間大小,除了重載C++中operator new的標(biāo)準(zhǔn)類型,另外我們也可以重載其他類型的operator new, 比如
void *operator new(std::size_t count, const string& s) throw(std::bad_alloc);
void *operator new[](std::size_t count, const string& s) throw(std::bad_alloc); 然后就可以這樣調(diào)用了: string str("abc"); Foo* p = new(str) Foo;當(dāng)然,如果我們自己重寫了operator new, 最好我們也重寫operator delete,這樣如果我們的構(gòu)造函數(shù)里拋出異常,我們自己重寫的operator delete會(huì)被調(diào)用。(當(dāng)然,如果構(gòu)造對象成功,最后delete時(shí)只會(huì)調(diào)用operator delete( void* p ))
比如針對上面新加的operator new函數(shù),新加operator delete如下:void operator delete(void* p, const string& s) throw();void operator delete[](void* p, const string& s) throw();
可以看到,自己新加的operator delete, 只需確保第一個(gè)參數(shù)內(nèi)存指針。
7. new operator和operator new的區(qū)別
new operator就象sizeof一樣是語言內(nèi)置的,我們不能改變它的含義,它的功能總是一樣的。它要完成的功能分成兩部分。第一部分是分配足夠的內(nèi)存以便容納所需類型的對象。第二部分是它調(diào)用構(gòu)造函數(shù)初始化內(nèi)存中的對象。new operator總是做這兩件事情,你不能以任何方式改變它的行為。
我們所能改變的是如何為對象分配內(nèi)存。new operator調(diào)用一個(gè)函數(shù)來完成必需的內(nèi)存分配,你能夠重寫或重載這個(gè)函數(shù)來改變它的行為。new operator為分配內(nèi)存所調(diào)用函數(shù)的名字是operator new。
如果想在堆上建立一個(gè)對象,應(yīng)該用new operator。它既分配內(nèi)存又為對象調(diào)用構(gòu)造函數(shù)。如果你僅僅想分配內(nèi)存,就應(yīng)該調(diào)用operator new函數(shù);它不會(huì)調(diào)用構(gòu)造函數(shù)。如果你想定制自己的在堆對象被建立時(shí)的內(nèi)存分配過程,你應(yīng)該寫你自己的operator new函數(shù),然后使用new operator,new operator會(huì)調(diào)用你定制的operator new。如果你想在一塊已經(jīng)獲得指針的內(nèi)存里建立一個(gè)對象,應(yīng)該用placement new。
與new operator/operator new相對應(yīng)的是delete operator/operator delete, 當(dāng)我們調(diào)用delete operator時(shí),實(shí)際上包含析構(gòu)函數(shù)調(diào)用和通過operator delete釋放內(nèi)存2個(gè)階段。
我們可以單純的通過operator new 和 operator delete來分配和釋放內(nèi)存:
void *buffer = operator new(50*sizeof(char)); // 內(nèi)存以容納50個(gè)char, 沒有調(diào)用構(gòu)造函數(shù) ...
operator delete(buffer); // 釋放內(nèi)存, 沒有調(diào)用析構(gòu)函數(shù)
8. operator new的一些原則:
a. 一般不要重寫全局的operator new, 具體可以參考 不要重載全局 ::operator new
b. 如果重載了operator new, 同時(shí)提供所有版本(plain new, nothrow new, placement new)
c. 成對的提供new和delete, 即如果重載了operator new, 同時(shí)重載operator delete
posted on 2012-10-06 22:25
Richard Wei 閱讀(2764)
評論(0) 編輯 收藏 引用 所屬分類:
C++