日韩天天综合网_野战两个奶头被亲到高潮_亚洲日韩欧美精品综合_av女人天堂污污污_视频一区**字幕无弹窗_国产亚洲欧美小视频_国内性爱精品在线免费视频_国产一级电影在线播放_日韩欧美内地福利_亚洲一二三不卡片区

More Effective C++:通過引用捕獲異常_Web服務(wù)器教程

編輯Tag賺U幣
教程Tag:暫無Tag,歡迎添加,賺取U幣!
當(dāng)你寫一個(gè)catch子句時(shí),必須確定讓異常通過何種方式傳遞到catch子句里。你可以有三個(gè)選擇:與你給函數(shù)傳遞參數(shù)一樣,通過指針(by pointer),通過傳值(by value)或通過引用(by reference)。

  我們首先討論通過指針方式捕獲異常(catch by pointer)。從throw處傳遞一個(gè)異常到catch子句是一個(gè)緩慢的過程,在理論上這種方法的實(shí)現(xiàn)對(duì)于這個(gè)過程來說是效率最高的。因?yàn)樵趥鬟f異常信息時(shí),只有采用通過指針拋出異常的方法才能夠做到不拷貝對(duì)象,例如:

class exception { ... }; // 來自標(biāo)準(zhǔn)C++庫(STL)
 // 中的異常類層次
 void someFunction()
 {
  static exception ex; // 異常對(duì)象
  ...
  throw &ex; // 拋出一個(gè)指針,指向ex
  ...
 }
 void doSomething()
 {
  try {
   someFunction(); // 拋出一個(gè) exception*
  }
  catch (exception *ex) { // 捕獲 exception*;
   ... // 沒有對(duì)象被拷貝
  }
 }

  這看上去很不錯(cuò),但是實(shí)際情況卻不是這樣。為了能讓程序正常運(yùn)行,程序員定義異常對(duì)象時(shí)必須確保當(dāng)程序控制權(quán)離開拋出指針的函數(shù)后,對(duì)象還能夠繼續(xù)生存。全局與靜態(tài)對(duì)象都能夠做到這一點(diǎn),但是程序員很容易忘記這個(gè)約束。如果真是如此的話,他們會(huì)這樣寫代碼:

void someFunction()
{
 exception ex; // 局部異常對(duì)象;
 // 當(dāng)退出函數(shù)的生存空間時(shí)
 // 這個(gè)對(duì)象將被釋放。
 ...
 throw &ex; // 拋出一個(gè)指針,指向
 ... // 已被釋放的對(duì)象
}

  這簡(jiǎn)直糟糕透了,因?yàn)樘幚磉@個(gè)異常的catch子句接受到的指針,其指向的對(duì)象已經(jīng)不再存在。

  另一種拋出指針的方法是在建立一個(gè)堆對(duì)象(new heap object):

void someFunction()
{
 ...
 throw new exception; // 拋出一個(gè)指針,指向一個(gè)在堆中
 ... // 建立的對(duì)象(希望
}
// 自己不要再拋出一個(gè)
// 異常!)

  這避免了捕獲一個(gè)指向已被釋放對(duì)象的指針的問題,但是catch子句的作者又面臨一個(gè)令人頭疼的問題:他們是否應(yīng)該刪除他們接受的指針?如果是在堆中建立的異常對(duì)象,那他們必須刪除它,否則會(huì)造成資源泄漏。如果不是在堆中建立的異常對(duì)象,他們絕對(duì)不能刪除它,否則程序的行為將不可預(yù)測(cè)。該如何做呢?

  這是不可能知道的。一些clients可能會(huì)傳遞全局或靜態(tài)對(duì)象的地址,另一些可能轉(zhuǎn)遞堆中建立的異常對(duì)象的地址。通過指針捕獲異常,將遇到一個(gè)哈姆雷特式的難題:是刪除還是不刪除?這是一個(gè)難以回答的問題。所以你最好避開它。

  而且,通過指針捕獲異常也不符合C++語言本身的規(guī)范。四個(gè)標(biāo)準(zhǔn)的異常――bad_alloc(當(dāng)operator new(參見條款8)不能分配足夠的內(nèi)存時(shí),被拋出),bad_cast(當(dāng)dynamic_cast針對(duì)一個(gè)引用(reference)操作失敗時(shí),被拋出),bad_typeid(當(dāng)dynamic_cast對(duì)空指針進(jìn)行操作時(shí),被拋出)和bad_exception(用于unexpected異常; 參見條款14)――都不是指向?qū)ο蟮闹羔�,所以你必須通過值或引用來捕獲它們。



  通過值捕獲異常(catch-by-value)可以解決上述的問題,例如異常對(duì)象刪除的問題和使用標(biāo)準(zhǔn)異常類型的問題。但是當(dāng)它們被拋出時(shí)系統(tǒng)將對(duì)異常對(duì)象拷貝兩次(參見條款12)。而且它會(huì)產(chǎn)生 slicing problem,即派生類的異常對(duì)象被做為基類異常對(duì)象捕獲時(shí),那它的派生類行為就被切掉了(sliced off)。這樣的sliced對(duì)象實(shí)際上是一個(gè)基類對(duì)象:它們沒有派生類的數(shù)據(jù)成員,而且當(dāng)調(diào)用它們的虛擬函數(shù)時(shí),系統(tǒng)解析后調(diào)用的是基類對(duì)象的函數(shù)。 (當(dāng)一個(gè)對(duì)象通過傳值方式傳遞給函數(shù),也會(huì)發(fā)生一樣的情況――參見Effective C++ 條款22)。例如下面這個(gè)程序采用了擴(kuò)展自標(biāo)準(zhǔn)異常類的異常類層次體系:

class exception { // 如上,這是
 public: // 一個(gè)標(biāo)準(zhǔn)異常類
  virtual const char * what() throw();
  // 返回異常的簡(jiǎn)短描述.
  ... // (在函數(shù)聲明的結(jié)尾處
  // 的"throw()",
}; //有關(guān)它的信息

class runtime_error: //也來自標(biāo)準(zhǔn)C++異常類
public exception { ... };
class Validation_error: // 客戶自己加入個(gè)類
public runtime_error {
 public:
  virtual const char * what() throw();
  // 重新定義在異常類中
  ... //虛擬函數(shù)
}; //

void someFunction() // 拋出一個(gè) validation
{ // 異常
 ...
 if (a validation 測(cè)試失敗) {
  throw Validation_error();
 }
 ...
}

void doSomething()
{
 try {
  someFunction(); // 拋出 validation
 } //異常
 catch (exception ex) { //捕獲所有標(biāo)準(zhǔn)異常類
  // 或它的派生類
  cerr << ex.what(); // 調(diào)用 exception::what(),
  ... // 而不是Validation_error::what()
 }
}

  調(diào)用的是基類的what函數(shù),即使被拋出的異常對(duì)象是Validation_error和 Validation_error類型,它們已經(jīng)重新定義的虛擬函數(shù)。這種slicing行為絕不是你所期望的。

  最后剩下方法就是通過引用捕獲異常(catch-by-reference)。通過引用捕獲異常能使你避開上述所有問題。不象通過指針捕獲異常,這種方法不會(huì)有對(duì)象刪除的問題而且也能捕獲標(biāo)準(zhǔn)異常類型。也不象通過值捕獲異常,這種方法沒有slicing problem,而且異常對(duì)象只被拷貝一次。

  我們采用通過引用捕獲異常的方法重寫最后那個(gè)例子,如下所示:

void someFunction() //這個(gè)函數(shù)沒有改變
{
 ...
 if (a validation 測(cè)試失敗) {
  throw Validation_error();
 }
 ...
}
void doSomething()
{
 try {
  someFunction(); // 沒有改變
 }
 catch (exception& ex) { // 這里,我們通過引用捕獲異常
  // 以替代原來的通過值捕獲
  cerr << ex.what(); // 現(xiàn)在調(diào)用的是
  // Validation_error::what(),
  ... // 而不是 exception::what()
 }
}

  這里沒有對(duì)throw進(jìn)行任何改變,僅僅改變了catch子句,給它加了一個(gè)&符號(hào)。然而這個(gè)微小的改變能造成了巨大的變化,因?yàn)閏atch塊中的虛擬函數(shù)能夠如我們所愿那樣工作了:調(diào)用的Validation_erro函數(shù)是我們重新定義過的函數(shù)。

  如果你通過引用捕獲異常(catch by reference),你就能避開上述所有問題,不會(huì)為是否刪除異常對(duì)象而煩惱;能夠避開slicing異常對(duì)象;能夠捕獲標(biāo)準(zhǔn)異常類型;減少異常對(duì)象需要被拷貝的數(shù)目。所以你還在等什么?通過引用捕獲異常吧(Catch exceptions by reference)!

來源:網(wǎng)絡(luò)搜集//所屬分類:Web服務(wù)器教程/更新時(shí)間:2013-04-14
相關(guān)Web服務(wù)器教程