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

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

C++中理解“傳遞參數”和異常之間的差異

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

  從語法上看,在函數里聲明參數與在catch子句中聲明參數幾乎沒有什么差別:

class Widget { ... }; //一個類,具體是什么類
// 在這里并不重要
void f1(Widget w); // 一些函數,其參數分別為
void f2(Widget& w); // Widget, Widget&,或
void f3(const Widget& w); // Widget* 類型
void f4(Widget *pw);
void f5(const Widget *pw);
catch (Widget w) ... //一些catch 子句,用來
catch (Widget& w) ... //捕捉異常,異常的類型為
catch (const Widget& w) ... // Widget, Widget&, 或
catch (Widget *pw) ... // Widget*
catch (const Widget *pw) ...
  你因此可能會認為用throw拋出一個異常到catch子句中與通過函數調用傳遞一個參數兩者基本相同。這里面確有一些相同點,但是他們也存在著巨大的差異。

  讓我們先從相同點談起。你傳遞函數參數與異常的途徑可以是傳值、傳遞引用或傳遞指針,這是相同的。但是當你傳遞參數和異常時,系統所要完成的操作過程則是完全不同的。產生這個差異的原因是:你調用函數時,程序的控制權最終還會返回到函數的調用處,但是當你拋出一個異常時,控制權永遠不會回到拋出異常的地方。

  有這樣一個函數,參數類型是Widget,并拋出一個Widget類型的異常:

// 一個函數,從流中讀值到Widget中
istream Operator>>(istream& s, Widget& w);
void passAndThrowWidget()
{
 Widget localWidget;
 cin >> localWidget; //傳遞localWidget到 operator>>
 throw localWidget; // 拋出localWidget異常
}
  當傳遞localWidget到函數operator>>里,不用進行拷貝操作,而是把operator>>內的引用類型變量w指向localWidget,任何對w的操作實際上都施加到localWidget上。這與拋出localWidget異常有很大不同。不論通過傳值捕捉異常還是通過引用捕捉(不能通過指針捕捉這個異常,因為類型不匹配)都將進行lcalWidget的拷貝操作,也就說傳遞到catch子句中的是localWidget的拷貝。必須這么做,因為當localWidget離開了生存空間后,其析構函數將被調用。假如把localWidget本身(而不是它的拷貝)傳遞給catch子句,這個子句接收到的只是一個被析構了的Widget,一個Widget的“尸體”。這是無法使用的。因此C++規范要求被做為異常拋出的對象必須被復制。

  即使被拋出的對象不會被釋放,也會進行拷貝操作。例如假如passAndThrowWidget函數聲明localWidget為靜態變量(static),

void passAndThrowWidget()
{
 static Widget localWidget; // 現在是靜態變量(static);
 //一直存在至程序結束
 cin >> localWidget; // 象以前那樣運行
 throw localWidget; // 仍將對localWidget
} //進行拷貝操作
  當拋出異常時仍將復制出localWidget的一個拷貝。這表示即使通過引用來捕捉異常,也不能在catch塊中修改localWidget;僅僅能修改localWidget的拷貝。對異常對象進行強制復制拷貝,這個限制有助于我們理解參數傳遞與拋出異常的第二個差異:拋出異常運行速度比參數傳遞要慢。

  當異常對象被拷貝時,拷貝操作是由對象的拷貝構造函數完成的。該拷貝構造函數是對象的靜態類型(static type)所對應類的拷貝構造函數,而不是對象的動態類型(dynamic type)對應類的拷貝構造函數。比如以下這經過少許修改的passAndThrowWidget:

class Widget { ... };
class SpecialWidget: public Widget { ... };
void passAndThrowWidget()
{
 SpecialWidget localSpecialWidget;
 ...
 Widget& rw = localSpecialWidget; // rw 引用SpecialWidget
 throw rw; //它拋出一個類型為Widget
 // 的異常
}
  這里拋出的異常對象是Widget,即使rw引用的是一個SpecialWidget。因為rw的靜態類型(static type)是Widget,而不是SpecialWidget。你的編譯器根本沒有主要到rw引用的是一個SpecialWidget。編譯器所注重的是rw的靜態類型(static type)。這種行為可能與你所期待的不一樣,但是這與在其他情況下C++中拷貝構造函數的行為是一致的。

  異常是其它對象的拷貝,這個事實影響到你如何在catch塊中再拋出一個異常。比如下面這兩個catch塊,乍一看似乎一樣:

catch (Widget& w) // 捕捉Widget異常
{
 ... // 處理異常
 throw; // 重新拋出異常,讓它
} // 繼續傳遞
catch (Widget& w) // 捕捉Widget異常
{
 ... // 處理異常
 throw w; // 傳遞被捕捉異常的
} // 拷貝
  這兩個catch塊的差別在于第一個catch塊中重新拋出的是當前捕捉的異常,而第二個catch塊中重新拋出的是當前捕捉異常的一個新的拷貝。假如忽略生成額外拷貝的系統開銷,這兩種方法還有差異么?

  當然有。第一個塊中重新拋出的是當前異常(current exception),無論它是什么類型。非凡是假如這個異常開始就是做為SpecialWidget類型拋出的,那么第一個塊中傳遞出去的還是SpecialWidget異常,即使w的靜態類型(static type)是Widget。這是因為重新拋出異常時沒有進行拷貝操作。第二個catch塊重新拋出的是新異常,類型總是Widget,因為w的靜態類型(static type)是Widget。一般來說,你應該用throw來重新拋出當前的異常,因為這樣不會改變被傳遞出去的異常類型,而且更有效率,因為不用生成一個新拷貝。

  (順便說一句,異常生成的拷貝是一個臨時對象。正如條款19解釋的,臨時對象能讓編譯器優化它的生存期(optimize it out of existence),不過我想你的編譯器很難這么做,因為程序中很少發生異常,所以編譯器廠商不會在這方面花大量的精力。)

  讓我們測試一下下面這三種用來捕捉Widget異常的catch子句,異常是做為passAndThrowWidgetp拋出的:


catch (Widget w) ... // 通過傳值捕捉異常
catch (Widget& w) ... // 通過傳遞引用捕捉
// 異常
catch (const Widget& w) ... //通過傳遞指向const的引用
//捕捉異常
  我們馬上注重到了傳遞參數與傳遞異常的另一個差異。一個被異常拋出的對象(剛才解釋過,總是一個臨時對象)可以通過普通的引用捕捉;它不需要通過指向const對象的引用(reference-to-const)捕捉。在函數調用中不答應轉遞一個臨時對象到一個非const引用類型的參數里,但是在異常中卻被答應。

  讓我們先不管這個差異,回到異常對象拷貝的測試上來。我們知道當用傳值的方式傳遞函數的參數,我們制造了被傳遞對象的一個拷貝(參見Effective C++ 條款22),并把這個拷貝存儲到函數的參數里。同樣我們通過傳值的方式傳遞一個異常時,也是這么做的。當我們這樣聲明一個catch子句時:

  catch (Widget w) ... // 通過傳值捕捉
  會建立兩個被拋出對象的拷貝,一個是所有異常都必須建立的臨時對象,第二個是把臨時對象拷貝進w中。同樣,當我們通過引用捕捉異常時,

catch (Widget& w) ... // 通過引用捕捉
catch (const Widget& w) ... //也通過引用捕捉
  這仍然會建立一個被拋出對象的拷貝:拷貝是一個臨時對象。相反當我們通過引用傳遞函數參數時,沒有進行對象拷貝。當拋出一個異常時,系統構造的(以后會析構掉)被拋出對象的拷貝數比以相同對象做為參數傳遞給函數時構造的拷貝數要多一個。

  我們還沒有討論通過指針拋出異常的情況,不過通過指針拋出異常與通過指針傳遞參數是相同的。不論哪種方法都是一個指針的拷貝被傳遞。你不能認為拋出的指針是一個指向局部對象的指針,因為當異常離開局部變量的生存空間時,該局部變量已經被釋放。Catch子句將獲得一個指向已經不存在的對象的指針。這種行為在設計時應該予以避免。

  對象從函數的調用處傳遞到函數參數里與從異常拋出點傳遞到catch子句里所采用的方法不同,這只是參數傳遞與異常傳遞的區別的一個方面,第二個差異是在函數調用者或拋出異常者與被調用者或異常捕捉者之間的類型匹配的過程不同。比如在標準數學庫(the standard math library)中sqrt函數:

double sqrt(double); // from or
  我們能這樣計算一個整數的平方根,如下所示:

int i;
double sqrtOfi = sqrt(i);
  毫無疑問,C++答應進行從int到double的隱式類型轉換,所以在sqrt的調用中,i 被靜靜地轉變為double類型,并且其返回值也是double。(有關隱式類型轉換的具體討論參見條款5)一般來說,catch子句匹配異常類型時不會進行這樣的轉換。見下面的代碼:

void f(int value)
{
 try {
  if (someFunction()) { // 假如 someFunction()返回
  throw value; //真,拋出一個整形值
  ...
  }
 }
 catch (double d) { // 只處理double類型的異常
  ...
 }
 ...
}
  在try塊中拋出的int異常不會被處理double異常的catch子句捕捉。該子句只能捕捉真真正正為double類型的異常;不進行類型轉換。因此假如要想捕捉int異常,必須使用帶有int或int&參數的catch子句。

  不過在catch子句中進行異常匹配時可以進行兩種類型轉換。第一種是繼續類與基類間的轉換。一個用來捕捉基類的catch子句也可以處理派生類類型的異常。例如在標準C++庫(STL)定義的異常類層次中的診斷部分(diagnostics portion )(參見Effective C++ 條款49)。

  捕捉runtime_errors異常的Catch子句可以捕捉range_error類型和overflow_error類型的異常,可以接收根類exception異常的catch子句能捕捉其任意派生類異常。

  這種派生類與基類(inheritance_based)間的異常類型轉換可以作用于數值、引用以及指針上:

catch (runtime_error) ... // can catch errors of type
catch (runtime_error&) ... // runtime_error,
catch (const runtime_error&) ... // range_error, or
// overflow_error
catch (runtime_error*) ... // can catch errors of type
catch (const runtime_error*) ... // runtime_error*,
// range_error*, or
// overflow_error*
  第二種是答應從一個類型化指針(typed pointer)轉變成無類型指針(untyped pointer),所以帶有const void* 指針的catch子句能捕捉任何類型的指針類型異常:

  catch (const void*) ... //捕捉任何指針類型異常

  傳遞參數和傳遞異常間最后一點差別是catch子句匹配順序總是取決于它們在程序中出現的順序。因此一個派生類異常可能被處理其基類異常的catch子句捕捉,即使同時存在有能處理該派生類異常的catch子句,與相同的try塊相對應。例如:

try {
 ...
}
catch (logic_error& ex) { // 這個catch塊 將捕捉
 ... // 所有的logic_error
} // 異常, 包括它的派生類
catch (invalid_argument& ex) { // 這個塊永遠不會被執行
 ... //因為所有的
} // invalid_argument
// 異常 都被上面的
// catch子句捕捉。
  與上面這種行為相反,當你調用一個虛擬函數時,被調用的函數位于與發出函數調用的對象的動態類型(dynamic type)最相近的類里。你可以這樣說虛擬函數采用最優適合法,而異常處理采用的是最先適合法。假如一個處理派生類異常的catch子句位于處理基類異常的catch子句前面,編譯器會發出警告。(因為這樣的代碼在C++里通常是不合法的。)不過你最好做好預先防范:不要把處理基類異常的catch子句放在處理派生類異常的catch子句的前面。象上面那個例子,應該這樣去寫:


try {
 ...
}
catch (invalid_argument& ex) { // 處理 invalid_argument
 ... //異常
}
catch (logic_error& ex) { // 處理所有其它的
 ... // logic_errors異常
}
  綜上所述,把一個對象傳遞給函數或一個對象調用虛擬函數與把一個對象做為異常拋出,這之間有三個主要區別。第一、異常對象在傳遞時總被進行拷貝;當通過傳值方式捕捉時,異常對象被拷貝了兩次。對象做為參數傳遞給函數時不需要被拷貝。第二、對象做為異常被拋出與做為參數傳遞給函數相比,前者類型轉換比后者要少(前者只有兩種轉換形式)。最后一點,catch子句進行異常類型匹配的順序是它們在源代碼中出現的順序,第一個類型匹配成功的catch將被用來執行。當一個對象調用一個虛擬函數時,被選擇的函數位于與對象類型匹配最佳的類里,即使該類不是在源代碼的最前頭。

上一篇:JOSEPH環

下一篇:實例解析C++/CLI之值類型

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲国产精品久久久久秋霞不卡| 欧美一级黑人aaaaaaa做受| 成人xvideos免费视频| 久久99视频免费| www.色综合| 欧美日韩一区二区三区| 粉嫩av一区二区三区免费野| 一区二区欧美在线| 国产亚洲精品久久| 国产精品福利网站| 51久久精品夜色国产麻豆| 亚洲精品永久免费| 亚洲国产精品久久久久秋霞蜜臀| 日韩国产欧美精品在线| 国产精品男女猛烈高潮激情| 91亚洲va在线va天堂va国| 亚洲精品久久久一区二区三区| 97精品久久久中文字幕免费| 成人黄色av播放免费| 久久最新资源网| 人人做人人澡人人爽欧美| 亚洲欧洲在线视频| 精品欧美国产一区二区三区| 亚洲女人天堂成人av在线| 97视频免费观看| 国产精品成人v| 色综合亚洲精品激情狠狠| 中文字幕久久久av一区| 国产精品美女久久久久久免费| 日韩在线免费视频观看| 久久综合久久八八| 日韩av电影手机在线| 亚洲一区二区久久久| 亚洲黄页网在线观看| 日韩视频在线观看免费| 亚洲乱码国产乱码精品精天堂| 日韩在线播放av| 精品国产视频在线| 国产美女91呻吟求| 92版电视剧仙鹤神针在线观看| 亚洲在线视频福利| 97视频在线观看亚洲| 福利精品视频在线| 久久久www成人免费精品| 亚洲欧美日韩国产中文| 丰满岳妇乱一区二区三区| 国产精品国产三级国产aⅴ9色| 亚洲精品网站在线播放gif| 91在线视频一区| 国产一区视频在线| 国产免费成人av| 57pao成人国产永久免费| 亚洲精品日韩丝袜精品| 亚洲成人久久一区| 亚洲网在线观看| 成人h视频在线| 国产深夜精品福利| 日韩精品免费在线| 国产网站欧美日韩免费精品在线观看| 欧美黄色成人网| 日韩美女在线播放| 久久99精品久久久久久噜噜| 亚洲国产精品人久久电影| 欧美性猛交xxxx久久久| 欧美资源在线观看| 久久大大胆人体| 久久九九热免费视频| 亚洲乱码国产乱码精品精天堂| 成人国产在线激情| 日韩欧美一区二区三区久久| 亚洲精品午夜精品| 日韩视频一区在线| 国产精品v片在线观看不卡| 成人激情av在线| 国产丝袜精品第一页| 中日韩美女免费视频网址在线观看| 91精品国产91久久久久| 欧美日韩国产色| 国产精品久久久久秋霞鲁丝| 日韩免费在线观看视频| 97高清免费视频| 美女少妇精品视频| 狠狠躁18三区二区一区| 欧美国产亚洲精品久久久8v| 亚洲色在线视频| 亚洲国产成人av在线| 91夜夜揉人人捏人人添红杏| 精品久久久久久亚洲国产300| 国产91精品久久久久久| 久久久久久高潮国产精品视| 8050国产精品久久久久久| 性金发美女69hd大尺寸| 欧美乱妇高清无乱码| 欧美日韩国产麻豆| 国产精品69久久| 亚洲成av人片在线观看香蕉| 国产成人精品一区| 国产精品久久91| 欧美日韩国产一区中文午夜| 亚洲精品日韩激情在线电影| 久久福利视频网| 亚洲国模精品一区| 欧美老女人www| 国产一区二区三区在线观看网站| 国产精品老女人精品视频| 精品国产自在精品国产浪潮| 亚洲精品中文字幕有码专区| 亚洲精品欧美日韩专区| 伊人久久久久久久久久久| 亚洲va码欧洲m码| 7m精品福利视频导航| 91精品啪在线观看麻豆免费| 91精品国产综合久久香蕉最新版| 欧美激情18p| 成人免费自拍视频| 国产精品流白浆视频| 欧美日本高清视频| 亚洲精品www久久久| 亚洲男子天堂网| 久久久久久有精品国产| 欧美在线视频在线播放完整版免费观看| 久久夜色精品国产| 国外成人在线视频| 欧美精品videosex性欧美| 亚洲成av人影院在线观看| 欧美重口另类videos人妖| 精品国偷自产在线视频99| 性色av一区二区三区红粉影视| 在线免费看av不卡| 日韩视频在线一区| 欧美电影免费观看网站| 国产成人aa精品一区在线播放| 九色91av视频| 奇米四色中文综合久久| 成人有码在线播放| 国产精品高潮呻吟久久av黑人| 国产成+人+综合+亚洲欧洲| 国产成人在线亚洲欧美| 久久精品中文字幕免费mv| 国产91成人video| 亚洲成色999久久网站| 欧美综合国产精品久久丁香| 欧美日韩一区免费| 欧美精品久久一区二区| 亚洲经典中文字幕| 国产美女主播一区| 日韩在线观看电影| 成人信息集中地欧美| 成人免费网站在线观看| 米奇精品一区二区三区在线观看| 2019中文字幕在线免费观看| 亚洲乱码一区二区| 日本不卡免费高清视频| 国产日韩欧美另类| 日本精品视频在线观看| 亚洲国产精品成人va在线观看| 精品亚洲一区二区三区在线观看| 欧美日韩免费看| 亚洲男人天堂古典| 精品国产精品三级精品av网址| 欧美激情亚洲视频| 8x海外华人永久免费日韩内陆视频| 亚洲成人激情小说| 欧美日韩国产999|