Way to C++: 3. std::unique_ptr
前言
在一個 class 裡面,有一個 raw pointer 還好,假如有兩個 raw pointer (都是要 new 的那種)呢?
class A {
public:
A() {
// ...
b = new int;
c = new int;
// ...
}
// ... other function
int *b, *c;
};
這樣寫就會造成一個重要的問題,c++ 在 new 時,其實有可能會因為系統空間不夠, new 不出來,丟出 Exception 。Exception 會中斷當前的 code ,直到有外層的 try-catch 抓起來。
假如在 b new 完,在 c new 時出現 Exception,那 b 就會 memory leak。
一種方法是在每個 new 時都用 try-catch 包起來,好好的處理前面的資源:
b = new int;
try {
c = new int;
} catch (...) {
delete b;
throw;
}
但這種方法完全很冗,有更多 resource 會讓 code 裡面有很多重複的地方。
所以我們會需要 1 個 class 管理 1 個指標(或資源),讓他在丟 Exception 跳離當前 block 時觸發解構子回收資源。
std::unique_ptr
std::unique_ptr
就可以幫我們做的這件事情,這 class 管理的 raw pointer 會在 destructor 觸發時刪除。
{
std::unique_ptr<int> a(new int(5));
} // 回收!
同樣的問題,裡面管理的指標,要怎麼「複製」? unique_ptr 是不管這塊的,應該說,複製 unique_ptr instance 被禁止了,只留下搬移,也就是說,只允許只有一個 unique_ptr instance 管理這個指標。
所以回來看一下 IntBuff 可以怎麼改寫,雖然 unique_ptr 只允許搬移,但是 IntBuff 的複製仍然可以實作。
#include <algorithm>
#include <cstdio>
#include <memory>
class IntBuff {
public:
IntBuff() = default;
explicit IntBuff(size_t sz) : sz(sz) {
if (sz) {
arr = std::unique_ptr<int[]>(new int[sz]);
}
};
IntBuff(const IntBuff& ib) {
sz = ib.sz;
arr = std::unique_ptr<int[]>(new int[sz]);
for (int lx = 0; lx < sz; lx++) {
arr[lx] = ib.arr[lx];
}
}
IntBuff& operator=(const IntBuff& ib) {
IntBuff tmp(ib); // copy-and-swap
std::swap(tmp.arr, arr);
std::swap(tmp.sz, sz);
return *this;
}
IntBuff(IntBuff&& ib) {
arr = std::move(ib.arr);
sz = ib.sz;
ib.sz = 0;
}
IntBuff& operator=(IntBuff&& ib) {
arr = std::move(ib.arr);
sz = ib.sz;
ib.sz = 0;
return *this;
}
private:
std::unique_ptr<int[]> arr = nullptr;
size_t sz = 0;
};
int main() {
{
IntBuff a1(size_t(10)), b1;
b1 = std::move(a1);
}
return 0;
}
基本上, delete 都可以省去,交由 std::unique_ptr
本身來管理。在複製時,就是直接開一個新的 unique_ptr ,和原本的 code 邏輯基本上是一樣的。
基本上, IntBuff 到這裡就算是完善了。