std::shared_ptr
| ヘッダ <memory> で定義 |
||
| template< class T > class shared_ptr; |
(C++11以降) | |
std::shared_ptr は、ポインターを介してオブジェクトの共有所有権を保持するスマートポインターです。複数の shared_ptr オブジェクトが同じオブジェクトを所有できます。オブジェクトは、以下のいずれかが発生した場合に破棄され、そのメモリは解放されます。
- オブジェクトを所有する最後の
shared_ptrが破棄されたとき。 - オブジェクトを所有する最後の
shared_ptrが operator= または reset() を介して別のポインターに割り当てられたとき。
オブジェクトは delete-expression またはコンストラクション時に shared_ptr に提供されたカスタムデリーターを使用して破棄されます。
shared_ptr は、別のオブジェクトへのポインターを格納しながら、オブジェクトの所有権を共有できます。この機能は、オブジェクトが属するオブジェクトを所有しながら、メンバオブジェクトを指すために使用できます。格納されているポインターは、get()、間接参照、および比較演算子によってアクセスされるものです。管理されているポインターは、使用カウントがゼロになったときにデリーターに渡されるものです。
shared_ptr はオブジェクトを所有しない場合もあり、その場合は「空」と呼ばれます(エイリアシングコンストラクターを使用して作成された場合、空の shared_ptr は非ヌルの格納ポインターを持つことがあります)。
shared_ptr のすべての特殊化は、CopyConstructible、CopyAssignable、および LessThanComparable の要件を満たし、bool に 文脈上変換可能 です。
すべてのメンバー関数(コピーコンストラクターとコピー代入を含む)は、異なる shared_ptr オブジェクトに対して、たとえこれらのオブジェクトが同じオブジェクトをコピーして所有権を共有している場合でも、追加の同期なしで複数のスレッドから呼び出すことができます。複数の実行スレッドが同期なしで同じ shared_ptr オブジェクトにアクセスし、それらのアクセスのいずれかが shared_ptr の非constメンバー関数を使用する場合、データ競合が発生します。std::atomic<shared_ptr> を使用してデータ競合を防ぐことができます。
目次 |
[編集] メンバー型
| メンバ型 | 定義 | ||||
element_type
|
| ||||
weak_type (C++17 以降) |
std::weak_ptr<T> |
[編集] メンバー関数
新しい shared_ptr を構築する(public メンバ関数) | |
参照している shared_ptr がこれ以上ない場合、所有するオブジェクトを破棄する(public メンバー関数) | |
shared_ptr を割り当てる(public メンバー関数) | |
変更 | |
| 管理対象オブジェクトを置き換える (public メンバー関数) | |
| 管理対象オブジェクトを交換する (public メンバー関数) | |
監視 | |
| 格納されたポインターを返す (public メンバー関数) | |
| 格納されたポインターを間接参照する (public メンバー関数) | |
| (C++17) |
格納された配列へのインデックス付きアクセスを提供する (public メンバー関数) |
同じ管理オブジェクトを参照している shared_ptr オブジェクトの数を返す(public メンバー関数) | |
| (C++20まで) |
管理オブジェクトが現在の shared_ptr オブジェクトのみによって管理されているかどうかをチェックする(public メンバー関数) |
| 格納されたポインターがヌルではないことをチェックする (public メンバー関数) | |
| 共有ポインタの所有者ベースの順序を提供します (public メンバー関数) | |
| (C++26) |
shared_ptrの所有者に基づくハッシュを提供する (public メンバー関数) |
| (C++26) |
shared_ptrの所有者に基づく等価比較を提供する (public メンバー関数) |
[編集] 非メンバー関数
| 新しいオブジェクトを管理する共有ポインタを作成します (関数テンプレート) | |
| アロケータを使用して割り当てられた新しいオブジェクトを管理する共有ポインタを作成する (関数テンプレート) | |
| 格納されたポインタにstatic_cast、dynamic_cast、const_cast、またはreinterpret_castを適用する (関数テンプレート) | |
| 所有している場合、指定された型のデリータを返す (関数テンプレート) | |
| (C++20で削除)(C++20で削除)(C++20で削除)(C++20で削除)(C++20で削除)(C++20) |
別の shared_ptr または nullptr と比較する(関数テンプレート) |
| 格納されたポインタの値を出力ストリームに出力する (関数テンプレート) | |
| (C++11) |
std::swap アルゴリズムを特殊化する (関数テンプレート) |
std::shared_ptr のアトミック操作を特殊化する(関数テンプレート) |
[編集] ヘルパークラス
| (C++20) |
アトミック共有ポインタ (クラステンプレート特殊化) |
| (C++11) |
std::shared_ptr のハッシュサポート (クラステンプレートの特殊化) |
[編集] 推論ガイド (C++17 以降)
[編集] ノート
オブジェクトの所有権は、その値を別の 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 は不完全な型への生ポインターから構築できることに注意してください)。
std::shared_ptr<T> の 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 カウンターもゼロに達するまで自身を解放しません。
既存の実装では、同じコントロールブロックへの共有ポインターが存在する場合、弱ポインターの数はインクリメントされます ([1], [2])。
スレッドセーフティ要件を満たすために、参照カウンターは通常、std::memory_order_relaxed を伴う std::atomic::fetch_add と同等のものを使用してインクリメントされます(デクリメントには、コントロールブロックを安全に破棄するためにより強力な順序付けが必要です)。
[編集] 例
#include <chrono> #include <iostream> #include <memory> #include <mutex> #include <thread> using namespace std::chrono_literals; struct Base { Base() { std::cout << "Base::Base()\n"; } // Note: non-virtual destructor is OK here ~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; // thread-safe, even though the // shared use_count is incremented { static std::mutex io_mutex; std::lock_guard<std::mutex> lk(io_mutex); print("Local pointer in a thread:", lp); } } int main() { std::shared_ptr<Base> p = std::make_shared<Derived>(); print("Created a shared Derived (as a pointer to Base)", p); std::thread t1{thr, p}, t2{thr, p}, t3{thr, p}; p.reset(); // release ownership from main print("Shared ownership between 3 threads and released ownership from main:", p); t1.join(); t2.join(); t3.join(); std::cout << "All threads completed, the last one deleted 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.
[編集] 例2
#include <iostream> #include <memory> struct MyObj { MyObj() { std::cout << "MyObj constructed\n"; } ~MyObj() { std::cout << "MyObj destructed\n"; } }; struct Container : std::enable_shared_from_this<Container> // note: public inheritance { std::shared_ptr<MyObj> memberObj; void CreateMember() { memberObj = std::make_shared<MyObj>(); } std::shared_ptr<MyObj> GetAsMyObj() { // Use an alias shared ptr for member 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("Creating shared container"); std::shared_ptr<Container> cont = std::make_shared<Container>(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Creating member"); cont->CreateMember(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Creating another shared container"); 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("Copying alias 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("Resetting cont2"); cont2.reset(); DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Resetting myobj2"); myobj2.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Resetting cont"); cont.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); }
出力
Creating shared container cont.use_count() = 1 cont->memberObj.use_count() = 0 Creating member MyObj constructed cont.use_count() = 1 cont->memberObj.use_count() = 1 Creating another shared container 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 Copying alias obj 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 Resetting cont2 myobj1.use_count() = 3 myobj2.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 Resetting myobj2 myobj1.use_count() = 2 cont.use_count() = 2 cont->memberObj.use_count() = 1 Resetting cont myobj1.use_count() = 1 cont.use_count() = 0 MyObj destructed
[編集] 関連項目
| (C++11) |
オブジェクトの所有権を唯一のものとするセマンティクスを持つスマートポインタ (クラステンプレート) |
| (C++11) |
std::shared_ptr によって管理されるオブジェクトへの弱い参照 (クラステンプレート) |