參數傳遞是函數介面設計的核心,直接影響程式的正確性、效能與可維護性。C++ 提供值傳遞、指標傳遞、引用傳遞三種模式,各有其語意契約與適用場景。本文深度剖析三者機制差異、效能特性、常見誤區與現代 C++ 選擇指南。
int m = 10;
int& n = m; // n 是 m 的別名 (alias),非拷貝、非指標
n = 20; // 直接修改 m,m 變為 20
int& r; 編譯錯誤,必須 int& r = obj;。r = other 是賦值操作,非重新綁定。| 特性 | 引用 | 指標 |
|---|---|---|
| 語法 | T& | T* |
| 初始化 | 強制、宣告時 | 可延遲、可為 nullptr |
| 重新綁定 | 不可 | 可隨時修改 |
| 運算子 | 透明存取 (如同物件) | 需解參考 */-> |
| 記憶體 | 通常無額外開銷 (編譯器優化) | 需儲存位址 (通常 8 bytes) |
| 適用場景 | 別名、參數傳遞、回傳值 | 動態記憶體、可選參數、資料結構節點 |
void Func1(int x) { x = x + 10; } // 操作副本
int n = 0;
Func1(n); // n 仍為 0
std::pair)、需修改副本的演算法 (如排序)。
void Func2(int* x) { *x = *x + 10; } // 解參考修改原物件
int n = 0;
Func2(&n); // n 變為 10
nullptr)、輸出參數、非擁有式觀察、C API 互操作。
void Func3(int& x) { x = x + 10; } // 直接操作原物件
int n = 0;
Func3(n); // n 變為 10
operator+=)、串流操作。const T&)、不能為空。
void Process(const BigObject& obj) { // 唯讀存取,避免複製
// obj 只能呼叫 const 成員函數
}
const T&** (大型物件)。| 場景 | 推薦模式 | 範例 | 理由 |
|---|---|---|---|
| 基本型別 / 小型可複製型別 | 值傳遞 | void f(int x) | 複製極快,語意清晰 |
| 大型物件唯讀 | const 引用 | void f(const Big& x) | 避免複製,接受臨時物件 |
| 需修改呼叫端物件 | 非 const 引用 | void f(Big& x) | 明確輸入輸出語意 |
| 可選參數 / 輸出參數 | 指標 / std::optional | void f(Big* out) / std::optional | 可表達「無值」,C++17 optional 更安全 |
| 轉移所有權 (Sink 參數) | 值傳遞 + 移動 | void f(Big x) 呼叫 f(std::move(obj)) | 明確所有權轉移,編譯器優化 (NRVO) |
| 通用轉發 (完美轉發) | 萬用引用 | template<class T> void f(T&& x) | 保持值類別,轉發給內部函數 |
Big CreateBig() {
Big b; // 建構
return b; // NRVO: 直接在呼叫端儲存空間建構,零複製/移動
}
Big b = CreateBig(); // 無建構/移動開銷
// 消耗參數並取得所有權
void Sink(Big b) { // 值傳遞
// 使用 b...
} // 離開作用域自動析構
Big obj;
Sink(std::move(obj)); // 明確移動語意,obj 狀態轉為 valid but unspecified
template
std::unique_ptr Make(Args&&... args) {
return std::make_unique(std::forward(args)...);
}
int& Bad() { int x = 10; return x; } // UB: 回傳區域變數引用
// 正確:回傳值、static 物件、或動態配置 (智慧指標)
void Modify(int& x);
Modify(10); // 編譯錯誤:暫時物件不能綁定非 const 引用
// 正確:const int& 或 值傳遞
void Use(int* p) { std::cout << *p; } // 危險
// 正確:assert(p); 或 使用 gsl::not_null (Guideline Support Library)
const T&:避免複製、接受臨時物件。T&:明確 in-out 語意。T* 或 std::optional:nullptr 表達無值。std::move:Sink 參數模式。T&&:完美轉發保持值類別。unique_ptr/shared_ptr 管理動態記憶體。掌握參數傳遞語意,不僅能寫出高效程式碼,更能設計出意圖明確、難以誤用的 API 介面——這正是專業 C++ 開發者的核心競爭力。