首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 進階:參數傳遞與異常傳遞的本質差異

     編輯:關於C++

語法相似,語意迥異

函數參數宣告與 catch 子句參數宣告語法幾乎相同(Widget, Widget&, const Widget&, Widget*...),但底層機制截然不同。核心差異在於:函數呼叫控制權最終返回呼叫端,異常拋出則永不返回。

差異一:拷貝次數與物件生命週期


Widget localWidget;
cin >> localWidget;      // 傳參考,無拷貝,操作直接作用於 localWidget
throw localWidget;       // 拋出異常:無論 catch by value 或 reference,皆會拷貝一份暫時物件

原因: localWidget 離開作用域時會被解構。若直接傳遞原物件給 catch,接收到的將是已解構的「屍體」。C++ 標準強制異常物件必須被複製,存放於例外處理機制管理的儲存區(通常在堆上),確保 catch 接收到有效物件。

  • 傳值捕獲: 兩次拷貝(暫時物件 → catch 參數)。
  • 傳參考捕獲: 一次拷貝(暫時物件),catch 參數綁定該暫時物件。
  • 靜態/全域物件拋出: 仍會拷貝,catch 無法修改原物件。

差異二:靜態類型決定拷貝行為(切割問題)


class Widget { ... };
class SpecialWidget : public Widget { ... };

SpecialWidget sw;
Widget& rw = sw;
throw rw; // 拋出類型為 Widget (rw 的靜態類型),而非 SpecialWidget!

拷貝建構函數依靜態類型 (static type) 選擇,而非動態類型。這與 C++ 其他拷貝語境一致,但極易導致物件切割,衍生類特有資料遺失。

差異三:重新拋出的語意差異


catch (Widget& w) { throw; }   // 重新拋出「當前異常物件」,類型保持原樣 (可能是 SpecialWidget),無額外拷貝
catch (Widget& w) { throw w; } // 拋出 w 的「新拷貝」,類型固定為 Widget (w 的靜態類型),有拷貝開銷

永遠使用 throw; 重新拋出,保留原始異常類型與效能。

差異四:臨時物件可綁定非 const 參考

函數參數:臨時物件不可綁定非 const 參考 (需 const Widget&)。
異常捕獲:允許 catch (Widget& w) 捕獲臨時異常物件。這是標準特例。

差異五:類型轉換規則嚴格不同

語境允許的隱式轉換
函數呼叫標準轉換、使用者定義轉換、int → double 等算術轉換
異常匹配僅兩類: 1. 公開繼承關係 (派生類 → 基類) 2. 指標轉 void*/const void*

throw 10;           // int 異常
catch (double d) {} // 無法捕獲!不進行 int→double 轉換
catch (int i) {}    // 必須精確匹配 int 或 int&

差異六:匹配順序 —「最先匹配」而非「最佳匹配」

虛擬函數:依動態類型選最佳匹配 (最衍生的覆寫)。
catch 子句:依原始碼出現順序,第一個匹配即執行。


try { ... }
catch (logic_error& ex) { ... }      // 基類,放前面會攔截所有派生類
catch (invalid_argument& ex) { ... } // 派生類,永不被執行!

正確順序: 派生類 catch 放前面,基類放後面。編譯器通常會警告順序錯誤。

總結比較表

特性函數參數傳遞異常傳遞
控制權流向返回呼叫端單向傳播,不返回
拷貝次數傳值 1 次,傳參考 0 次至少 1 次 (暫時物件),傳值 2 次
靜態/動態類型參數類型固定拷貝依靜態類型,易切割
隱式類型轉換豐富極少 (僅繼承與 void*)
匹配策略最佳匹配 (重載解析)最先匹配 (原始碼順序)
臨時物件綁定僅限 const 參考可綁定非 const 參考

理解這些差異,才能正確設計異常介面、避免切割陷阱、撰寫高效的 catch 區塊。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved