日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

读书笔记 effective c++ Item 49 理解new-handler的行为

發(fā)布時(shí)間:2025/5/22 205 豆豆
生活随笔 收集整理的這篇文章主要介紹了 读书笔记 effective c++ Item 49 理解new-handler的行为 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. new-handler介紹

當(dāng)操作符new不能滿足內(nèi)存分配請(qǐng)求的時(shí)候,它就會(huì)拋出異常。很久之前,它會(huì)返回一個(gè)null指針,一些舊的編譯器仍然會(huì)這么做。你仍然會(huì)看到這種舊行為,但是我會(huì)把關(guān)于它的討論推遲到本條款結(jié)束的時(shí)候。

1.1 調(diào)用set_new_handler來指定全局new-handler

在operator new由于不能滿足內(nèi)存分配要求而拋出異常之前,它會(huì)調(diào)用一個(gè)客戶指定的叫做new-handler的錯(cuò)誤處理函數(shù)。(這也不是完全正確的。Operator new的真正行為更加復(fù)雜。詳細(xì)內(nèi)容在Item 51中描述。)為了指定內(nèi)存溢出處理(out-of-memory-handling)函數(shù),客戶可以調(diào)用set_new_handler函數(shù),這個(gè)標(biāo)準(zhǔn)庫(kù)函數(shù)被聲明在<new>中:

1 namespace std { 2 typedef void (*new_handler)(); 3 new_handler set_new_handler(new_handler p) throw(); 4 }

?

正如你所看到的,new_handler是一個(gè)函數(shù)指針的typedef,這個(gè)函數(shù)沒有參數(shù)沒有返回值,set_new_handler是一個(gè)參數(shù)和返回值都為new_handler的函數(shù)。(函數(shù)set_new_handler聲明結(jié)束處的”throw()”是一個(gè)異常指定(exception specification)。從本質(zhì)上來說它的意思是說這個(gè)函數(shù)不會(huì)拋出任何異常,然而事實(shí)更加有意思。詳細(xì)內(nèi)容見Item 29。)

set_new_handler的參數(shù)是指向函數(shù)的指針,operator new會(huì)在請(qǐng)求的內(nèi)存無法分配的情況下調(diào)用這個(gè)函數(shù)。Set_new_handler的返回值也是指向函數(shù)的指針,返回的是在調(diào)用set_new_handler之前調(diào)用的new_handler函數(shù)(也就是在new_handler被替換之前的函數(shù))。

你可以像下面這樣使用set_new_handler:

1 // function to call if operator new can’t allocate enough memory 2 void outOfMem() 3 { 4 std::cerr << "Unable to satisfy request for memory\n"; 5 std::abort(); 6 } 7 8 int main() 9 { 10 std::set_new_handler(outOfMem); 11 int *pBigDataArray = new int[100000000L]; 12 ... 13 }

?

如果operaotr new無法為100,000,000個(gè)整數(shù)分配內(nèi)存,就會(huì)調(diào)用outOfMem,也就是輸出一個(gè)error信息之后程序終止(abort)。(順便說一下,考慮在向cerr中寫入error信息期間如果必須動(dòng)態(tài)的分配內(nèi)存會(huì)發(fā)生什么。。)

1.2 如何設(shè)計(jì)一個(gè)良好的new-handler函數(shù)

當(dāng)operator new不能滿足一個(gè)內(nèi)存請(qǐng)求的時(shí)候,它會(huì)反復(fù)調(diào)用new-handler函數(shù)直到它發(fā)現(xiàn)有足夠的內(nèi)存可以分配了。引起這些函數(shù)被反復(fù)調(diào)用的代碼在Item 51中可以找到,但是這種高級(jí)別的描述信息足夠讓我們得出結(jié)論:一個(gè)設(shè)計(jì)良好的new-handler函數(shù)必須能夠做到如下幾點(diǎn)。

  • 提供更多的可被使用的內(nèi)存。這可以保證下次在operator new內(nèi)部嘗試分配內(nèi)存時(shí)能夠成功。實(shí)現(xiàn)這個(gè)策略的一種方法是在程序的開始階段分配一大塊內(nèi)存,然后在第一次調(diào)用new-handler的時(shí)候釋放它。
  • 安裝一個(gè)不同的new-handler。如果當(dāng)前的new-handler不能夠?yàn)槟闾峁└嗟膬?nèi)存,可能另外一個(gè)new-handler可以。如果是這樣,可以在當(dāng)前的new-handler的位置上安裝另外一個(gè)new-handler(通過調(diào)用set_new_handler)。下次operator new調(diào)用new-handler函數(shù)的時(shí)候,它會(huì)調(diào)用最近安裝的。(這個(gè)主題的一個(gè)變種是一個(gè)使用new_handler來修改它自己的行為,所以在下次觸發(fā)這個(gè)函數(shù)的時(shí)候,它就會(huì)做一些不同的事情。達(dá)到這個(gè)目的的一個(gè)方法是讓new_handler修改影響new-handler行為的static數(shù)據(jù),命名空間數(shù)據(jù)或者全局?jǐn)?shù)據(jù)。)
  • 卸載new-handler,也就是為set_new_handler傳遞null指針。如果沒有安裝new-handler,operator ?new在內(nèi)存分配失敗的時(shí)候會(huì)拋出異常。
  • 沒有返回值,調(diào)用abort或者exit。

這些選擇讓你在實(shí)現(xiàn)new-handler的時(shí)候有相當(dāng)大的靈活性。

2. 為特定類指定new-handler

有時(shí)候你想用不同方式來處理內(nèi)存分配失敗,這依賴于需要分配內(nèi)存的對(duì)象所屬的類:

1 class X { 2 public: 3 static void outOfMemory(); 4 ... 5 }; 6 class Y { 7 public: 8 static void outOfMemory(); 9 ... 10 }; 11 X* p1 = new X; // if allocation is unsuccessful, 12 // call X::outOfMemory 13 Y* p2 = new Y; // if allocation is unsuccessful, 14 // call Y::outOfMemory

?

C++沒有為類提供指定的new-handlers,但也不需要。你可以自己實(shí)現(xiàn)這種行為。你可以使每個(gè)類提供自己版本的set_new_handler和operator new。類中的set_new_handler允許客戶為類提供new_handler(就像標(biāo)準(zhǔn)的set_new_handler允許客戶指定全局的new-handler一樣)。類的operator new確保為類對(duì)象分配內(nèi)存時(shí),會(huì)使用其指定的new-handler來替代全局new-handler。

2.1 在類中聲明static new_handler成員

假設(shè)你想對(duì)Widget類對(duì)象的內(nèi)存分配失敗做一下處理。當(dāng)operator new不能為Widget對(duì)象分配足夠的內(nèi)存的時(shí)候你必須跟蹤一下函數(shù)調(diào)用過程,所以你要聲明一個(gè)類型為new_handler的static成員,來指向這個(gè)類的new-handler函數(shù)。Widget將會(huì)是下面這個(gè)樣子:

1 class Widget { 2 public: 3 static std::new_handler set_new_handler(std::new_handler p) throw(); 4 static void* operator new(std::size_t size) throw(std::bad_alloc); 5 private: 6 static std::new_handler currentHandler; 7 };

?

靜態(tài)類成員必須在類外部定義(除非他們是const整型,見Item 2),所以:

1 std::new_handler Widget::currentHandler = 0; // init to null in the class 2 // impl. File

?

Widget中的set_new_handler函數(shù)會(huì)把傳遞進(jìn)去的指針(所指向的new-handler函數(shù))保存起來,并且會(huì)返回調(diào)用set_new_handler之前所保存的指針。這也是標(biāo)準(zhǔn)版本set_new_handler的做法:

1 std::new_handler Widget::set_new_handler(std::new_handler p) throw() 2 { 3 std::new_handler oldHandler = currentHandler; 4 currentHandler = p; 5 return oldHandler; 6 }

?

2.2 重新定義operator new

最后,Widget的operator new將會(huì)做下面的事情:

  • 調(diào)用標(biāo)準(zhǔn)set_new_handler,參數(shù)為Widget的錯(cuò)誤處理函數(shù)。這就將Widget的new-handler安裝成為了全局的new-handler。
  • 調(diào)用全局的operator new來執(zhí)行實(shí)際的內(nèi)存分配。如果分配失敗,全局的operator new會(huì)觸發(fā)Widget的new-handler,因?yàn)檫@個(gè)函數(shù)已經(jīng)被安裝為全局new-handler。如果全局的operator new最終不能分配內(nèi)存,它會(huì)拋出bad_alloc異常。在這種情況下,Widget的operator new必須恢復(fù)原來的全局new-handler,然后傳播異常。為了確保源new-handler總是能被恢復(fù),Widget將全局new-handler作為資源來處理,遵循Item 13的建議,使用資源管理對(duì)象來防止資源泄漏。
  • 如果全局operator new能夠?yàn)閃idget對(duì)象分配足夠的內(nèi)存。Widget的operator new就會(huì)返回指向被分配內(nèi)存的指針。管理全局new-handler的對(duì)象的析構(gòu)函數(shù)會(huì)自動(dòng)恢復(fù)調(diào)用Widget的operator new之前的new-handler。
  • 這里我們以資源處理(resource-handling)類開始,只包含基本的RAII處理操作,包括在構(gòu)造時(shí)獲取資源和在在析構(gòu)時(shí)釋放資源(Item 13):

    1 class NewHandlerHolder { 2 public: 3 explicit NewHandlerHolder(std::new_handler nh) // acquire current 4 : handler(nh) {} // new-handler 5 6 ~NewHandlerHolder() // release it 7 8 { std::set_new_handler(handler); } 9 10 private: 11 12 13 14 std::new_handler handler; // remember it 15 16 NewHandlerHolder(const NewHandlerHolder&); // prevent copying 17 18 19 20 NewHandlerHolder& // (see Item 14) 21 22 operator=(const NewHandlerHolder&); 23 24 };

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

    ?這會(huì)使得Widget的operator new的實(shí)現(xiàn)非常簡(jiǎn)單:

    1 void* Widget::operator new(std::size_t size) throw(std::bad_alloc) 2 { 3 NewHandlerHolder // install Widget’s 4 h(std::set_new_handler(currentHandler)); // new-handler 5 6 return ::operator new(size); // allocate memory 7 // or throw 8 9 } // restore global 10 // new-handler 11 12 13 14 void outOfMem(); // decl. of func. to call if mem. alloc. 15 // for Widget objects fails 16 17 Widget::set_new_handler(outOfMem); // set outOfMem as Widget’s 18 // new-handling function 19 20 Widget *pw1 = new Widget; // if memory allocation 21 // fails, call outOfMem 22 23 std::string *ps = new std::string; // if memory allocation fails, 24 // call the global new-handling 25 // function (if there is one) 26 27 Widget::set_new_handler(0); // set the Widget-specific 28 // new-handling function to 29 // nothing (i.e., null) 30 31 Widget *pw2 = new Widget; // if mem. alloc. fails, throw an 32 // exception immediately. (There is 33 // no new- handling function for 34 // class Widget.)

    ?

    2.3 將NewHandlerHolder轉(zhuǎn)換為模板

    不管在什么類中,實(shí)現(xiàn)的這個(gè)主題的代碼都是一樣的,所以我們可以為其設(shè)一個(gè)合理的目標(biāo),就是代碼能夠在其他地方重用。達(dá)到這個(gè)目標(biāo)的一個(gè)簡(jiǎn)單方法是創(chuàng)建一個(gè)“混合風(fēng)格(mixin-style)”的基類,也就是設(shè)計(jì)一個(gè)基類,允許派生類繼承單一特定的能力——在這個(gè)例子中,這種能力就是為類指定new-handler。然后將基類變?yōu)橐粋€(gè)模板,于是你可以為每個(gè)繼承類獲得一份不同的類數(shù)據(jù)的拷貝。

    這個(gè)設(shè)計(jì)的基類部分使得派生類能夠繼承它們都需要的set_new_handler和operator new函數(shù),同時(shí)設(shè)計(jì)的模板部分確保每個(gè)繼承類獲得一個(gè)不同的currentHandler數(shù)據(jù)成員。說起來有些復(fù)雜,但是代碼看上去很熟悉。事實(shí)上,唯一真正不一樣的是現(xiàn)在任何類都能夠獲得這個(gè)功能:

    1 template<typename T> // “mixin-style” base class for 2 class NewHandlerSupport { // class-specific set_new_handler 3 public: // support 4 static std::new_handler set_new_handler(std::new_handler p) throw(); 5 static void* operator new(std::size_t size) throw(std::bad_alloc); 6 ... // other versions of op. new — 7 // see Item 52 8 private: 9 static std::new_handler currentHandler; 10 }; 11 template<typename T> 12 std::new_handler 13 NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw() 14 { 15 std::new_handler oldHandler = currentHandler; 16 currentHandler = p; 17 return oldHandler; 18 } 19 template<typename T> 20 void* NewHandlerSupport<T>::operator new(std::size_t size) 21 throw(std::bad_alloc) 22 { 23 NewHandlerHolder h(std::set_new_handler(currentHandler)); 24 return ::operator new(size); 25 } 26 // this initializes each currentHandler to null 27 template<typename T> 28 std::new_handler NewHandlerSupport<T>::currentHandler = 0;

    ?

    有了這個(gè)類模板之后,向Widget中添加set_new_handler支持就變得容易了:Widget只需要繼承自NewHandlerSupport<Widget>。(這可能看上去比較獨(dú)特,接下來我會(huì)進(jìn)行詳細(xì)的解釋。)

    1 class Widget: public NewHandlerSupport<Widget> { 2 ... // as before, but without declarations for 3 4 }; // set_new_handler or operator new

    ?

    這是Widget提供一個(gè)特定的set_new_handler需要做的所有事情。

    但是對(duì)于Widget繼承自NewHandlerSupport<Widget>,你可能還是有些不安。如果是這樣,當(dāng)你注意到NewHandlerSupport模板永遠(yuǎn)不會(huì)使用類型參數(shù)T之后你的不安可能會(huì)加劇。你沒有必要這樣。對(duì)于每個(gè)繼承自NewHandlerSupport的類來說,我們所有需要的是一份不同的NewHandlerSupport的拷貝——特別是靜態(tài)數(shù)據(jù)成員currentHandler的不同拷貝。模板機(jī)制自身會(huì)為每個(gè)T自動(dòng)生成currentHandler的一份拷貝,NewHandlerSupport使用這個(gè)T來進(jìn)行實(shí)例化。

    對(duì)于Widget繼承自一個(gè)使用Widget作為類型參數(shù)的模板基類來說,如果這個(gè)概念讓你感覺眩暈,不要感覺不好。每個(gè)人看到開始看到它的時(shí)候都會(huì)有這種感覺。但是,它是非常有用的技術(shù),它有一個(gè)名字,這個(gè)名字如果這個(gè)概念一樣,第一次看到它的人沒有人會(huì)感覺它很自然,它叫做怪異的循環(huán)模板模式(curiously recurring template pattern CRTP)。

    我曾經(jīng)寫過一遍文章建議為它起一個(gè)更好的名字:do it for me,因?yàn)楫?dāng)Widget繼承自NewHandlerSupport<Widget>,它真的像是在說:“我是Widget,我需要為Widget繼承NewHandlerSupport類“。沒有人使用我建議的名字,但是使用“do it for me”來想象一下CRTP可能會(huì)幫助你理解模板化的繼承會(huì)做什么。

    有了像NewHandlerSupport這樣的模板,為任何需要new-hadler的類添加一個(gè)特定的new-handler就會(huì)變得容易。混合風(fēng)格的繼承總是會(huì)將你引入多繼承的主題,在開始進(jìn)入這個(gè)主題之前,你可能想讀一下Item 40。

    3. Nothrow版本的new

    直到1993年,當(dāng)不能滿足分配內(nèi)存的要求時(shí),C++要求operator new要返回null。現(xiàn)在指定operator new要拋出bad_alloc異常,但是大量的C++是在編譯器支持修訂版本之前寫出來的。C++標(biāo)準(zhǔn)委員會(huì)也不想廢棄test-for-null的代碼,所以它們?yōu)閛perator new提供了一種替代形式,它能夠提供傳統(tǒng)的“失敗產(chǎn)生null(failure-yields-null)”行為。這些形式被叫做“nothrow”形式,某種程度上是因?yàn)樗麄兪褂昧瞬粫?huì)拋出異常的對(duì)象(定義在頭文件<new>中),new在這種情況下被使用:

    1 class Widget { ... }; 2 Widget *pw1 = new Widget; // throws bad_alloc if 3 // allocation fails 4 5 if (pw1 == 0) ... // this test must fail 6 7 Widget *pw2 = new (std::nothrow) Widget; // returns 0 if allocation for 8 // the Widget fails 9 10 if (pw2 == 0) ... // this test may succeed

    ?

    nothrow版本的new不會(huì)像從表面上看起來這樣可靠,對(duì)于異常它沒有提供讓人信服的保證。對(duì)于表達(dá)式“new (std::nothrow) Widget”,會(huì)發(fā)生兩件事情。首先,通過調(diào)用nothrow版本的operator new來為一個(gè)Widget 對(duì)象分配足夠的內(nèi)存。如果分配失敗了,operator new會(huì)返回null指針。然而如果分配成功了,Widget構(gòu)造函數(shù)會(huì)被調(diào)用,到這個(gè)時(shí)候,就會(huì)世事難料了。Widget構(gòu)造函數(shù)能夠做任何它想做的。它自己可能new一些內(nèi)存,如果是這樣,并沒有強(qiáng)迫它使用nothrow版本的new。雖然在”new (std::nothrow) Widget”中的operator new不會(huì)拋出異常,但是Widget構(gòu)造函數(shù)卻可能拋出來。如果是這樣,異常會(huì)像平時(shí)一樣傳播出去。結(jié)論是什么?使用nothrow new只能保證operator new不會(huì)拋出異常,不能保證像“new(std::nothrow) Widget”這樣的表達(dá)式不拋出異常。十有八九,你將永遠(yuǎn)不會(huì)有使用nothrow new的需要。

    不論你是使用”普通的”(也就是拋出異常的)new還是nothrow版本的new,重要的是你需要明白new-handler的行為,因?yàn)樵趦煞Nnew中都會(huì)使用到它。

    4. 總結(jié)

    • Set_new_handler允許你在分配內(nèi)存不能滿足要求的時(shí)候指定一個(gè)特定的被調(diào)用的函數(shù)。
    • Nothrow new功能有限,因?yàn)樗荒鼙粦?yīng)用在內(nèi)存分配上;相關(guān)聯(lián)的構(gòu)造函數(shù)調(diào)用可能仍然會(huì)拋出異常。


    作者: HarlanC

    博客地址: http://www.cnblogs.com/harlanc/
    個(gè)人博客: http://www.harlancn.me/
    本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出, 原文鏈接

    如果覺的博主寫的可以,收到您的贊會(huì)是很大的動(dòng)力,如果您覺的不好,您可以投反對(duì)票,但麻煩您留言寫下問題在哪里,這樣才能共同進(jìn)步。謝謝!

    總結(jié)

    以上是生活随笔為你收集整理的读书笔记 effective c++ Item 49 理解new-handler的行为的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。