欧美日韩精品在线,国内精品久久久久久久久,一级毛片恃级毛片直播,清纯唯美亚洲综合欧美色

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

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

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

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

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

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

  這簡直糟糕透了,因為處理這個異常的catch子句接受到的指針,其指向的對象已經(jīng)不再存在。

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

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

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

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

  而且,通過指針捕獲異常也不符合C++語言本身的規(guī)范。四個標準的異常――bad_alloc(當(dāng)operator new(參見條款8)不能分配足夠的內(nèi)存時,被拋出),bad_cast(當(dāng)dynamic_cast針對一個引用(reference)操作失敗時,被拋出),bad_typeid(當(dāng)dynamic_cast對空指針進行操作時,被拋出)和bad_exception(用于unexpected異常; 參見條款14)――都不是指向?qū)ο蟮闹羔槪阅惚仨毻ㄟ^值或引用來捕獲它們。



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

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

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

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

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

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

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

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

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

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

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

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