**包含動態分配成員(指針、句柄等)的類,必須提供拷貝構造函數並重載賦值運算符**,實現深拷貝,避免雙重釋放、資源洩漏、邏輯錯誤。
class CExample {
public:
CExample() : pBuffer(nullptr), nSize(0) {}
~CExample() { delete[] pBuffer; }
void Init(int n) { pBuffer = new char[n]; nSize = n; }
private:
char* pBuffer;
int nSize;
};
逐位複製指針值 → 兩對象共享同一塊堆內存 → 析構時雙重 `delete[]` → 崩潰。
class CExample {
public:
// ... 原有代碼 ...
CExample(const CExample& rhs); // 聲明
private:
char* pBuffer;
int nSize;
};
CExample::CExample(const CExample& rhs) {
nSize = rhs.nSize;
pBuffer = new char[nSize];
memcpy(pBuffer, rhs.pBuffer, nSize);
}
已構造對象間的賦值:`obj3 = obj1;`(obj3 已存在,需釋放舊資源)。
指針值覆蓋 → 舊內存洩漏、新內存共享 → 雙重釋放。
class CExample {
public:
// ...
CExample& operator=(const CExample& rhs);
};
CExample& CExample::operator=(const CExample& rhs) {
if (this != &rhs) { // 自賦值檢查
char* tmp = new char[rhs.nSize]; // 先分配新,再釋放舊
memcpy(tmp, rhs.pBuffer, rhs.nSize);
delete[] pBuffer;
pBuffer = tmp;
nSize = rhs.nSize;
}
return *this;
}
CExample::CExample(const CExample& rhs) : pBuffer(nullptr), nSize(0) {
*this = rhs; // 復用賦值邏輯
}
優點:邏輯集中維護。缺點:默認構造+賦值比直接初始化稍慢(可忽略)。
#include <algorithm>
#include <utility>
class CExample {
public:
CExample() : pBuffer(nullptr), nSize(0) {}
CExample(const CExample& rhs) : pBuffer(nullptr), nSize(0) { *this = rhs; }
CExample(CExample&& rhs) noexcept : pBuffer(rhs.pBuffer), nSize(rhs.nSize) {
rhs.pBuffer = nullptr; rhs.nSize = 0;
}
CExample& operator=(CExample rhs) noexcept { // by value!
swap(*this, rhs);
return *this;
}
~CExample() { delete[] pBuffer; }
friend void swap(CExample& a, CExample& b) noexcept {
using std::swap;
swap(a.pBuffer, b.pBuffer);
swap(a.nSize, b.nSize);
}
private:
char* pBuffer;
int nSize;
};
class CExample {
std::vector<char> buffer_; // 自動管理內存,無需任何特殊函數
public:
void Init(int n) { buffer_.resize(n); }
// 編譯器合成的拷貝/移動/析構全正確!
};
| 場景 | 必須實現 | 推薦模式 |
|---|---|---|
| 手動管理動態內存 | 拷貝構造、賦值、析構、移動構造、移動賦值 | Copy-and-Swap |
| 用 RAII 類型管理資源 | 無(Rule of Zero) | 直接用 vector/unique_ptr/string |
| 禁止拷貝 | 刪除拷貝構造/賦值 | `= delete` (C++11) 或私有化 (C++98) |