#include <cstring>
using namespace std;
class Name {
char *str;
public:
Name(const char *n) {
str = new char[strlen(n) + 1];
strcpy(str, n);
}
~Name() { delete[] str; }
};
int main() {
Name a("peter");
if (true) {
Name b = a; // 呼叫預設拷貝建構(淺拷貝)
} // b 解構,釋放 str 記憶體
// a 解構,再次釋放同一記憶體 → 雙重釋放崩潰!
return 0;
}
| 階段 | 預設拷貝行為 | 後果 |
|---|---|---|
| 建構 a | 分配記憶體,str 指向 "peter" | 正常 |
| 拷貝建構 b | 逐位元複製 str 指標值 | a.str == b.str,指向同一塊記憶體 |
| b 解構 | delete[] str | 記憶體被釋放,a.str 變成野指標 |
| a 解構 | delete[] str | 雙重釋放,未定義行為,程式崩潰 |
#include <cstring>
using namespace std;
class Name {
char *str;
public:
// 一般建構
Name(const char *n) {
str = new char[strlen(n) + 1];
strcpy(str, n);
}
// 深拷貝建構函式
Name(const Name& other) {
str = new char[strlen(other.str) + 1];
strcpy(str, other.str); // 分配新記憶體並複製內容
}
~Name() { delete[] str; }
};
int main() {
Name a("peter");
if (true) {
Name b = a; // 呼叫深拷貝建構
} // b 解構,釋放自己的記憶體
// a 解構,釋放自己的記憶體 → 正常!
return 0;
}
【深拷貝版本】
Name a("peter"); // 建構 1 次
Name b = a; // 拷貝建構 1 次(分配新記憶體)
} // b 解構 1 次(釋放 b.str)
} // a 解構 1 次(釋放 a.str)
總計:建構 2 次,解構 2 次,記憶體獨立管理,零錯誤
若類別管理資源(動態記憶體、檔案控制代、Socket 等),必須自定義:
class Name {
char *str;
public:
Name(const char *n) : str(nullptr) {
if (n) {
str = new char[strlen(n) + 1];
strcpy(str, n);
}
}
// 拷貝建構
Name(const Name& other) : str(nullptr) {
if (other.str) {
str = new char[strlen(other.str) + 1];
strcpy(str, other.str);
}
}
// 拷貝賦值(複製並交換慣用語)
Name& operator=(Name other) { // 參數傳值,自動拷貝
swap(str, other.str); // 交換資源
return *this; // other 解構時釋放舊資源
}
// 移動建構 (C++11)
Name(Name&& other) noexcept : str(other.str) {
other.str = nullptr;
}
// 移動賦值 (C++11)
Name& operator=(Name&& other) noexcept {
if (this != &other) {
delete[] str;
str = other.str;
other.str = nullptr;
}
return *this;
}
~Name() { delete[] str; }
};
std::unique_ptr/shared_ptr 自動管理記憶體,編譯器自動生成正確的拷貝/移動語義std::string, std::vector 內建深拷貝總結
拷貝建構函式是 C++ 資源管理的核心。淺拷貝導致雙重釋放是經典災難。掌握深拷貝實作、複製並交換慣用語、移動語義與Rule of Zero現代實務,能從根本上避免記憶體管理錯誤,寫出安全高效的 C++ 程式碼。