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
の構築時に指定されたカスタムデリーターを使用して破棄されます。
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>
を使用することでデータ競合を防ぐことができます。
目次 |
メンバー型
| メンバー型 | 定義 | ||||
element_type
|
|
||||
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) |
|
(C++26)
|
共有ポインタの所有者ベースの等価比較を提供する
(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
と比較する
(関数テンプレート) |
|
格納されたポインタの値を出力ストリームに出力する
(関数テンプレート) |
|
|
(C++11)
|
std::swap
アルゴリズムを特殊化する
(関数テンプレート) |
std::shared_ptr
に対するアトミック操作を特殊化
(関数テンプレート) |
ヘルパークラス
|
(C++20)
|
アトミック共有ポインタ
(クラステンプレートの特殊化) |
|
(C++11)
|
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
によって管理されるオブジェクトへの弱参照
(クラステンプレート) |