unique_ptrについて
C++ではポインタ管理用にunique_ptrが追加されており、これの使い方と解決できる問題をちょっとまとめ。
生ポインタの問題
生ポインタ(rawポインタ)をそのまま使うということはC言語からずっと行われており、C、C++に慣れた方なら言語もうみんな違和感がなくなっている。
しかし、rawポインタの操作には実はどうしても不明瞭な部分があり、それをプログラマーが解決している。
例えば、
Widget* createWidget(int key)
という関数があったとして、これを利用する部分が、
int function() { Widget* p = createWidget(key); if (p != nullptr) bool ret = p->show(); if (ret) { delete p; return -1; } delete p; return 1; }
となっている。
rawポインタだと実は上記のコードを見るだけでは安全とは断言できなくて、
実は次の点が安全か疑問が残るんです。
unique_ptr
unique_ptrを使用するとcreateWidgetを使用する側は受け取ったポインタの消し方を考える必要がなくなる。
std::unique_ptr<Widget> createWidget() { std::unique_ptr<Widget> p; p.reset(new Widget()); return p; } int main() { auto p = createWidget(); if (p) { return -1; } bool ret = p->show(); if (ret) { return -1; } return 1; }
unique_ptrはただ一人がrawポインタを所有するという特性を持つため、createWidgetからポインタの所有権を移譲されたため、
- delete不要。例:戻り値がstaticな領域のポインタ、クラスメンバのポインタ
→この場合はそもそも戻り値の型を参照型にすればいいと思う
- delete[]。例:配列のポインタを返す
→解放の仕方はunique_ptrで配列だと勝手にdelete []を呼んでくれる
- 解放用の関数がある。例:free関数がある
→deleterを定義できる
- 全パスで一度だけ解放する。(リーク対策、2重開放対策)
→RAIIになっているので必ずスコープを外れると解放される。ポインタの受け渡しはmoveのため2重開放を起こさない
deleterを定義できる
auto createWidget() { auto deleter = [](Widget* p) { // Widget::free(p); }; std::unique_ptr<Widget, decltype(deleter)> p(nullptr, deleter); p.reset(new Widget()); return p; } int main() { auto p = createWidget(); if (p) { return -1; } bool ret = p->show(); if (ret) { return -1; } return 1; }