C 程式設計師習慣用 memcpy 複製陣列:
T src[N], dest[N];
memcpy(dest, src, N * sizeof(T)); // C 風格
在 C 中完美運作,但在 C++ 中若 T 為非基本類型,將引發淺拷貝災難:雙重釋放、記憶體洩漏、資料損壞。
類型 T 滿足任一條件即禁止 memcpy:
T 顯式定義了複製建構函式或賦值運算子T 未顯式定義,但其資料成員需要顯式複製/賦值(如成員含 std::string、std::vector、裸指標資源)
#include
#include
using namespace std;
/* 類別 B:管理動態記憶體,需深拷貝 */
class B {
int *a;
public:
B(int m = 0) { cout << "B建構\n"; a = new int; *a = m; }
B(const B& b) { cout << "B複製建構\n"; a = new int; *a = *(b.a); }
B& operator=(const B& b) {
cout << "B賦值\n";
if (&b != this) { delete a; a = new int; *a = *(b.a); }
return *this;
}
~B() { cout << "B解構\n"; delete a; }
int* get_ptr() const { return a; }
int get_val() const { return *a; }
};
/* 類別 A:未顯式定義複製/賦值/解構,但成員 B 需深拷貝 */
class A {
B b;
public:
A(int m = 0) : b(m) {}
B& get_b() { return b; }
};
int main() {
A a1(1), a2(2);
cout << "a1: " << a1.get_b().get_ptr() << " " << a1.get_b().get_val() << endl;
cout << "a2: " << a2.get_b().get_ptr() << " " << a2.get_b().get_val() << endl;
a1 = a2; // 編譯器生成的預設賦值會遞迴呼叫 B::operator=
// memcpy(&a1, &a2, sizeof(A)); // 災難!位元複製導致 a1.b.a 指向 a2.b.a 同一塊記憶體
cout << "賦值後 a1: " << a1.get_b().get_ptr() << " " << a1.get_b().get_val() << endl;
cout << "賦值後 a2: " << a2.get_b().get_ptr() << " " << a2.get_b().get_val() << endl;
A a3(1);
A a4(a3); // 複製建構遞迴呼叫 B 複製建構
cout << "a3: " << a3.get_b().get_ptr() << " " << a3.get_b().get_val() << endl;
cout << "a4: " << a4.get_b().get_ptr() << " " << a4.get_b().get_val() << endl;
return 0;
}
正確賦值/複製建構輸出: B建構 B建構 a1: 0x... 1 a2: 0x... 2 B賦值 a1: 0x... 2 <-- 新記憶體,值獨立 a2: 0x... 2 B建構 B複製建構 a3: 0x... 1 a4: 0x... 1 <-- 新記憶體,值獨立 B解構 x4 若使用 memcpy(&a1, &a2, sizeof(A)): a1.b.a 與 a2.b.a 指向同一地址 雙重 delete → heap corruption / crash a1 修改值會影響 a2
| 類別類型 | TriviallyCopyable | 可用 memcpy | 建議 |
|---|---|---|---|
| 基本類型 | 是 | 是 | 放心用 |
| POD (Plain Old Data) | 是 | 是 | C 風格結構體 |
| 含 std::string/vector | 否 | 否 | 必須用複製建構/賦值 |
| 含裸指標資源 | 否 | 否 | 遵循 Rule of Three/Five |
| 含虛函式/虛基類 | 否 | 否 | vptr 複製會破壞多型 |
// 1. 標準演算法(推薦)
#include
#include
vector dest = src; // 或
copy(begin(src), end(src), begin(dest));
// 2. 陣列專用
array dest = src; // C++11
// 3. 手寫迴圈(效能敏感且確認 TriviallyCopyable)
for (size_t i = 0; i < N; ++i) dest[i] = src[i]; // 呼叫複製建構/賦值
memcpy 是位元級複製,不呼叫建構/解構/賦值。任何管理資源(記憶體、檔案、鎖、句柄)的類型,使用 memcpy 即為未定義行為。
養成習慣:std::copy、std::vector、std::array、RAII 智慧指標。將 memcpy 限制在 char 緩衝區、網路封包、硬體介面等純位元資料場景。