memcpy 執行「位元級淺拷貝」,僅複製記憶體比特模式。 若類型管理動態資源(指標、檔案句柄、鎖等)或包含此類成員,memcpy 會導致雙重釋放、資源洩漏、邏輯錯誤等未定義行為。
符合以下任一條件即為「非平凡可複製類型」,禁用 memcpy:
std::string、智慧指標等資源管理型別。
#include
#include // memcpy
class Resource {
public:
int* data;
Resource(int v = 0) { data = new int(v); std::cout << "Resource ctor\n"; }
~Resource() { delete data; std::cout << "Resource dtor\n"; }
// 隱式複製建構/賦值 = 淺拷貝(雙指標指向同一記憶體)
};
class Wrapper {
public:
Resource res; // 成員為非 POD
Wrapper(int v = 0) : res(v) {}
// 編譯器隱式產生的複製/賦值會正確複製 res(呼叫 Resource 的複製建構)
// 但 memcpy 會繞過此機制!
};
int main() {
Wrapper a(1), b(2);
std::cout << "Before memcpy: a.res.data=" << a.res.data << " val=" << *a.res.data
<< " b.res.data=" << b.res.data << " val=" << *b.res.data << "\n";
// 災難發生:位元拷貝,a.res.data 指標被 b.res.data 覆蓋
std::memcpy(&a, &b, sizeof(Wrapper));
std::cout << "After memcpy: a.res.data=" << a.res.data << " val=" << *a.res.data
<< " b.res.data=" << b.res.data << " val=" << *b.res.data << "\n";
// 離開作用域:a、b 依序解構 → 同一記憶體 delete 兩次 → 崩潰/堆損壞
return 0;
}
輸出示例:
Resource ctor Resource ctor Before memcpy: a.res.data=0x55a3c2c0 val=1 b.res.data=0x55a3c2e0 val=2 After memcpy: a.res.data=0x55a3c2e0 val=2 b.res.data=0x55a3c2e0 val=2 Resource dtor // b 先解構,釋放 0x55a3c2e0 Resource dtor // a 再解構,再次釋放 0x55a3c2e0 → 雙重釋放,程式崩潰
Wrapper a(1), b(2);
a = b; // 呼叫 Wrapper::operator= → Resource::operator= → 深拷貝,安全
// 或
Wrapper c = b; // 複製建構,安全
#include
static_assert(std::is_trivially_copyable_v == false, "Wrapper 非 trivially copyable");
static_assert(std::is_trivially_copyable_v == true, "int 可 memcpy");
static_assert(std::is_trivially_copyable_v == false, "string 內含指標");
std::is_trivially_copyable:可安全用 memcpy(等價於 C 的 POD 型別)。std::is_trivial + std::is_standard_layout → C++11 起可判斷 POD。| 場景 | 推薦做法 | 禁忌 |
|---|---|---|
| 結構體僅含基本型別 | memcpy / std::copy / 直接賦值 | — |
含 std::string/容器/智慧指標 | 直接賦值 / std::move | memcpy |
| 自訂資源管理類別 | 實作 Rule of Five,用賦值/建構 | memcpy |
| 序列化/網路傳輸 | 逐欄位序列化 / Protobuf / FlatBuffers | 直接 memcpy 物件 |
| 效能關鍵路徑 | 確認 is_trivially_copyable 後用 memcpy | 未驗證就用 |
std::is_trivially_copyable 編譯期檢查,請善用靜態斷言防呆。a = b;,不可冒險用 memcpy(&a, &b, sizeof(a))。