問題代碼
class Text {
private:
char* str;
public:
Text() { str = new char[20]; memset(str, 0, 20); }
void SetText(char* s) { strcpy(str, s); }
char* GetText() const { return str; }
~Text() { delete[] str; }
};
void Print(Text s) { // 傳值:觸發拷貝構造
cout << s.GetText() << endl;
} // s 析構,釋放 str
int main() {
Text t;
t.SetText("abc");
Print(t); // t 的 str 被釋放!
return 0; // t 再次析構 → 雙重釋放 → 崩潰
}
根因分析
Print(Text s) 傳值參數 → 編譯器生成默認拷貝構造函數 → 淺拷貝指針 str
- 兩個對象
t 和 s 指向同一堆內存
Print 結束,s 析構 → delete[] str
main 結束,t 析構 → 再次 delete[] str → **Heap Corruption**
三種解決方案
1. 實現深拷貝 (三大法則/五大法則)
class Text {
// ...
Text(const Text& other) { // 拷貝構造
str = new char[strlen(other.str)+1];
strcpy(str, other.str);
}
Text& operator=(const Text& other) { // 拷貝賦值
if (this != &other) {
delete[] str;
str = new char[strlen(other.str)+1];
strcpy(str, other.str);
}
return *this;
}
// C++11 移動語義
Text(Text&& other) noexcept : str(other.str) { other.str = nullptr; }
Text& operator=(Text&& other) noexcept {
if (this != &other) { delete[] str; str = other.str; other.str = nullptr; }
return *this;
}
};
2. 傳引用避免拷貝
void Print(const Text& s) { // const 引用,無拷貝、無修改
cout << s.GetText() << endl;
}
3. 使用智能指針 / 標準容器 (現代 C++ 首選)
class Text {
std::string str; // 自動管理內存,正確拷貝/移動
public:
void SetText(const char* s) { str = s; }
const char* GetText() const { return str.c_str(); }
// 無需手寫構造/析構/拷貝/移動
};
核心原則
- 管理資源的類必須定義拷貝/移動語義 (Rule of 3/5/0)
- 優先用標準庫容器/智能指針 替代裸指針手動管理
- 函數參數傳遞:大對象用
const T&,需修改用 T&,移動語義用 T&&,避免無意義傳值
- 編譯器警告:開啟
-Wextra -Weffc++ (GCC) 或 /W4 (MSVC) 可捕獲缺失拷貝構造等問題