スマート ポインター (Modern C++)

最新の C++ プログラミングでは、標準ライブラリにスマート ポインターが含まれています。これは、プログラムからメモリ リークとリソース リークをなくし、プログラムを例外セーフにするのに役立ちます。

スマート ポインターの使用

スマート ポインターは、<memory> ヘッダー ファイルの std 名前空間で定義されます。 これらは、RAII (Resource Acquisition Is Initialialization) プログラミングの表現方法にとって重要です。 この表現方法の主な目的は、オブジェクトのすべてのリソースが 1 行のコードで作成されて準備が完了するように、リソースの取得がオブジェクトの初期化と同時に行われるようにすることです。 具体的には、RAII の主要な原則は、ヒープ割り当てリソース (動的に割り当てられたメモリやシステム オブジェクト ハンドルなど) の所有権を、リソースを削除または解放するコードと関連するクリーンアップ コードがデストラクターに含まれるスタック割り当てオブジェクトに付与するというものです。

ほとんどの場合、生のポインターまたはリソース ハンドルを初期化して実際のリソースをポイントするとき、ポインターをすぐにスマート ポインターに渡します。 最新の C++ では、生のポインターは、パフォーマンスが重要な意味を持ち、所有権に関する混乱が発生する可能性がない限られたスコープ、ループ、またはヘルパー関数の小さなコード ブロックでのみ使用されます。

次の例では、生のポインターの宣言とスマート ポインターの宣言を比較します。

void UseRawPointer()
{
    // Using a raw pointer -- not recommended.
    Song* pSong = new Song(L"Nothing on You", L"Bruno Mars"); 

    // Use pSong...

    // Don't forget to delete!
    delete pSong;   
}

void UseSmartPointer()
{
    // Declare a smart pointer on stack and pass it the raw pointer.
    unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));

    // Use song2...
    wstring s = song2->duration_;
    //...

} // song2 is deleted automatically here.

この例に示されているように、スマート ポインターは、スタック上で宣言し、ヒープ割り当てオブジェクトをポイントする生のポインターを使用して初期化するクラス テンプレートです。 スマート ポインターは、初期化後、生のポインターを所有します。 つまり、スマート ポインターは、生のポインターが指定するメモリの削除を担当します。 スマート ポインターのデストラクターには、削除する呼び出しが含まれています。スマート ポインターはスタック上で宣言されるため、スタックの別の場所で例外がスローされた場合でも、スマート ポインターがスコープ外に移動するとデストラクターが呼び出されます。

カプセル化されたポインターには、使い慣れたポインター演算子 -> および * を使用することでアクセスします。これらの演算子には、カプセル化された生のポインターを返すためにスマート ポインター クラスがオーバーロードします。

C++ のスマート ポインターの表現方法は、C# などの言語でのオブジェクト作成に似ています。オブジェクトを作成し、システムが正しい時刻にファイルの削除を実行するようにします。 違いは、別個のガベージ コレクターがバックグラウンドで実行されないことです。メモリは、標準 C++ のスコープ規則を通じて管理されるため、ランタイム環境が高速かつ効率的になります。

重要

特定のパラメーター リストの割り当て規則が原因でわずかなリソース リークが発生しないように、スマート ポインターは必ずパラメーター リストではなく別個のコード行に作成してください。

C++ 標準ライブラリの unique_ptr スマート ポインターの型を使用して、大きいオブジェクトへのポインターをカプセル化する方法を次の例に示します。


class LargeObject
{
public:
    void DoSomething(){}
};

void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{    
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Pass a reference to a method.
    ProcessLargeObject(*pLarge);

} //pLarge is deleted automatically when function block goes out of scope.

この例は、スマート ポインターを使用するために必要な次の手順を示しています。

  1. 自動 (ローカル) 変数としてスマート ポインターを宣言します (スマート ポインター自体で new または malloc 式を使用しないでください)。

  2. 型パラメーターで、カプセル化されたポインターの pointed-to 型を指定します。

  3. 生のポインターを、スマート ポインター コンストラクター内の new オブジェクトに渡します。 (これは、一部のユーティリティ関数またはスマート ポインター コンストラクターによって行われます)。

  4. オーバーロードされた -> および * 演算子を使用してオブジェクトにアクセスします。

  5. スマート ポインターがオブジェクトを削除するようにします。

スマート ポインターは、メモリとパフォーマンスの両方の点でできる限り効率が高くなるように設計されています。 たとえば、unique_ptr の唯一のデータ メンバーはカプセル化されたポインターです。 これは、unique_ptr がそのポインターとまったく同じサイズ (4 バイトまたは 8 バイト) であることを意味します。 スマート ポインターによってオーバーロードされた * および -> 演算子を使用することでカプセル化されたポインターにアクセスしても、生のポインターに直接アクセスするよりそれほど遅くなるわけではありません。

スマート ポインターには、"ドット" 表記を使用してアクセスできる独自のメンバー関数があります。 たとえば、一部の C++ 標準ライブラリ スマート ポインターにはポインターの所有権を解放するリセット メンバー関数があります。 これは、次の例に示すように、スマート ポインターがスコープ外に移動する前に、スマート ポインターが所有するメモリを解放する場合に便利です。

void SmartPointerDemo2()
{
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Free the memory before we exit function block.
    pLarge.reset();

    // Do some other work...

}

スマート ポインターには、通常、生のポインターに直接アクセスする方法が用意されています。 C++ 標準ライブラリ スマート ポインターには、この目的で get メンバー関数が用意されており、CComPtr にはパブリック p クラス メンバーがあります。 基になるポインターへの直接アクセスを用意することにより、スマート ポインターを使用して独自のコード内のメモリを管理し、スマート ポインターをサポートしないコードに生のポインターを渡すこともできます。

void SmartPointerDemo4()
{
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Pass raw pointer to a legacy API
    LegacyLargeObjectFunction(pLarge.get());    
}

スマート ポインターの種類

次のセクションでは、Windows プログラミング環境で使用できるさまざまな種類のスマート ポインターの概要を示し、使用するタイミングについて説明します。

C++ 標準ライブラリのスマート ポインター

これらのスマート ポインターは、Plain Old C++ Object (POCO) にポインターをカプセル化する最初のオプションとして使用します。

  • unique_ptr
    基になるポインターの所有者は、厳密に 1 人許可されます。 shared_ptr が必要であることがわかっている場合を除き、POCO の既定のオプションとして使用します。 新しい所有者に移動できますが、コピーおよび共有することはできません。 非推奨とされた auto_ptr を置き換えます。 boost::scoped_ptr に相当します。 unique_ptr は、サイズが小さく効率的です。サイズはポインター 1 個であり、C++ 標準ライブラリ コレクションからの高速な挿入および取得を実現するために右辺値参照がサポートされます。 ヘッダー ファイルは <memory> です。 詳細については、「方法: unique_ptr インスタンス と、unique_ptr クラス の作成と使用」を参照してください。

  • shared_ptr
    参照カウント スマート ポインターです。 複数の所有者に 1 個の生のポインターなどを割り当てる場合に使用します。たとえば、コンテナーからポインターのコピーを返し、元のポインターを維持する場合などです。 生のポインターは、shared_ptr のすべての所有者がスコープ外になるか、所有権を放棄するまで削除されません。 サイズはポインター 2 個です。1 個はオブジェクト用で、もう 1 個は参照カウントを含む共有コントロール ブロック用です。 ヘッダー ファイルは <memory> です。 詳細については、「方法: shared_ptr インスタンス と、shared_ptr クラス の作成と使用」を参照してください。

  • weak_ptr
    shared_ptr と同時に使用する特殊ケースのスマート ポインターです。 weak_ptr は、1 つ以上の shared_ptr インスタンスが所有するオブジェクトへのアクセスを提供しますが、参照カウントには参加しません。 オブジェクトを観察するが、オブジェクトを維持しておく必要はない場合に使用します。 shared_ptr インスタンス間の循環参照を解除するいくつかのケースで必要です。 ヘッダー ファイルは <memory> です。 詳細については、「方法: weak_ptr インスタンス と、weak_ptr クラス の作成と使用」を参照してください。

COM オブジェクト用のスマート ポインター (従来の Windows プログラミング)

COM オブジェクトを使用する場合、スマート ポインターの適切な型でインターフェイス ポインターをラップします。 Active Template Library (ATL) は、さまざまな目的で複数のスマート ポインターを定義します。 さらに、コンパイラが .tlb ファイルからラッパー クラスを作成するときに使用する _com_ptr_t スマート ポインターの型を使用することもできます。 これは、ATL ヘッダー ファイルをインクルードしたくない場合に最も適しています。

CComPtr クラス
ATL を使用できない場合以外は、これを使用してください。 AddRef メソッドと Release メソッドを使用して、参照カウントを実行します。 詳細については、「方法: CComPtr と CComQIPtr インスタンスの作成と使用」を参照してください。

CComQIPtr クラス
CComPtr に似ていますが、COM オブジェクトで QueryInterface を呼び出すための簡単な構文も提供します。 詳細については、「方法: CComPtr と CComQIPtr インスタンスの作成と使用」を参照してください。

CComHeapPtr クラス
CoTaskMemFree を使用してメモリを解放するオブジェクトへのスマート ポインター。

CComGITPtr クラス
グローバル インターフェイス テーブル (GIT) から取得されたインターフェイスのスマート ポインターです。

_com_ptr_t クラス
機能の点では CComQIPtr に似ていますが、ATL ヘッダーには依存しません。

POCO オブジェクト用の ATL スマート ポインター

COM オブジェクト用のスマート ポインターに加えて、ATL は Plain Old C++ Object (POCO) 用のスマート ポインター、およびスマート ポインター コレクションも定義します。 従来の Windows プログラミングでは、特にコードに移植性が必要ない場合や、C++ 標準ライブラリと ATL のプログラミング モデルを混在させたくない場合に、これらの型は C++ 標準ライブラリ コレクションの代わりとして役立ちます。

CAutoPtr クラス
コピー時に所有権を移動することで一意の所有権を強制するスマート ポインターです。 非推奨とされた std::auto_ptr クラスに相当します。

CHeapPtr クラス
C malloc 関数を使用して割り当てられたオブジェクトのスマート ポインターです。

CAutoVectorPtr クラス
new[] を使用して割り当てられた配列のスマート ポインターです。

CAutoPtrArray クラス
CAutoPtr 要素の配列をカプセル化するクラスです。

CAutoPtrList クラス
CAutoPtr ノードの一覧を操作するメソッドをカプセル化するクラスです。

関連項目

ポインター
C++ 言語リファレンス
C++ 標準ライブラリ