コンテンツにスキップ

スマートポインタ

リソースの所有権

ポインタはコピー可能なため、ポインタが指す先のリソースを複数のオブジェクトから参照することが可能です。

int main() {
    int* a = new int(100);
    int* b = a; // b からも a と同じリソースを参照できるようにする。

    // a と b のどちらを delete するべきか?
    return 0;
}

動的確保したリソースを扱う場合、 誤って delete を忘れたり、同じリソースを複数回 delete したりすることを防ぐために、 どの変数がリソースの所有権(リソースを参照する権利と解放する権利)を持つのかをプログラマが細心の注意を払ってコードを書く必要があります。

このようなポインタを扱う上での危険性や負担を下げるために、 C++ ではスマートポインタという仕組みが存在します。

スマートポインタは <memory> ヘッダにて提供されています。

std::shared_ptr

std::shared_ptr は動的確保したリソースの所有権を共有することができるスマートポインタです。 内部で所有権を持つオブジェクトの一覧を管理し、所有者がいなくなった時に自動的に delete する仕組みを有しています。

std::shared_ptr オブジェクトを生成するには、 std::make_shared を利用します。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> x = std::make_shared<int>(100); // int* x = new int(100); の代わり
                                                         // 所有者は1人。
    {
        std::shared_ptr<int> y = x; // 通常のポインタ同様、コピーすることで所有権が共有される
                                    // 所有者が2人に増える。
        std::cout << *y << std::endl;
    } // y が破棄されて所有者が1人になる。

    std::cout << *x << std::endl;

    return 0;
} // 所有者が0人になるので、 x のデストラクタで自動的に delete が行われる。

std::unique_ptr

std::unique_ptr は、 std::shared_ptr と違い、コピーが出来ません。 そのため、確保したリソースの所有者が常に1人になります。

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> x(new int(100));
    // std::unique_ptr<int> y = x; // コピー出来ない。コンパイルエラー。

    std::cout << *x << std::endl;

    return 0;
} // x が所有しているリソースが解放される。

所有権の共有はできませんが、std::move を使うことで所有権の移動は出来ます。このことで std::shared_ptr よりも軽快に動作します。

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> x(new int(100));
    std::unique_ptr<int> y(std::move(x)); // ムーブは出来るため、所有権の移動は可能。
                                          // 所有権を移動したため、x は何も所有していない。

    std::cout << *y << std::endl;

    return 0;
} // y が所有しているリソースが解放される。

std::auto_ptr

std::auto_ptr は C++11 では非推奨となっており C++17 では削除されているため使用しないでください。

詳細は std::auto_ptr - cppreference.com を参照してください。

std::weak_ptr

std::weak_ptr は普段使うことは有りませんが、 std::shared_ptr を使う時に循環参照するような場合で std::shared_ptr のリソースの所有権を持つことなく、 そのリソースを監視する(弱参照)ことが出来るスマートポインタとして std::weak_ptr があります。 lock() を使うことで、監視している std::shared_ptr のリソースが有効な場合、 監視先とリソースを共有する std::shared_ptr が取得できます。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> sp = std::make_shared<int>(246);
    std::weak_ptr<int> wp = sp; // sp を監視対象として wp に登録する

    std::shared_ptr<int> sp2 = wp.lock(); // 有効な場合は sp とリソースを共有する sp2 が作られる
    if (sp2) {
        std::cout << *sp2 << std::endl;
    } else {
        std::cout << "リソースは解放済み" << std::endl;
    }

    return 0;
}