方法: shared_ptr インスタンスを作成して使用する
shared_ptr 型は、C++ 標準ライブラリ内のスマート ポインターであり、複数の所有者がメモリ内のオブジェクトの有効期間を管理する必要が生じる可能性があるシナリオを想定して設計されたものです。 shared_ptr を初期化した後、そのポインターをコピーすること、関数の引数内の値として渡すこと、および他の shared_ptr インスタンスに割り当てることができます。 すべてのインスタンスは同じオブジェクトを指し、1 つの "コントロール ブロック" へのアクセスを共有します。このコントロール ブロックは、新しい shared_ptr が追加されるとき、スコープ外になるとき、またはリセットされるときに必ず参照カウントをインクリメントおよびデクリメントします。 参照カウントが 0 に達したときに、コントロール ブロックはメモリ リソースと自らを削除します。
次の図に、1 つのメモリ位置を指す shared_ptr の複数のインスタンスを示します。

設定例
この後の例はここで示された必要なヘッダーをすべて含み、必要な型をすべて宣言していることを前提としています。
// shared_ptr-examples.cpp
// The following examples assume these declarations:
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
struct MediaAsset
{
virtual ~MediaAsset() = default; // make it polymorphic
};
struct Song : public MediaAsset
{
std::wstring artist;
std::wstring title;
Song(const std::wstring& artist_, const std::wstring& title_) :
artist{ artist_ }, title{ title_ } {}
};
struct Photo : public MediaAsset
{
std::wstring date;
std::wstring location;
std::wstring subject;
Photo(
const std::wstring& date_,
const std::wstring& location_,
const std::wstring& subject_) :
date{ date_ }, location{ location_ }, subject{ subject_ } {}
};
using namespace std;
int main()
{
// The examples go here, in order:
// Example 1
// Example 2
// Example 3
// Example 4
// Example 6
}
例 1
最初にメモリ リソースを作成するときは、可能な限り、shared_ptr を作成するために make_shared 関数を使用してください。 make_shared は例外セーフです。 これは、コントロール ブロックとリソースにメモリを割り当てるために同じ呼び出しを使用し、その結果、構造のオーバーヘッドが削減されます。 make_shared を使用しない場合は、オブジェクトを shared_ptr コンストラクターに渡す前にオブジェクトを作成するために、明示的な new 式を使用する必要があります。 次の例では、新しいオブジェクトと共に shared_ptr を宣言して初期化するさまざまな方法を示します。
// Use make_shared function when possible.
auto sp1 = make_shared<Song>(L"The Beatles", L"Im Happy Just to Dance With You");
// Ok, but slightly less efficient.
// Note: Using new expression as constructor argument
// creates no named variable for other code to access.
shared_ptr<Song> sp2(new Song(L"Lady Gaga", L"Just Dance"));
// When initialization must be separate from declaration, e.g. class members,
// initialize with nullptr to make your programming intent explicit.
shared_ptr<Song> sp5(nullptr);
//Equivalent to: shared_ptr<Song> sp5;
//...
sp5 = make_shared<Song>(L"Elton John", L"I'm Still Standing");
例 2
次の例では、別の shared_ptrによって既に割り当てられているオブジェクトの共有所有権を取得する shared_ptr インスタンスを宣言して初期化する方法を示します。 sp2 が初期化された shared_ptrであることを想定します。
//Initialize with copy constructor. Increments ref count.
auto sp3(sp2);
//Initialize via assignment. Increments ref count.
auto sp4 = sp2;
//Initialize with nullptr. sp7 is empty.
shared_ptr<Song> sp7(nullptr);
// Initialize with another shared_ptr. sp1 and sp2
// swap pointers as well as ref counts.
sp1.swap(sp2);
例 3
shared_ptr は、要素をコピーするアルゴリズムを使用しているときに、C++ の標準ライブラリ コンテナー内でも役立ちます。 基になるメモリが、必要とされている間は有効であり、必要なくなった後は無効になることを理解している場合は、要素を shared_ptr 内でラップし、他のコンテナーにコピーすることができます。 次の例では、ベクター内の remove_copy_if インスタンスに対して shared_ptr アルゴリズムを使用する方法を示します。
vector<shared_ptr<Song>> v {
make_shared<Song>(L"Bob Dylan", L"The Times They Are A Changing"),
make_shared<Song>(L"Aretha Franklin", L"Bridge Over Troubled Water"),
make_shared<Song>(L"Thalía", L"Entre El Mar y Una Estrella")
};
vector<shared_ptr<Song>> v2;
remove_copy_if(v.begin(), v.end(), back_inserter(v2), [] (shared_ptr<Song> s)
{
return s->artist.compare(L"Bob Dylan") == 0;
});
for (const auto& s : v2)
{
wcout << s->artist << L":" << s->title << endl;
}
例 4
dynamic_pointer_cast, static_pointer_cast および const_pointer_cast を使用して、shared_ptr をキャストすることができます。 これらの関数は、dynamic_cast、static_cast、および const_cast の各演算子に似ています。 次の例では、基底クラスの shared_ptr のベクター内にある各要素の派生型をテストし、要素をコピーし、それらに関する情報を表示する方法を示します。
vector<shared_ptr<MediaAsset>> assets {
make_shared<Song>(L"Himesh Reshammiya", L"Tera Surroor"),
make_shared<Song>(L"Penaz Masani", L"Tu Dil De De"),
make_shared<Photo>(L"2011-04-06", L"Redmond, WA", L"Soccer field at Microsoft.")
};
vector<shared_ptr<MediaAsset>> photos;
copy_if(assets.begin(), assets.end(), back_inserter(photos), [] (shared_ptr<MediaAsset> p) -> bool
{
// Use dynamic_pointer_cast to test whether
// element is a shared_ptr<Photo>.
shared_ptr<Photo> temp = dynamic_pointer_cast<Photo>(p);
return temp.get() != nullptr;
});
for (const auto& p : photos)
{
// We know that the photos vector contains only
// shared_ptr<Photo> objects, so use static_cast.
wcout << "Photo location: " << (static_pointer_cast<Photo>(p))->location << endl;
}
例 5
次の方法で、shared_ptr を別の関数に渡すことができます。
値渡しで
shared_ptrを渡します。 これは、コピー コンストラクターを呼び出し、参照カウントをインクリメントし、呼び出し先を所有者にします。 この操作には小規模なオーバーヘッドがあり、渡そうとするshared_ptrオブジェクトの数によっては、非常に大規模になる可能性があります。 呼び出し元と呼び出し先の間にあるコード コントラクト (暗黙的または明示的) が、呼び出し先を所有者にすることを要求する場合は、このオプションを使用します。参照または定数参照により、
shared_ptrを渡します。 この場合、参照カウントはインクリメントされず、呼び出し元がスコープ内にとどまっている限り、呼び出し先はポインターにアクセスできます。 または、呼び出し先は参照に基づいてshared_ptrを作成し、その後、共有所有者になる方針を使用することもできます。 呼び出し元が呼び出し先を把握していない場合や、shared_ptrを渡す必要があり、パフォーマンス上の理由でコピー操作を回避したいと考える場合は、このオプションを使用します。基になるポインター、または基になるオブジェクトへの参照を渡します。 この結果、呼び出し先はオブジェクトを使用できますが、そのオブジェクトを有効にして所有権を共有することや、有効期間を延長することはできません。 呼び出し先が、生ポインターから
shared_ptrを作成する場合は、新しいshared_ptrは元のポインターから独立し、基になるリソースを制御することはできません。 呼び出し元と呼び出し先の間のコントラクトが、呼び出し元がshared_ptrの有効期間の所有権を保持することを明示的に指定する場合は、このオプションを使用します。shared_ptrを渡す方法を決定するときに、呼び出し先が、基になるリソースの所有権を共有する必要があるかどうかを判断してください。 "所有者" とは、基になるリソースを自らが必要とする限り、そのリソースを維持することができるオブジェクトまたは関数です。 呼び出し先が、(関数の) 有効期間を上回ってポインターの有効期間を延長できるようにすることを、呼び出し元が保証する必要がある場合は、最初のオプションを使用します。 呼び出し先が有効期間を延長するかどうかが問題にならない場合は、参照渡しを使用し、コピーするかどうかを呼び出し先に任せます。基になるポインターへのアクセスをヘルパー関数に付与する必要があり、ヘルパー関数がそのポインターを使用するだけであり、呼び出し元関数が制御を返す前にヘルパー関数が制御を返すことがわかっている場合は、関数は基になるポインターの所有権を共有する必要はありません。 ヘルパー関数は、呼び出し元の
shared_ptrの有効期間内にのみ、ポインターにアクセスする必要があります。 この場合は、shared_ptrを参照渡しにするか、生ポインターを渡すか、基になるオブジェクトへの参照を渡す方法が安全です。 この方法で渡すと、パフォーマンスに関するある程度の利点が生じ、プログラミングの意図を示すのに役立つ可能性もあります。時には、たとえば
std::vector<shared_ptr<T>>のように、各shared_ptrをラムダ式の本体または名前付き関数オブジェクトに渡す必要が生じることがあります。 ラムダまたは関数がそのポインターを格納しない場合は、shared_ptrの参照渡しを行い、各要素に対してコピー コンストラクターが呼び出されることを防止します。
例 6
shared_ptr インスタンスによって所有されているメモリ上のポインター比較を有効にするために、shared_ptr がさまざまな比較演算子をオーバーロードする方法を次の例に示します。
// Initialize two separate raw pointers.
// Note that they contain the same values.
auto song1 = new Song(L"Village People", L"YMCA");
auto song2 = new Song(L"Village People", L"YMCA");
// Create two unrelated shared_ptrs.
shared_ptr<Song> p1(song1);
shared_ptr<Song> p2(song2);
// Unrelated shared_ptrs are never equal.
wcout << "p1 < p2 = " << std::boolalpha << (p1 < p2) << endl;
wcout << "p1 == p2 = " << std::boolalpha <<(p1 == p2) << endl;
// Related shared_ptr instances are always equal.
shared_ptr<Song> p3(p2);
wcout << "p3 == p2 = " << std::boolalpha << (p3 == p2) << endl;