代碼場景
class Coord {
public:
Coord(int a=0, int b=0) : x(a), y(b) {}
Coord(const Coord& p) : x(p.x), y(p.y) { cout << "Copy Ctor\n"; }
~Coord() { cout << "Dtor\n"; }
void print() const { cout << "(" << x << "," << y << ")\n"; }
int getX() const { return x; }
int getY() const { return y; }
private:
int x, y;
};
void fun1(Coord p) { // 值傳遞
cout << "fun1: "; p.print();
}
void fun2(Coord& p) { // 引用傳遞
cout << "fun2: "; p.print();
}
void fun3(Coord* p) { // 指針傳遞
cout << "fun3: "; p->print();
}
Coord fun4() { // 返回值
Coord tmp(10, 20);
return tmp; // NRVO/RVO 優化
}
int main() {
Coord c1(1, 2);
cout << "=== 值傳遞 ===\n"; fun1(c1);
cout << "=== 引用傳遞 ===\n"; fun2(c1);
cout << "=== 指針傳遞 ===\n"; fun3(&c1);
cout << "=== 返回值 ===\n"; Coord c2 = fun4(); c2.print();
return 0;
}
調用過程分析
1. 值傳遞 fun1(c1)
- 調用拷貝構造函數創建臨時對象
p(輸出 "Copy Ctor")。
- 函數內操作副本,不影響原對象。
- 函數結束,調用析構函數銷毀
p(輸出 "Dtor")。
2. 引用傳遞 fun2(c1)
- 無拷貝,
p 直接綁定到 c1。
- 函數內可修改
c1。
- 無額外構造/析構開銷。
3. 指針傳遞 fun3(&c1)
- 傳遞地址值(拷貝指針,極輕量)。
- 函數內通過指針訪問/修改原對象。
- 需注意空指針檢查。
4. 返回值 fun4()
- 理論上:構造
tmp,拷貝構造返回臨時對象,析構 tmp,移動構造/拷貝構造初始化 c2,析構臨時對象。
- 實際上:編譯器啟用 RVO (Return Value Optimization) / NRVO,直接在調用者棧幀構造
c2,零拷貝。輸出無 "Copy Ctor"。
- C++17 保證 RVO;C++11 起引入移動語義,即使不優化也調用移動構造(若定義)。
拷貝構造函數 vs 賦值運算符
Coord c3 = c1; // 拷貝構造(初始化)
Coord c4; c4 = c1; // 賦值運算符(先構造 c4,再賦值)
- 拷貝構造:創建新對象。
- 賦值運算符:修改已存在對象,需處理自賦值、釋放舊資源、分配新資源。
最佳實踐
- 參數傳遞:優先 const 引用(
const Coord&),避免拷貝且保護原對象。
- 需修改原對象:非 const 引用。
- 需可選參數/空值:指針(或
std::optional C++17)。
- 返回對象:直接返回值,信任 RVO/移動語義,勿手動
new 返回指針。
- 類管理資源(指針、句柄):必須實現拷貝構造、賦值運算符、析構函數(三大法則),C++11 另加移動構造、移動賦值(五大法則)。