C++ 基礎からマスターまで — 第9回
スマートポインタで所有権を表現する
本ブログ作成文は、講義を目指して製作されました。 また、作成された文章の情報も間違っている可能性がありますので、ご注意ください。
ポインタを使用しても問題が生じるわけではない。 問題は、誰がオブジェクトを破壊するのかわからない状態で、生のポインタを所有権のように使用するときに発生します。
スマートポインタは所有権を型で表現する。
unique_ptr
std::unique_ptrは独占所有権を有する。
#include <memory>
auto warrior = std::make_unique<Warrior>("Rose");
std::cout << warrior->Attack() << '\n';
直接 new を呼び出さずにstd::make_uniqueでオブジェクトを作成します。 unique_ptrが範囲外になると、所有するオブジェクトも自動的に破壊されます。
単独所有なのでコピーはできないが移動は可能だ。
std::vector<std::unique_ptr<Character>> party;
auto warrior = std::make_unique<Warrior>("Rose");
party.push_back(std::move(warrior));
std::move以後warriorは空であり、所有権はvectorに移動する。 std::move自体が物を移動する関数ではなく、移動できる値として扱うようにする役割をする。
shared_ptr
一つのモンスターをアクティブリストと戦闘システムが一緒に所有しなければならない場合もある。 この時std::shared_ptrを使うことができる。
auto monster = std::make_shared<Monster>(1, "Slime", 30);
std::vector<std::shared_ptr<Monster>> activeMonsters;
std::queue<std::shared_ptr<Monster>> spawnQueue;
spawnQueue.push(monster);
activeMonsters.push_back(monster);
shared_ptrがコピーされたときにオブジェクトがコピーされるのではなく、参照数が増加します。 最後の所有者が消えると、実際のオブジェクトが破壊されます。
簡単だから、すべてのポインタをshared_ptrにするのは良くありません。 誰が所有者であるかがぼやけて循環参照が発生することもあり、参照回数管理費用もある。
weak_ptr
現在のターゲットのようにオブジェクトを所有せずに観察だけをしたい場合std::weak_ptrを使用する。
std::weak_ptr<Monster> currentTarget;
currentTarget = monster;
if (auto target = currentTarget.lock())
{
std::cout << target->GetHealth() << '\n';
}
weakptrはオブジェクトの寿命を延ばさない。 対象が生きている場合lock()このsharedptrを返し、すでに破壊されている場合は空のshared_ptrを返します。
クリーンアップすると、所有者が1人の場合はuniqueptr、 複数のシステムが本当に一緒に所有する必要がある場合は、sharedptr、 所有せずに見るだけならweak_ptrです。
所有権がコードに現れたら、deleteをいつすべきか悩む時間も減る。