亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

C++箴言:理解 new-handler的行為

2019-11-17 05:18:01
字體:
來源:轉載
供稿:網友

  當 Operator new 不能滿足一個內存分配請求時,它拋出一個 exception(異常)。很久以前,他返回一個 null pointer(空指針),而一些比較老的編譯器還在這樣做。你依然能達到以前的目的(在一定程度上),但是我要到本文的最后再討論它。

  在 operator new 因回應一個無法滿足的內存請求而拋出一個 exception 之前,它先調用一個可以由客戶指定的被稱為 new-handler 的 error-handling function(錯誤處理函數)。(這并不完全確切,operator new 真正做的事情比這個稍微復雜一些,具體細節將在下一篇文章中討論。)為了指定 out-of-memory-handling function,客戶調用 set_new_handler ——一個在 <new> 中聲明的標準庫函數:

namespace std {
 typedef void (*new_handler)();
 new_handler set_new_handler(new_handler p) throw();
}
  就像你能夠看到的,new_handler 是一個指針的 typedef,這個指針指向不取得和返回任何東西的函數,而 set_new_handler 是一個取得和返回一個 new_handler 的函數。(set_new_handler 的聲明的結尾處的 "throw()" 是一個 exception specification(異常規范)。它基本上是說這個函數不會拋出任何異常,盡管真相更有趣一些。關于細節,參見《C++箴言:爭取異常安全的代碼》。)

  set_new_handler 的形參是一個指向函數的指針,這個函數是 operator new 無法分配被請求的內存時應該調用的。set_new_handler 的返回值是一個指向函數的指針,這個函數是 set_new_handler 被調用前有效的目標。

  你可以像這樣使用 set_new_handler:

// function to call if operator new can't allocate enough memory
void outOfMem()
{
 std::cerr << "Unable to satisfy request for memory/n";
 std::abort();
}
int main()
{
 std::set_new_handler(outOfMem);
 int *pBigDataArray = new int[100000000L];
 ...
}
  假如 operator new 不能為 100,000,000 個整數分配空間,outOfMem 將被調用,而程序將在發出一個錯誤信息后中止。(順便說一句,考慮假如在寫這個錯誤信息到 cerr... 的過程中內存必須被動態分配會發生什么。)

  當 operator new 不能滿足一個內存請求時,它反復調用 new-handler function 直到它能找到足夠的內存。但是從這種高層次的描述已足夠推導出一個設計得好的 new-handler function 必須做到以下事情之一:

  ·Make more memory available(使得更多的內存可用)。這可能使得 operator new 中下一次內存分配的嘗試成功。實現這一策略的一個方法是在程序啟動時分配一大塊內存,然后在 new-handler 第一次被調用時釋放它供程序使用。

  ·Install a different new-handler(安裝一個不同的 new-handler)。假如當前的 new-handler 不能做到使更多的內存可用,或許它知道有一個不同的 new-handler 可以做到。假如是這樣,當前的 new-handler 能在它自己的位置上安裝另一個 new-handler(通過調用 set_new_handler)。operator new 下一次調用 new-handler function 時,它會得到最近安裝的那一個。(這個主線上的一個變化是讓一個 new-handler 改變它自己的行為,這樣,下一次它被調用時,可以做一些不同的事情。做到這一點的一個方法是讓 new-handler 改變能影響 new-handler 行為的 static(靜態),namespace-specific(名字空間專用)或 global(全局)的數據。)

  ·Deinstall the new-handler(卸載 new-handler),也就是,將空指針傳給 set_new_handler。沒有 new-handler 被安裝,當內存分配沒有成功時,operator new 拋出一個異常。

  ·Throw an exception(拋出一個異常),類型為 bad_alloc 或繼續自 bad_alloc 的其它類型。這樣的異常不會被 operator new 捕捉,所以它們將被傳播到發出內存請求的地方。

  ·Not return(不再返回),典型情況下,調用 abort 或 exit。

  這些選擇使你在實現 new-handler functions 時擁有極大的彈性。

  有時你可能希望根據被分配 object 的不同,用不同的方法處理內存分配的失敗:

class X {
public:
 static void outOfMemory();
 ...
};
class Y {
public:
 static void outOfMemory();
 ...
};
X* p1 = new X; // if allocation is unsUCcessful,
// call X::outOfMemory

Y* p2 = new Y; // if allocation is unsuccessful,
// call Y::outOfMemory
  C++ 沒有對 class-specific new-handlers 的支持,但是它也不需要。你可以自己實現這一行為。你只要讓每一個 class 提供 set_new_handler 和 operator new 的它自己的版本即可。class 的 set_new_handler 答應客戶為這個 class 指定 new-handler(正像standard set_new_handler 答應客戶指定global new-handler)。class 的 operator new 確保當為 class objects 分配內存時,class-specific new-handler 代替 global new-handler 被使用。

  假設你要為 Widget class 處理內存分配失敗。你就必須清楚當 operator new 不能為一個 Widget object 分配足夠的內存時所調用的函數,所以你需要聲明一個 new_handler 類型的 static member(靜態成員)指向這個 class 的 new-handler function。Widget 看起來就像這樣:


class Widget {
public:
 static std::new_handler set_new_handler(std::new_handler p) throw();
 static void * operator new(std::size_t size) throw(std::bad_alloc);
PRivate:
 static std::new_handler currentHandler;
};
  static class members(靜態類成員)必須在 class 定義外被定義(除非它們是 const 而且是 integral),所以:

std::new_handler Widget::currentHandler = 0; // init to null in the class
// impl. file
  Widget 中的 set_new_handler 函數會保存傳遞給它的任何指針,而且會返回前次調用時被保存的任何指針,這也正是 set_new_handler 的標準版本所做的事情:

std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
 std::new_handler oldHandler = currentHandler;
 currentHandler = p;
 return oldHandler;
}
  最終,Widget 的 operator new 將做下面這些事情: photoshop教程 數據結構 五筆輸入法專題 QQ病毒專題 共享上網專題 Google工具和服務專題
  以 Widget 的 error-handling function 為參數調用 standard set_new_handler。這樣將 Widget 的new-handler 安裝為 global new-handler。

  調用 global operator new 進行真正的內存分配。假如分配失敗,global operator new 調用 Widget 的 new-handler,因為那個函數剛才被安裝為 global new-handler。假如 global operator new 最后還是無法分配內存,它會拋出一個 bad_alloc exception。在此情況下,Widget 的 operator new 必須恢復原來的 global new-handler,然后傳播那個 exception。為了確保原來的 new-handler 總能被恢復,Widget 將 global new-handler 作為一種資源對待,并遵循《C++箴言:使用對象治理資源》中的建議,使用 resource-managing objects(資源治理對象)來預防 resource leaks(資源泄漏)。

  假如 global operator new 能夠為一個 Widget object 分配足夠的內存,Widget 的 operator new 返回一個指向被分配內存的指針。object 的用于治理 global new-handler 的 destructor(析構函數)自動將 global new-handler 恢復到調用 Widget 的 operator new 之前的狀態。

  以下就是你如何在 C++ 中表達這所有的事情。我們以 resource-handling class 開始,組成部分中除了基本的 RAII 操作(在構造過程中獲得資源并在析構過程中釋放)(《C++箴言:使用對象治理資源》),沒有更多的東西:

class NewHandlerHolder {
public:
 eXPlicit NewHandlerHolder(std::new_handler nh) // acquire current
 :handler(nh) {} // new-handler

 ~NewHandlerHolder() // release it
 { std::set_new_handler(handler); }
private:
 std::new_handler handler; // remember it

 NewHandlerHolder(const NewHandlerHolder&); // prevent copying
 NewHandlerHolder& // (see 《C++箴言:謹慎考慮資源治理類的拷貝行為》)
 operator=(const NewHandlerHolder&);
};
  這使得 Widget 的 operator new 的實現非常簡單:


void * Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
 NewHandlerHolder // install Widget's
 h(std::set_new_handler(currentHandler)); // new-handler

 return ::operator new(size); // allocate memory
 // or throw

} // restore global
// new-handler
  Widget 的客戶像這樣使用它的 new-handling capabilities(處理 new 的能力):

void outOfMem(); // decl. of func. to call if mem. alloc.
// for Widget objects fails

Widget::set_new_handler(outOfMem); // set outOfMem as Widget's
// new-handling function

Widget *pw1 = new Widget; // if memory allocation
// fails, call outOfMem

std::string *ps = new std::string; // if memory allocation fails,
// call the global new-handling
// function (if there is one)

Widget::set_new_handler(0); // set the Widget-specific
// new-handling function to
// nothing (i.e., null)

Widget *pw2 = new Widget; // if mem. alloc. fails, throw an
// exception immediately. (There is
// no new- handling function for
// class Widget.)
  無論 class 是什么,實現這個方案的代碼都是一樣的,所以在其它地方重用它就是一個合理的目標。使它成為可能的一個簡單方法是創建一個 "mixin-style" base class(“混合風格”基類),也就是說,一個設計為答應 derived classes(派生類)繼續一個單一特定能力(在當前情況下,就是設定一個 class-specific new-handler 的能力)的 base class(基類)。然后把這個 base class(基類)轉化為一個 template(模板),以便于你得到針對每一個 inheriting class(繼續來的類)的 class data 的不同拷貝。

  這個設計的 base class(基類)部分讓 derived classes(派生類)繼續它們全都需要的 set_new_handler 和 operator new functions,而這個設計 template(模板)部分確保每一個 inheriting class(繼續來的類)得到一個不同的 currentHandler data member(數據成員)。這聽起來可能有點復雜,但是代碼看上去可靠而且熟悉。實際上,僅有的真正不同是它現在可以用在任何需要它的 class 之上:

template<typename T> // "mixin-style" base class for
class NewHandlerSupport{
 // class-specific set_new_handler
public: // support

 static std::new_handler set_new_handler(std::new_handler p) throw();
 static void * operator new(std::size_t size) throw(std::bad_alloc);

 ... // other versions of op. new
private:
 static std::new_handler currentHandler;
};

template<typename T>
std::new_handler
NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw()
{
 std::new_handler oldHandler = currentHandler;
 currentHandler = p;
 return oldHandler;
}

template<typename T>
void* NewHandlerSupport<T>::operator new(std::size_t size)
throw(std::bad_alloc)
{
 NewHandlerHolder h(std::set_new_handler(currentHandler));
 return ::operator new(size);
}
// this initializes each currentHandler to null
template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;
  有了這個 class template(類模板),為 Widget 增加 set_new_handler 支持就很輕易了:Widget 只需要從 NewHandlerSupport<Widget> 繼續即可。(可能看起來很奇異,但是下面我將解釋更多的細節。)

class Widget: public NewHandlerSupport<Widget> {
 ... // as before, but without declarations for
}; // set_new_handler or operator new
  這些就是 Widget 為了提供一個 class-specific set_new_handler 所需要做的全部。

  但是也許你依然在為 Widget 從 NewHandlerSupport<Widget> 繼續而煩惱。假如是這樣,當你注重到 NewHandlerSupport template 從來沒有用到它的 type parameter T 時,你可能會更加煩惱。它不需要那樣做。我們需要的全部就是為每一個從 NewHandlerSupport 繼續的 class 提供一份不同的 NewHandlerSupport ——非凡是它的 static data member(靜態數據成員)currentHandler ——的拷貝。template parameter T 只是為了將一個 inheriting class 同另一個區分開來。template 機制自己自動地為每一個被實例化的 NewHandlerSupport 中的 T 生成一個 currentHandler 的拷貝。

  對于 Widget 從一個把 Widget 當作一個 type parameter(類型參數)的 templatized base class(模板化基類)繼續,假如這個概念把你弄得有點糊涂,不必難受。它最開始對每一個人都有這種影響。然而,它發展成如此有用的一項技術,它有一個名字,雖然它正??瓷先ニ从车氖聦嵅⒉皇撬麄兊谝淮慰吹剿臉幼印K环Q作 curiously recurring template pattern(奇異的遞歸模板模式) (CRTP)。真的。

  在這一點上,我發表了一篇文章建議一個更好的名字叫做 "Do It For Me",因為當 Widget 從 NewHandlerSupport<Widget> 繼續時,它其實是在說:“我是 Widget,而我要從針對 Widget 的 NewHandlerSupport class 繼續。”沒有人使用我提議的名字(甚至是我自己),但是把 CRTP 考慮成說 "do it for me" 的一種方式也許會幫助你理解 templatized inheritance(模板化繼續)在做些什么。

  像 NewHandlerSupport 這樣的 templates 使得為任何有需要的 class 添加一個 class-specific new-handler 變得易如反掌。然而,mixin-style inheritance(混合風格繼續)總是會導致 multiple inheritance(多繼續)的話題,而在我們沿著這條路走下去之前,你需要閱讀《C++箴言:謹慎使用多繼續》。

  直到 1993 年,C++ 還要求 operator new 不能分配被請求的內存時要返回 null。operator new 現在則被指定拋出一個 bad_alloc exception,但是很多 C++ 程序是在編譯器開始支持這個修訂標準之前寫成的。C++ 標準化委員會不想遺棄這些 test-for-null(檢驗是否為 null)的代碼基礎,所以他們提供了 operator new 的另一種可選形式,用以提供傳統的 failure-yields-null(失敗導致 null)的行為。這些形式被稱為 "nothrow" 形式,這在一定程度上是因為它們在使用 new 的地方使用了 nothrow objects(定義在頭文件 <new> 中):


class Widget { ... };
Widget *pw1 = new Widget; // throws bad_alloc if
// allocation fails

if (pw1 == 0) ... // this test must fail

Widget *pw2 =new (std::nothrow) Widget; // returns 0 if allocation for
// the Widget fails

if (pw2 == 0) ... // this test may succeed
  對于異常,nothrow new 提供了比最初看上去更少的強制保證。在表達式 "new (std::nothrow) Widget" 中,發生了兩件事。首先,operator new 的 nothrow 版本被調用來為一個 Widget object 分配足夠的內存。假如這個分配失敗,眾所周知,operator new 返回 null pointer。然而,假如它成功了,Widget constructor 被調用,而在此刻,所有打的賭都失效了。Widget constructor 能做任何它想做的事。它可能自己 new 出來一些內存,而假如它這樣做了,它并沒有被強迫使用 nothrow new。那么,雖然在 "new (std::nothrow) Widget" 中調用的 operator new 不會拋出,Widget constructor 卻可以。假如它這樣做了,exception 像往常一樣被傳播。結論?使用 nothrow new 只能保證 operator new 不會拋出,不能保證一個像 "new (std::nothrow) Widget" 這樣的表達式絕不會導致一個 exception。在所有的可能性中,你最好絕不需要 nothrow new。

  無論你是使用 "normal"(也就是說,exception-throwing)new,還是它的稍微有些矮小的堂兄弟,理解 new-handler 的行為是很重要的,因為它可以用于兩種形式。

  Things to Remember

  ·set_new_handler 答應你指定一個當內存分配請求不能被滿足時可以被調用的函數。

  ·nothrow new 作用有限,因為它僅適用于內存分配,隨后的 constructor 調用可能依然會拋出 exceptions。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品2018| 欧美成人精品三级在线观看| 18一19gay欧美视频网站| 午夜精品国产精品大乳美女| 日本久久精品视频| 亚洲欧美成人在线| 在线观看国产精品淫| 成人在线国产精品| 日本高清久久天堂| 在线电影中文日韩| 亚洲国产天堂网精品网站| 国产大片精品免费永久看nba| 91亚洲精品久久久久久久久久久久| 国产精品久久久久久av福利软件| 日韩在线激情视频| 久久精品一本久久99精品| 久久亚洲影音av资源网| 91精品国产乱码久久久久久久久| 亚洲精品suv精品一区二区| 91产国在线观看动作片喷水| 欧美在线视频一区二区| 日韩在线观看免费网站| 国产精品福利在线| 尤物99国产成人精品视频| 91在线观看免费高清完整版在线观看| 国产在线观看不卡| 日韩中文字幕精品| 国产欧美精品在线播放| 亚洲视频网站在线观看| 清纯唯美亚洲综合| 91夜夜揉人人捏人人添红杏| 国产综合香蕉五月婷在线| 久久久精品欧美| 亚洲乱码一区av黑人高潮| 亚洲欧美在线免费观看| 亚洲va久久久噜噜噜久久天堂| 国产在线观看精品| 亚洲欧美一区二区三区情侣bbw| 日韩精品视频在线| 午夜精品久久久久久久99热浪潮| 日韩性生活视频| 久久影院中文字幕| 动漫精品一区二区| 欧美激情日韩图片| 亚洲福利在线播放| 亚洲另类欧美自拍| 日韩美女视频免费在线观看| 精品一区二区三区电影| 欧美成人四级hd版| 最近2019中文字幕第三页视频| 久久99久久99精品免观看粉嫩| 欧美另类xxx| 国产精品久久久久久久久久尿| 国产一区二区丝袜| 午夜精品福利视频| 狠狠色香婷婷久久亚洲精品| 日本久久久久久久久| 美女999久久久精品视频| 国产a∨精品一区二区三区不卡| 久久精品视频免费播放| 91老司机精品视频| 日韩美女免费视频| 中国人与牲禽动交精品| 97精品视频在线观看| 亚洲毛片在线免费观看| 日韩电影在线观看免费| 伊人伊成久久人综合网站| 久久夜色精品国产亚洲aⅴ| 在线播放国产一区二区三区| 欧美日在线观看| 日韩电影中文 亚洲精品乱码| 日本亚洲欧美三级| 国产精品美女www爽爽爽视频| 色综合91久久精品中文字幕| 国产欧美一区二区三区在线看| 亚洲伦理中文字幕| 亚洲欧美激情四射在线日| 午夜精品蜜臀一区二区三区免费| 久久影院模特热| 日韩va亚洲va欧洲va国产| 18性欧美xxxⅹ性满足| 在线观看亚洲视频| 在线亚洲午夜片av大片| 国产一区视频在线播放| 欧美日韩电影在线观看| 亚洲精品网址在线观看| 色悠悠国产精品| 亚洲欧美日韩中文视频| 欧美丰满少妇xxxxx做受| 成人免费大片黄在线播放| 中文字幕最新精品| 亚州欧美日韩中文视频| 97视频在线观看网址| 欧美黑人视频一区| 九九热这里只有精品6| 欧美日韩在线视频一区二区| 日本欧美一二三区| 日韩欧美黄色动漫| 亚洲视频专区在线| 91久久久久久久久| 国产精品日日摸夜夜添夜夜av| 日韩高清免费观看| 一本色道久久综合亚洲精品小说| 亚洲美女福利视频网站| 美女啪啪无遮挡免费久久网站| 午夜精品一区二区三区av| 国产精品久久久久久久久久小说| 国产精品国产三级国产aⅴ9色| 亚洲图片在区色| 国产精品久久久久久网站| 成人激情在线播放| 亚洲成人久久网| 亚洲石原莉奈一区二区在线观看| 日韩a**中文字幕| 欧美天天综合色影久久精品| 精品女同一区二区三区在线播放| 亚洲自拍小视频| 国产精品福利在线观看网址| 国内精品久久久久影院 日本资源| 国内免费久久久久久久久久久| 国产日韩在线免费| 久久精品夜夜夜夜夜久久| 136fldh精品导航福利| 91性高湖久久久久久久久_久久99| 久久久久亚洲精品成人网小说| 欧美性少妇18aaaa视频| 精品一区电影国产| 欧美日本国产在线| 久久久久久久999精品视频| 在线日韩日本国产亚洲| 色偷偷91综合久久噜噜| 91大神在线播放精品| 欧美日韩免费一区| 欧美区二区三区| 国产在线98福利播放视频| 国产精品美女无圣光视频| 欧美在线一级va免费观看| 5278欧美一区二区三区| 欧美精品成人91久久久久久久| 国产一区二区三区免费视频| 亚洲天堂男人天堂女人天堂| 精品福利在线观看| 欧美精品一区二区三区国产精品| 欧美又大又硬又粗bbbbb| 精品成人国产在线观看男人呻吟| 国产精品毛片a∨一区二区三区|国| 97av在线影院| 国产91色在线| 91免费国产网站| 欧美日韩午夜视频在线观看| 欧美一区二三区| 日韩成人在线电影网| 在线成人激情黄色| 伊人一区二区三区久久精品| 国产精品久久一| 午夜精品久久久久久99热软件| 国产精品白嫩美女在线观看| 日韩欧美视频一区二区三区| 国产综合色香蕉精品| 日韩av在线不卡| 亚洲视频第一页| 日韩欧美一区二区在线| 国产精品手机播放| 国产成人一区二区在线|