Copy, Move 與 RAII
C++ 用 destructor 自動釋放資源(RAII),但 class 持有 raw pointer 時,只寫 constructor / destructor 不夠。
Rule of Five
| 函數 | 簽名 |
|---|---|
| copy constructor | C(const C&) |
| copy assignment | C& operator=(const C&) |
| move constructor | C(C&&) |
| move assignment | C& operator=(C&&) |
| destructor | ~C() |
編譯器自動生成的 copy 只複製指標 → 兩個物件指向同一塊記憶體 → double free。
Copy:copy-and-swap
cpp
IntBuff& operator=(const IntBuff& ib) {
IntBuff tmp(ib); // 可能 throw 的部分先做
std::swap(arr, tmp.arr); // 不會 throw 才改自己
std::swap(sz, tmp.sz);
return *this;
}順便處理了 self-assignment 與 exception safety。
Move
- r-value:只能放在等號右邊的值,例如
foo()、b + 1、99 T&&綁定 r-value;std::move(x)把 l-value 轉成 r-value reference- 實作 move:接手指標,並把來源設成「空」,避免重複釋放
cpp
IntBuff(IntBuff&& ib) noexcept : arr(ib.arr), sz(ib.sz) {
ib.arr = nullptr;
ib.sz = 0;
}std::unique_ptr
一個 class 管理一個資源。成員改成 std::unique_ptr<int[]> 後:
- 不用手寫
delete - move 直接
= default - copy 被禁止,需要時自己實作(new 一份再複製內容)
cpp
class IntBuff {
public:
explicit IntBuff(size_t sz) : arr(std::make_unique<int[]>(sz)), sz(sz) {}
IntBuff(const IntBuff& ib) : IntBuff(ib.sz) {
std::copy(ib.arr.get(), ib.arr.get() + sz, arr.get());
}
IntBuff& operator=(const IntBuff& ib) {
IntBuff tmp(ib);
std::swap(*this, tmp);
return *this;
}
IntBuff(IntBuff&&) noexcept = default;
IntBuff& operator=(IntBuff&&) noexcept = default;
private:
std::unique_ptr<int[]> arr;
size_t sz = 0;
};