Namespaces
Variants

std:: shared_ptr

From cppreference.net
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
ヘッダーで定義 <memory>
template < class T > class shared_ptr ;
(C++11以降)

std::shared_ptr は、ポインタを通じてオブジェクトの共有所有権を保持するスマートポインタです。複数の shared_ptr オブジェクトが同じオブジェクトを所有することができます。以下のいずれかが発生した場合、オブジェクトは破棄され、そのメモリは解放されます:

  • オブジェクトを所有する最後の shared_ptr が破棄されたとき;
  • オブジェクトを所有する最後の shared_ptr operator= または reset() によって別のポインタに代入されたとき。

オブジェクトは、 delete-expression または、 shared_ptr の構築時に指定されたカスタムデリーターを使用して破棄されます。

A shared_ptr は、別のオブジェクトへのポインタを格納しながら、オブジェクトの所有権を共有できます。この機能は、メンバーオブジェクトが属するオブジェクトを所有しながら、そのメンバーオブジェクトを指し示すために使用できます。格納されたポインタは、 get() や逆参照演算子、比較演算子によってアクセスされるものです。管理対象ポインタは、使用カウントがゼロに達したときにデリーターに渡されるポインタです。

shared_ptr はオブジェクトを所有していない場合もあり、その場合は と呼ばれます(空の shared_ptr は、エイリアシングコンストラクタを使用して作成された場合、非ヌルの格納ポインタを持つことがあります)。

shared_ptr のすべての特殊化は、 CopyConstructible CopyAssignable 、および LessThanComparable の要件を満たし、 文脈上変換可能 であり、 bool に変換可能です。

すべてのメンバ関数(コピーコンストラクタとコピー代入演算子を含む)は、異なる shared_ptr オブジェクトに対して、それらのオブジェクトが同じオブジェクトの所有権を共有するコピーであったとしても、追加の同期なしに複数のスレッドから呼び出すことができます。複数の実行スレッドが同期なしに同じ shared_ptr オブジェクトにアクセスし、それらのアクセスのいずれかが shared_ptr の非constメンバ関数を使用する場合、データ競合が発生します。 std::atomic<shared_ptr> を使用することでデータ競合を防ぐことができます。

目次

翻訳の説明: - 「Contents」を「目次」に翻訳しました - C++専門用語(Member types、Member functions、Modifiers、Observers、Non-member functions、Helper classes、Deduction guides、Notes、Implementation notes、Example、See also)は原文のまま保持しました - HTMLタグ、属性、数値はすべて変更せず保持しました - フォーマットと構造は完全に維持しました

メンバー型

メンバー型 定義
element_type
T (C++17まで)
std:: remove_extent_t < T > (C++17以降)
weak_type (C++17以降) std:: weak_ptr < T >

メンバー関数

新しい shared_ptr を構築する
(public member function)
所有するオブジェクトを破棄する(他の shared_ptr がリンクしていない場合)
(public member function)
shared_ptr を代入する
(public member function)
修飾子
管理対象オブジェクトを置き換える
(public member function)
管理対象オブジェクトを交換する
(public member function)
オブザーバー
格納されたポインタを返す
(public member function)
格納されたポインタを間接参照する
(public member function)
(C++17)
格納された配列へのインデックスアクセスを提供する
(public member function)
同じ管理対象オブジェクトを参照する shared_ptr オブジェクトの数を返す
(public member function)
(until C++20)
管理対象オブジェクトが現在の shared_ptr オブジェクトのみによって管理されているかどうかをチェックする
(public member function)
格納されたポインタがnullでないかどうかをチェックする
(public member function)
共有ポインタの所有者ベースの順序付けを提供する
(public member function)
(C++26)
共有ポインタの所有者ベースのハッシュを提供する
(public member function)
共有ポインタの所有者ベースの等価比較を提供する
(public member function)

非メンバー関数

新しいオブジェクトを管理するshared_ptrを作成する
(関数テンプレート)
アロケータを使用して割り当てられた新しいオブジェクトを管理するshared_ptrを作成する
(関数テンプレート)
格納されたポインタに static_cast , dynamic_cast , const_cast , または reinterpret_cast を適用する
(関数テンプレート)
指定された型のデリーターを返す(所有している場合)
(関数テンプレート)
(C++20で削除) (C++20で削除) (C++20で削除) (C++20で削除) (C++20で削除) (C++20)
別の shared_ptr または nullptr と比較する
(関数テンプレート)
格納されたポインタの値を出力ストリームに出力する
(関数テンプレート)
std::swap アルゴリズムを特殊化する
(関数テンプレート)
std::shared_ptr に対するアトミック操作を特殊化
(関数テンプレート)

ヘルパークラス

アトミック共有ポインタ
(クラステンプレートの特殊化)
std::shared_ptr のハッシュサポート
(クラステンプレートの特殊化)

推論ガイド (C++17以降)

注記

オブジェクトの所有権は、他の shared_ptr オブジェクトを受け取るコンストラクタまたは代入関数を通じてのみ共有できます。新しい shared_ptr が、他の shared_ptr が保持する生の基盤ポインタのみを使用して構築された場合、この新しい shared_ptr は、自身が所有するオブジェクトを他の shared_ptr インスタンスが保持していないと見なします。これにより(後続の代入が行われない限り)、破棄時に同じオブジェクトに対してデリータが繰り返し適用される結果を招きます。

std::shared_ptr 不完全型 T と共に使用することができます。ただし、生ポインタからのコンストラクタ ( template < class Y > shared_ptr ( Y * ) ) および template < class Y > void reset ( Y * ) メンバ関数は、完全型へのポインタでのみ呼び出すことができます ( std::unique_ptr は不完全型への生ポインタから構築可能であることに注意してください)。

T は関数型でも構いません:この場合、オブジェクトポインタではなく関数へのポインタを管理します。これは、動的ライブラリやプラグインがその関数のいずれかが参照されている限りロードされた状態を維持するために使用されることがあります:

void del(void(*)()) {}
void fun() {}
int main()
{
    std::shared_ptr<void()> ee(fun, del);
    (*ee)();
}

実装ノート

一般的な実装では、 shared_ptr は2つのポインタのみを保持します:

  • 格納されたポインタ( get() によって返されるもの);
  • コントロールブロック へのポインタ。

コントロールブロックは、以下を保持する動的に割り当てられるオブジェクトです:

  • 管理対象オブジェクトへのポインタ、または管理対象オブジェクト自体;
  • デリータ(型消去済み);
  • アロケータ(型消去済み);
  • 管理対象オブジェクトを所有する shared_ptr の数;
  • 管理対象オブジェクトを参照する weak_ptr の数。

shared_ptr std::make_shared または std::allocate_shared を呼び出して作成される場合、制御ブロックと管理対象オブジェクトの両方のメモリは単一の割り当てで作成されます。管理対象オブジェクトは制御ブロックのデータメンバ内にその場で構築されます。 shared_ptr shared_ptr コンストラクタのいずれかを通じて作成される場合、管理対象オブジェクトと制御ブロックは別々に割り当てる必要があります。この場合、制御ブロックは管理対象オブジェクトへのポインタを格納します。

shared_ptr が直接保持するポインタは get() によって返されるものであり、一方、制御ブロックが保持するポインタ/オブジェクトは、共有所有者の数がゼロに達したときに削除されるものです。これらのポインタは必ずしも等しいとは限りません。

shared_ptr のデストラクタは、制御ブロックの共有所有者の数をデクリメントします。そのカウンタがゼロに達すると、制御ブロックは管理対象オブジェクトのデストラクタを呼び出します。制御ブロック自体は、 std::weak_ptr カウンタも同様にゼロに達するまで解放されません。

既存の実装では、同じ制御ブロックへのshared pointerが存在する場合、weak pointerの数が増加されます( [1] , [2] )。

スレッド安全性の要件を満たすため、参照カウンタは通常、 std::atomic::fetch_add std::memory_order_relaxed を使用した等価な操作でインクリメントされます(デクリメントには、制御ブロックを安全に破棄するためにより強い順序付けが必要です)。

#include <chrono>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
using namespace std::chrono_literals;
struct Base
{
    Base() { std::cout << "Base::Base()\n"; }
    // 注: 非仮想デストラクタでもここでは問題ありません
    ~Base() { std::cout << "Base::~Base()\n"; }
};
struct Derived : public Base
{
    Derived() { std::cout << "Derived::Derived()\n"; }
    ~Derived() { std::cout << "Derived::~Derived()\n"; }
};
void print(auto rem, std::shared_ptr<Base> const& sp)
{
    std::cout << rem << "\n\tget() = " << sp.get()
              << ", use_count() = " << sp.use_count() << '\n';
}
void thr(std::shared_ptr<Base> p)
{
    std::this_thread::sleep_for(987ms);
    std::shared_ptr<Base> lp = p; // スレッドセーフ、共有use_countがインクリメントされても
                                  //
    {
        static std::mutex io_mutex;
        std::lock_guard<std::mutex> lk(io_mutex);
        print("スレッド内のローカルポインタ:", lp);
    }
}
int main()
{
    std::shared_ptr<Base> p = std::make_shared<Derived>();
    print("共有DerivedをBaseへのポインタとして作成", p);
    std::thread t1{thr, p}, t2{thr, p}, t3{thr, p};
    p.reset(); // mainからの所有権を解放
    print("3つのスレッド間での共有所有権とmainからの所有権解放:", p);
    t1.join();
    t2.join();
    t3.join();
    std::cout << "全スレッドが完了し、最後のスレッドがDerivedを削除しました。\n";
}

出力例:

Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
	get() = 0x118ac30, use_count() = 1
Shared ownership between 3 threads and released ownership from main:
	get() = 0, use_count() = 0
Local pointer in a thread:
	get() = 0x118ac30, use_count() = 5
Local pointer in a thread:
	get() = 0x118ac30, use_count() = 4
Local pointer in a thread:
	get() = 0x118ac30, use_count() = 2
Derived::~Derived()
Base::~Base()
All threads completed, the last one deleted Derived.

#include <iostream>
#include <memory>
struct MyObj
{
    MyObj() { std::cout << "MyObj 構築されました\n"; }
    ~MyObj() { std::cout << "MyObj デストラクタ呼び出し\n"; }
};
struct Container : std::enable_shared_from_this<Container> // 注: public継承
{
    std::shared_ptr<MyObj> memberObj;
    void CreateMember() { memberObj = std::make_shared<MyObj>(); }
    std::shared_ptr<MyObj> GetAsMyObj()
    {
        // メンバーのためのエイリアス shared_ptr を使用
        return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get());
    }
};
#define COUT(str) std::cout << '\n' << str << '\n'
#define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n'
int main()
{
    COUT("共有コンテナの作成");
    std::shared_ptr<Container> cont = std::make_shared<Container>();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("メンバーの作成");
    cont->CreateMember();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("別の共有コンテナを作成");
    std::shared_ptr<Container> cont2 = cont;
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
    COUT("GetAsMyObj");
    std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
    COUT("エイリアス obj のコピー");
    std::shared_ptr<MyObj> myobj2 = myobj1;
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
    COUT("cont2をリセット中");
    cont2.reset();
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("myobj2 をリセット中");
    myobj2.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    COUT("コンティニューのリセット");
    cont.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
}

出力:

共有コンテナの作成
cont.use_count() = 1
cont->memberObj.use_count() = 0
メンバーの作成
MyObj 構築完了
cont.use_count() = 1
cont->memberObj.use_count() = 1
別の共有コンテナの作成
cont.use_count() = 2
cont->memberObj.use_count() = 1
cont2.use_count() = 2
cont2->memberObj.use_count() = 1
GetAsMyObj 実行
myobj1.use_count() = 3
cont.use_count() = 3
cont->memberObj.use_count() = 1
cont2.use_count() = 3
cont2->memberObj.use_count() = 1
エイリアスオブジェクトのコピー
myobj1.use_count() = 4
myobj2.use_count() = 4
cont.use_count() = 4
cont->memberObj.use_count() = 1
cont2.use_count() = 4
cont2->memberObj.use_count() = 1
cont2のリセット
myobj1.use_count() = 3
myobj2.use_count() = 3
cont.use_count() = 3
cont->memberObj.use_count() = 1
myobj2のリセット
myobj1.use_count() = 2
cont.use_count() = 2
cont->memberObj.use_count() = 1
contのリセット
myobj1.use_count() = 1
cont.use_count() = 0
MyObj 破棄完了

関連項目

(C++11)
ユニークなオブジェクト所有権セマンティクスを持つスマートポインタ
(クラステンプレート)
(C++11)
std::shared_ptr によって管理されるオブジェクトへの弱参照
(クラステンプレート)