構造函數與拷貝控制是 C++ 類設計的基石。本文通過實例講解構造函數特性、淺拷貝陷阱、深拷貝實現及現代 C++ 最佳實踐。
const 或 virtual(析構函數可虛)const、引用成員及基類子對象的方式,性能優於構造函數體內賦值類包含指針成員時,默認拷貝構造函數執行逐位拷貝(淺拷貝),導致多個對象共享同一塊堆內存:
class Person {
public:
Person(int n, const string& str) : age(n), name(new string(str)) {}
~Person() { delete name; }
private:
int age;
string* name; // 動態分配
};
int main() {
Person p1(10, "SCOTT");
Person p2 = p1; // 調用默認拷貝構造:淺拷貝 name 指針
return 0; // p2、p1 依次析構,雙重 delete 同一地址 → 崩潰
}
運行結果:
Init Person ~Person name: SCOTT age: 10 Segmentation fault (core dumped)
自定義拷貝構造函數,為指針成員分配新內存並複製內容:
Person(const Person& other) {
if (other.name) {
name = new string(*other.name); // 深拷貝字符串內容
age = other.age;
} else {
name = nullptr;
age = 0;
}
}
現在每個對象擁有獨立的 string 堆內存,析構時互不干擾。
| C++98 三法則 | C++11 五法則 | 說明 |
|---|---|---|
| 拷貝構造函數 | 拷貝構造函數 | 深拷貝資源 |
| 拷貝賦值運算符 | 拷貝賦值運算符 | 釋放舊資源,深拷貝新資源 |
| 析構函數 | 析構函數 | 釋放資源 |
| 移動構造函數 | 轉移資源所有權(noexcept) | |
| 移動賦值運算符 | 轉移資源所有權(noexcept) |
原則:若類管理資源(堆內存、文件句柄、Socket 等),必須顯式實現上述操作;否則改用智能指針(unique_ptr/shared_ptr)或標準容器,讓編譯器合成的操作自動正確(Rule of Zero)。
class Person {
public:
Person(int n, string str) : age(n), name(std::make_unique<string>(std::move(str))) {}
// 無需顯式聲明拷貝/移動/析構,unique_ptr 自動管理
// 若需拷貝語義,改用 shared_ptr 或實現深拷貝
private:
int age;
std::unique_ptr<string> name; // 單一所有權,禁止拷貝,允許移動
};
std::unique_ptr 表達獨占所有權,std::shared_ptr 表達共享所有權