本來想自己總結下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
}
6. operator new的函數類型:
對我們來說一般有3種是語言要求的標準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內存分配失敗不會拋出異常(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;
p2->~Foo();
針對數組則是:
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();
可以看到上面函數第一個都是對象空間大小,除了重載C++中operator new的標準類型,另外我們也可以重載其他類型的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); 然后就可以這樣調用了: 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();void operator delete[](void* p, const string& s) throw();
可以看到,自己新加的operator delete, 只需確保第一個參數內存指針。
7. new operator和operator new的區別
new operator就象sizeof一樣是語言內置的,我們不能改變它的含義,它的功能總是一樣的。它要完成的功能分成兩部分。第一部分是分配足夠的內存以便容納所需類型的對象。第二部分是它調用構造函數初始化內存中的對象。new operator總是做這兩件事情,你不能以任何方式改變它的行為。
我們所能改變的是如何為對象分配內存。new operator調用一個函數來完成必需的內存分配,你能夠重寫或重載這個函數來改變它的行為。new operator為分配內存所調用函數的名字是operator new。
如果想在堆上建立一個對象,應該用new operator。它既分配內存又為對象調用構造函數。如果你僅僅想分配內存,就應該調用operator new函數;它不會調用構造函數。如果你想定制自己的在堆對象被建立時的內存分配過程,你應該寫你自己的operator new函數,然后使用new operator,new operator會調用你定制的operator new。如果你想在一塊已經獲得指針的內存里建立一個對象,應該用placement 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); // 釋放內存, 沒有調用析構函數
8. operator new的一些原則:
a. 一般不要重寫全局的operator new, 具體可以參考 不要重載全局 ::operator new
b. 如果重載了operator new, 同時提供所有版本(plain new, nothrow new, placement new)
c. 成對的提供new和delete, 即如果重載了operator new, 同時重載operator delete
posted on 2012-10-06 22:25
Richard Wei 閱讀(2761)
評論(0) 編輯 收藏 引用 所屬分類:
C++