国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

More Effective C++:通過引用捕獲異常

2019-11-17 05:45:21
字體:
供稿:網(wǎng)友

  當(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ì)不能刪除它,否則程序的行為將不可猜測(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)!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 隆回县| 基隆市| 马尔康县| 禹州市| 普定县| 师宗县| 包头市| 疏附县| 涞水县| 浮梁县| 云梦县| 晴隆县| 右玉县| 清流县| 老河口市| 丹凤县| 同德县| 汉中市| 旬阳县| 车致| 许昌市| 鄂温| 邓州市| 嘉善县| 曲阳县| 天长市| 防城港市| 东光县| 通山县| 灌阳县| 寿宁县| 漳平市| 云阳县| 康马县| 莫力| 垦利县| 灯塔市| 华池县| 尤溪县| 武隆县| 阿合奇县|