條款49中關於new handler行為

2021-07-08 13:32:57 字數 3673 閱讀 9162

當operator new無法滿足某一記憶體分配時,就會丟擲一次。以前它會返回null指標,現在某些舊式編譯器也還這麼做。

namespace std

new_handler是個函式指標,該函式沒有引數也不返回任何東西。set_new_handler是設定乙個new_handler並返回乙個new_handler函式,返回的new_handler是指向set_new_handler被呼叫前正在執行的那個new-handler函式。後面的throw是乙份異常明細,表示該函式不丟擲異常。可以這樣使用

#include using namespace std;

void outofmem()

int main()

設計良好的new-handler必須做好以下事情:

有時候,我們希望處理記憶體分配失敗的情況和class相關。例如

class x;

class y;

x* p1=new x;//分配不成功,呼叫x::outofmemory

y* p2=new y;//分配不成功,呼叫y::outofmemory

c++並不支援class專屬的new-handler,但是我們自己可以實現這種行為。令每乙個class提供自己的set_new_handler和operator new即可。……………………………………………………………………………………

現在打算處理widget class記憶體分配失敗的情況。首先要有乙個operator new無法為widget分配足夠記憶體時的呼叫函式,即new_handler函式

class widget;

std::new_handler widget::currenthandler=0;

std::new_handler widget::set_new_handler(std::new_handler p) throw()

widget的operator new做以下事情: 

1、呼叫標準set_new_handler,告知widget錯誤處理函式。這會將widget的new-handler安裝為global new-handler。 

2、呼叫global operator new,如果失敗,global operator new會呼叫widget的new-handler,因為第一步。如果global operator new最終無法分配足夠記憶體,會丟擲乙個bad_alloc異常。這時widget的operator new要恢復原本的global new-handler,之後在傳播異常。 

3、如果global operator new呼叫成功,widget的operator new會返回乙個指標,指向分配的記憶體。widget析構函式會管理global new-handler,它會將widget』s operator new被呼叫前的那個global new-handler恢復回來。

class newhandlerholder

~newhandlerholder()

private:

std::new_handler handler;

newhandlerholder&(const newhandlerholder&);//防止copying

newhandlerholder& operator-(const newhandlerholder&);

};

這使得widget』s operator new的實現變得簡單

void* widget::operator new(std::size_t size) throw(std::bad_alloc)

widget客戶應該類似這樣使用其new-handling

void outofmem();

widget::set_new_handler(outofmem);//設定outofmem為widget的new-handling函式

widget* pw1=new widget;//記憶體分配失敗,則呼叫outofmem

std::string* ps=new std::string;//記憶體分配失敗則呼叫global new-handling(如果有)

widget::set_new_handler(0);//設定widget專屬new-handling為null

widget* pw2=new widget;//記憶體分配失敗則立刻丟擲異常

實現這個方案的class**基本相同,用個基類base class加以復用是個好的方法。可以用個template base class,如此以來每個derived class將獲得實體互異的class data復件。這個base class讓其derived class繼承它獲取set_new_handler和operator new,template部分確保每乙個derived class獲得乙個實體互異的currenthandler成員變數。

templateclass newhandlersupport;

templatestd::new_handler

newhandlersupport::set_new_handler(std::new_handler p) throw()

templatevoid* newhandlersupport::operator new(std::size_t size)

throw(std::bad_alloc)

//將每乙個currenthandler初始化為null

templatestd::new_handler newhandlersupport::currenthandler=0;

有了這個class template,為widget新增set_new_handler就容易了

class widget:public newhandlersupport;

在template base class中,從未使用型別t。因為currenthandler是static型別,使用模板的話會是每個class都有自己的currenthandler。如果使用多重繼承,要注意**條款**40所提到的內容。

c++中operator new分配失敗丟擲異常bad_alloc,但是舊標準是返回null指標。舊標準這個形式為nothrow形式。

class widget{};

widget* pw1=new widget;//分配失敗,丟擲bad_alloc

if(pw1==null)//判斷是否分配成功。但是這個測試失敗

widget* pw2=new(std::nothrow)widget;//分配失敗,返回null

if(pw2==null)//可以偵測

new(std::nothrow) widget發生兩件事,第一分配記憶體給widget物件,如果失敗返回null指標。第二,如果成功,呼叫widget的建構函式,但是在這個建構函式做什麼,nothrow new並不知情,有可能再次開闢記憶體。如果在建構函式使用operator new開闢記憶體,那麼還是有可能丟擲異常並傳播。使用nothrow new只能保證operator new不丟擲異常,不能保證像new(std::nothrow) widget這樣的表示式不丟擲異常。所以,並沒有運用nothrow的需要。總結

條款49 了解new handle行為

多執行緒下的記憶體管理與單執行緒下是完全不同的,因為heap是乙個可以被全域性改動的資源,所以所有的執行緒都有可能去訪問這一資源,這回導致很多的race conditions。當operator new未取得想要的記憶體的時候,會呼叫乙個使用者指定的處理函式,new handler。這個函式可以使用...

條款49 盡量讓自己熟悉C 標準庫

1,標準庫的每一樣東西幾乎都在namespace std中,然而世界上有無可計數的c 程式 依賴那些已經使用多年的 準 標準程式庫,例如宣告於,等檔案中的功能。2,解決方法,設計新的頭檔名,給披上std外衣的各個元件使用。1 將原有的c 標頭檔案中的.h擴充套件拿掉,於是變為。如 cin的型別 ba...

49 shell中的冒號

1 表示永真,相當於true關鍵字。colon.sh指令碼利用while迴圈列印1到10的整數,while迴圈的條件使用了冒號,此時冒號就表示永真,即while迴圈永遠執行下去,while迴圈體內使用if then結構判斷跳出while迴圈的條件。1 bin bash 2 3 i 0 4 while...