C++でunique_ptrとshared_ptrを使い分ける目安
C++でスマートポインタを使うとき、unique_ptr と shared_ptr のどちらにするか迷うことがあります。
最初に見るのは所有権です。
- 1箇所だけが所有する ->
std::unique_ptr - 複数箇所で所有を共有する ->
std::shared_ptr
迷ったら unique_ptr から考える方が整理しやすいです。
unique_ptrを使う場面
std::unique_ptr は、所有者が1つだけのポインタです。
#include <memory>
struct User {
int id;
};
std::unique_ptr<User> makeUser() {
return std::make_unique<User>(User{1});
}
戻り値として返す場合も、所有権が移動するだけなので扱いやすいです。
auto user = makeUser();
コピーはできません。
auto a = std::make_unique<User>();
// auto b = a; // error
所有者が1つだけなので、いつ破棄されるかも追いやすくなります。
shared_ptrを使う場面
std::shared_ptr は、参照カウントで所有を共有します。
#include <memory>
#include <vector>
auto user = std::make_shared<User>(User{1});
std::vector<std::shared_ptr<User>> users;
users.push_back(user);
複数の場所が同じオブジェクトを保持し、最後の shared_ptr が消えたときに破棄されます。
便利ですが、ライフタイムが見えづらくなりやすいです。
shared_ptrを避けたい場面
「とりあえずshared_ptr」にすると、誰が所有しているのか分からなくなります。
たとえば、単に関数内で使うだけなら参照で十分です。
void printUser(const User& user) {
// 読むだけ
}
所有しないなら、スマートポインタを渡す必要はありません。
循環参照に注意する
shared_ptr 同士が互いに持ち合うと、参照カウントが0にならず破棄されません。
struct Node {
std::shared_ptr<Node> next;
std::shared_ptr<Node> prev;
};
こういう場合は、片方を std::weak_ptr にします。
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev;
};
まとめ
スマートポインタは所有権で選ぶと整理しやすいです。
- 基本は
std::unique_ptr - 複数箇所で所有する必要があるときだけ
std::shared_ptr - 所有しない引数は参照やポインタで渡す
shared_ptrの循環参照にはweak_ptr
まず unique_ptr で表現できないかを見る。そこから外れる場合に shared_ptr を使う、くらいが扱いやすいです。


