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

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

More effective C++:審慎使用異常規格

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

  毫無疑問,異常規格是一個引人注目的特性。它使得代碼更輕易理解,因為它明確地描述了一個函數可以拋出什么樣的異常。但是它不只是一個有趣的注釋。編譯器在編譯時有時能夠檢測到異常規格的不一致。而且假如一個函數拋出一個不在異常規格范圍里的異常,系統在運行時能夠檢測出這個錯誤,然后一個非凡函數uneXPected將被自動地調用。異常規格既可以做為一個指導性文檔同時也是異常使用的強制約束機制,它似乎有著很誘人的外表。
 
  不過在通常情況下,美貌只是一層皮,外表的漂亮并不代表其內在的素質。函數unexpected缺省的行為是調用函數terminate,而terminate缺省的行為是調用函數abort,所以一個違反異常規格的程序其缺省的行為就是halt(停止運行)。在激活的stack frame中的局部變量沒有被釋放,因為abort在關閉程序時不進行這樣的清除操作。對異常規格的觸犯變成了一場并不應該發生的災難。

  不幸的是,我們很輕易就能夠編寫出導致發生這種災難的函數。編譯器僅僅部分地檢測異常的使用是否與異常規格保持一致。一個函數調用了另一個函數,并且后者可能拋出一個違反前者異常規格的異常,(A函數調用B函數,因為B函數可能拋出一個不在A函數異常規格之內的異常,所以這個函數調用就違反了A函數的異常規格 譯者注)編譯器不對此種情況進行檢測,并且語言標準也禁止它們拒絕這種調用方式(盡管可以顯示警告信息)。

  例如函數f1沒有聲明異常規格,這樣的函數就可以拋出任意種類的異常:

extern void f1(); // 可以拋出任意的異常
  假設有一個函數f2通過它的異常規格來聲明其只能拋出int類型的異常:

void f2() throw(int);
  f2調用f1是非常合法的,即使f1可能拋出一個違反f2異常規格的異常:

void f2() throw(int)
{
 ...
 f1(); // 即使f1可能拋出不是int類型的
 //異常,這也是合法的。
 ...
}
  當帶有異常規格的新代碼與沒有異常規格的老代碼整合在一起工作時,這種靈活性就顯得很重要。

  因為你的編譯器答應你調用一個函數其拋出的異常與發出調用的函數的異常規格不一致,并且這樣的調用可能導致你的程序執行被終止,所以在編寫軟件時采取措施把這種不一致減小到最少。一種好方法是避免在帶有類型參數的模板內使用異常規格。例如下面這種模板,它似乎不能拋出任何異常:

// a poorly designed template wrt exception specifications
template<class T>
bool Operator==(const T& lhs, const T& rhs) throw()
{
 return &lhs == &rhs;
}
  這個模板為所有類型定義了一個操作符函數operator==。對于任意一對類型相同的對象,假如對象有一樣的地址,該函數返回true,否則返回false。

  這個模板包含的異常規格表示模板生成的函數不能拋出異常。但是事實可能不會這樣,因為opertor&能被一些類型對象重載。假如被重載的話,當調用從operator==函數內部調用opertor&時,opertor&可能會拋出一個異常,這樣就違反了我們的異常規格,使得程序控制跳轉到unexpected。

  上述的例子是一種更一般問題的特例,這個問題也就是沒有辦法知道某種模板類型參數拋出什么樣的異常。我們幾乎不可能為一個模板提供一個有意義的異常規格。,因為模板總是采用不同的方法使用類型參數。解決方法只能是模板和異常規格不要混合使用。

  能夠避免調用unexpected函數的第二個方法是假如在一個函數內調用其它沒有異常規格的函數時應該去除這個函數的異常規格。這很輕易理解,但是實際中輕易被忽略。比如答應用戶注冊一個回調函數:

// 一個window系統回調函數指針
//當一個window系統事件發生時
typedef void (*CallBackPtr)(int eventXLocation,
int eventYLocation,
void *dataToPassBack);
//window系統類,含有回調函數指針,
//該回調函數能被window系統客戶注冊
class CallBack {
 public:
  CallBack(CallBackPtr fPtr, void *dataToPassBack): func(fPtr), data(dataToPassBack) {}
  void makeCallBack(int eventXLocation,int eventYLocation) const throw();
 PRivate:
  CallBackPtr func; // function to call when
  // callback is made
  void *data; // data to pass to callback
}; // function
// 為了實現回調函數,我們調用注冊函數,
//事件的作標與注冊數據做為函數參數。
void CallBack::makeCallBack(int eventXLocation,
int eventYLocation) const throw()
{
 func(eventXLocation, eventYLocation, data);
}
  這里在makeCallBack內調用func,要冒違反異常規格的風險,因為無法知道func會拋出什么類型的異常。

  通過在程序在CallBackPtr typedef中采用更嚴格的異常規格來解決問題:


typedef void (*CallBackPtr)(int eventXLocation,
int eventYLocation,
void *dataToPassBack) throw();
這樣定義typedef后,假如注冊一個可能會拋出異常的callback函數將是非法的:

// 一個沒有異常給各的回調函數
void callBackFcn1(int eventXLocation, int eventYLocation,
void *dataToPassBack);
void *callBackData;
...
CallBack c1(callBackFcn1, callBackData);
//錯誤!callBackFcn1可能
// 拋出異常
//帶有異常規格的回調函數
void callBackFcn2(int eventXLocation,
int eventYLocation,
void *dataToPassBack) throw();
CallBack c2(callBackFcn2, callBackData);
// 正確,callBackFcn2
// 沒有異常規格
  傳遞函數指針時進行這種異常規格的檢查,是語言的較新的特性,所以有可能你的編譯器不支持這個特性。假如它們不支持,那就依靠你自己來確保不能犯這種錯誤。

  避免調用unexpected的第三個方法是處理系統本身拋出的異常。這些異常中最常見的是bad_alloc,當內存分配失敗時它被operator new 和operator new[]拋出(參見條款8)。假如你在函數里使用new操作符(還參見條款8),你必須為函數可能碰到bad_alloc異常作好預備。

  現在常說預防勝于治療(即做任何事都要未雨綢繆 譯者注),但是有時卻是預防困難而治療輕易。也就是說有時直接處理unexpected異常比防止它們被拋出要簡單。例如你正在編寫一個軟件,精確地使用了異常規格,但是你必須從沒有使用異常規格的程序庫中調用函數,要防止拋出unexpected異常是不現實的,因為這需要改變程序庫中的代碼。

  雖然防止拋出unexpected異常是不現實的,但是C++答應你用其它不同的異常類型替換unexpected異常,你能夠利用這個特性。例如你希望所有的unexpected異常都被替換為UnexpectedException對象。你能這樣編寫代碼:

class UnexpectedException {}; // 所有的unexpected異常對象被
//替換為這種類型對象

void convertUnexpected() // 假如一個unexpected異常被
{
 // 拋出,這個函數被調用
 throw UnexpectedException();
}
  通過用convertUnexpected函數替換缺省的unexpected函數,來使上述代碼開始運行。:

set_unexpected(convertUnexpected);
  當你這么做了以后,一個unexpected異常將觸發調用convertUnexpected函數。Unexpected異常被一種UnexpectedException新異常類型替換。假如被違反的異常規格包含UnexpectedException異常,那么異常傳遞將繼續下去,似乎異常規格總是得到滿足。(假如異常規格沒有包含UnexpectedException,terminate將被調用,就似乎你沒有替換unexpected一樣)

  另一種把unexpected異常轉變成知名類型的方法是替換unexpected函數,讓其重新拋出當前異常,這樣異常將被替換為bad_exception。你可以這樣編寫:

void convertUnexpected() // 假如一個unexpected異常被
{
 //拋出,這個函數被調用
 throw; // 它只是重新拋出當前
} // 異常

set_unexpected(convertUnexpected);
// 安裝 convertUnexpected
// 做為unexpected
// 的替代品
  假如這么做,你應該在所有的異常規格里包含bad_exception(或它的基類,標準類exception)。你將不必再擔心假如碰到unexpected異常會導致程序運行終止。任何不聽話的異常都將被替換為bad_exception,這個異常代替原來的異常繼續傳遞。

  到現在你應該理解異常規格能導致大量的麻煩。編譯器僅僅能部分地檢測它們的使用是否一致,在模板中使用它們會有問題,一不注重它們就很輕易被違反,并且在缺省的情況下它們被違反時會導致程序終止運行。異常規格還有一個缺點就是它們能導致unexpected被觸發即使一個high-level調用者預備處理被拋出的異常,比如下面這個幾乎一字不差地來自從條款11例子:

class session { // for modeling online
public: // sessions
~Session();
...

private:
static void logDestrUCtion(Session *objAddr) throw();
};

Session::~Session()
{
try {
logDestruction(this);
}
catch (...) { }
}
  session的析構函數調用logDestruction記錄有關session對象被釋放的信息,它明確地要捕捉從logDestruction拋出的所有異常。但是logDestruction的異常規格表示其不拋出任何異?!,F在假設被logDestruction調用的函數拋出了一個異常,而logDestruction沒有捕捉。我們不會期望發生這樣的事情,凡是正如我們所見,很輕易就會寫出違反異常規格的代碼。當這個異常通過logDestruction傳遞出來,unexpected將被調用,缺省情況下將導致程序終止執行。這是一個正確的行為,這是session析構函數的作者所希望的行為么?作者想處理所有可能的異常,所以似乎不應該不給session析構函數里的catch塊執行的機會就終止程序。假如logDestruction沒有異常規格,這種事情就不會發生。(一種防止的方法是如上所描述的那樣替換unexpected)

  以全面的角度去看待異常規格是非常重要的。它們提供了優秀的文檔來說明一個函數拋出異常的種類,并且在違反它的情況下,會有可怕的結果,程序被立即終止,在缺省時它們會這么做。同時編譯器只會部分地檢測它們的一致性,所以他們很輕易被不經意地違反。而且他們會阻止high-level異常處理器來處理unexpected異常,即使這些異常處理器知道如何去做。綜上所述,異常規格是一個應被審慎使用的公族。在把它們加入到你的函數之前,應考慮它們所帶來的行為是否就是你所希望的行為。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品国产乱码久久久久久天美| 精品人伦一区二区三区蜜桃网站| 国内伊人久久久久久网站视频| 国产日韩换脸av一区在线观看| 国产美女久久久| 国产精品久久久久久久久久久新郎| 91精品久久久久久久久久久久久| 欧美第一淫aaasss性| 青青久久av北条麻妃海外网| 亚洲精品成人久久| 亚洲电影免费观看高清完整版| 色av吧综合网| 亚洲色图五月天| 精品久久中文字幕久久av| 日本精品一区二区三区在线播放视频| 一本色道久久88精品综合| 国产盗摄xxxx视频xxx69| 日韩精品极品毛片系列视频| 国产精品久久久久久久久久三级| 在线观看日韩欧美| 亚洲欧美日本精品| 欧美日本在线视频中文字字幕| 欧美有码在线视频| 中文字幕国产精品久久| 精品国产区一区二区三区在线观看| 欧美成人激情图片网| 亚洲欧美在线第一页| 国产v综合v亚洲欧美久久| 久久深夜福利免费观看| 国产成人久久久精品一区| 亚洲精品动漫久久久久| 国产视频精品xxxx| 九九热精品视频| 欧美性xxxxx| 国产一区玩具在线观看| 91精品国产91久久久久久最新| 亚洲国产精品久久久久久| 一区二区三区视频观看| 欧美日韩国产中字| 亚洲精品一区二区久| 亚洲欧洲日产国产网站| 日本成人在线视频网址| 国产偷亚洲偷欧美偷精品| 91豆花精品一区| 欧美视频国产精品| 国产激情综合五月久久| 草民午夜欧美限制a级福利片| 免费不卡在线观看av| 欧美日韩亚洲一区二区三区| 日韩免费在线免费观看| 51视频国产精品一区二区| 亚洲国产欧美在线成人app| 亚洲精品久久久久久久久久久久久| 九九精品视频在线| 日韩激情视频在线播放| 国产成人亚洲综合| 国内外成人免费激情在线视频| 538国产精品视频一区二区| 亚洲二区中文字幕| 国产精品美腿一区在线看| 视频在线一区二区| 亚洲人线精品午夜| 国产日韩精品综合网站| 久久中国妇女中文字幕| 亚洲国产成人久久综合一区| 日韩高清欧美高清| 国内精品视频久久| 日韩av成人在线观看| 欧美性感美女h网站在线观看免费| 欧美色另类天堂2015| 欧美精品日韩www.p站| 免费不卡在线观看av| 国产美女直播视频一区| 国产亚洲成精品久久| 中文字幕亚洲专区| 青草成人免费视频| 日韩免费观看高清| 欧美乱妇40p| 国产精品自产拍高潮在线观看| 成人国产在线激情| 亚洲一区二区免费| 久久激情视频免费观看| 欧美性猛交xxxxx水多| 久久久噜噜噜久噜久久| 欧美电影免费观看网站| 午夜精品久久17c| 久久久久久97| 亚洲丁香久久久| 精品亚洲一区二区| 日韩电影在线观看免费| 国模gogo一区二区大胆私拍| 欧美日韩国产专区| 日韩成人在线视频| 91香蕉嫩草影院入口| 成人在线中文字幕| 久久久国产精彩视频美女艺术照福利| 亚洲福利在线观看| 久久久免费精品视频| 久久精品色欧美aⅴ一区二区| 亚洲精品电影网在线观看| 欧美裸身视频免费观看| 亚洲精美色品网站| 欧美日韩精品国产| 视频一区视频二区国产精品| 日韩av中文字幕在线播放| 亚洲成人av片在线观看| 欧美激情第99页| 午夜美女久久久久爽久久| 午夜精品一区二区三区在线视| 国产成人在线精品| 亚洲欧洲国产精品| 欧美亚洲视频在线看网址| 欧美午夜精品久久久久久浪潮| 九九热精品视频国产| 91精品国产综合久久久久久久久| 91久久久久久久久久久久久| 色综合男人天堂| 在线成人激情视频| 亚洲精品资源美女情侣酒店| 日韩a**中文字幕| 欧美一级片一区| 亚洲精品www久久久久久广东| 亚洲曰本av电影| 欧美激情a∨在线视频播放| 亚州成人av在线| 91精品国产综合久久香蕉的用户体验| 日韩av在线看| 亚洲人高潮女人毛茸茸| 亚洲三级黄色在线观看| 日韩最新中文字幕电影免费看| 在线播放亚洲激情| 欧美国产在线视频| 欧美亚洲在线观看| 国产区亚洲区欧美区| 在线一区二区日韩| 久久99视频免费| 久久久久久久久久久久久久久久久久av| 欧美黑人巨大精品一区二区| yw.139尤物在线精品视频| 国产一区二区在线免费视频| 日韩在线一区二区三区免费视频| 中文字幕日韩有码| 久久久久久噜噜噜久久久精品| 亚洲成人在线视频播放| 欧美电影免费观看高清完整| 狠狠做深爱婷婷久久综合一区| 麻豆乱码国产一区二区三区| 91青草视频久久| 欧美一级在线亚洲天堂| 国产成人精品视频在线观看| 日韩久久精品成人| 中日韩美女免费视频网站在线观看| 91高清在线免费观看| 91经典在线视频| 国产精品老女人精品视频| 国内伊人久久久久久网站视频| 伊是香蕉大人久久| 日韩欧美成人免费视频| 国产激情999| 国产视频在线一区二区| 国产一区二区欧美日韩| 国产精品嫩草影院久久久| 成人久久一区二区三区| 亚洲成年网站在线观看|