オブジェクトの有効期間とリソースの管理 (RAII)

マネージド言語とは異なり、C++ には、プログラムの実行時にヒープ メモリやその他のリソースを解放する内部プロセスである自動 ガベージ コレクションはありません。 C++ プログラムでは、取得したすべてのリソースがオペレーティング システムに返されます。 未使用のリソースを解放できない状態は "リーク" と呼ばれます。 リークしたリソースは、プロセスが終了するまで他のプログラムでは使用できません。 特にメモリ リークは、C スタイルのプログラミングのバグの一般的な原因です。

最新の C++ では、スタックでオブジェクトを宣言することで、ヒープ メモリを可能な限り使用しないようにします。 リソースがスタックに対して大きすぎる場合は、オブジェクトがそのリソースを "所有" する必要があります。 オブジェクトが初期化されると、所有するリソースを取得します。 オブジェクトは、そのデストラクターでリソースを解放する必要があります。 所有オブジェクト自体はスタックで宣言されます。 "オブジェクトがリソースを所有する" 原則は、"リソース取得は初期化である" (RAII) とも呼ばれます。

リソースを所有するスタック オブジェクトがスコープ外に出ると、そのデストラクターが自動的に呼び出されます。 このように、C++ のガベージ コレクションはオブジェクトの有効期間と密接に関連しており、確定的な処理です。 リソースは常にプログラムの既知のポイントで解放されます。これは制御可能です。 メモリ リソースと非メモリ リソースを等しく処理できるのは、C++ のデストラクターのような確定的なデストラクターのみです。

次の例は、単純なオブジェクト w を示しています。 これはスタックの関数スコープで宣言され、関数ブロックの末尾で破棄されます。 オブジェクト w は "リソース" (ヒープ割り当てメモリなど) を所有しません。 その唯一のメンバーである g はスタックで宣言され、w と共に単にスコープ外に出ます。 widget デストラクターに特別なコードは必要はありません。

class widget {
private:
    gadget g;   // lifetime automatically tied to enclosing object
public:
    void draw();
};

void functionUsingWidget () {
    widget w;   // lifetime automatically tied to enclosing scope
                // constructs w, including the w.g gadget member
    // ...
    w.draw();
    // ...
} // automatic destruction and deallocation for w and w.g
  // automatic exception safety,
  // as if "finally { w.dispose(); w.g.dispose(); }"

次の例では、w はメモリ リソースを所有します。そのため、メモリを削除するには、デストラクターにコードが必要です。

class widget
{
private:
    int* data;
public:
    widget(const int size) { data = new int[size]; } // acquire
    ~widget() { delete[] data; } // release
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data member
    w.do_something();

} // automatic destruction and deallocation for w and w.data

C++11 以降では、前の例を記述するためのより適切な方法として、標準ライブラリのスマート ポインターを使用します。 スマート ポインターは、所有しているメモリの割り当てと削除を処理します。 スマート ポインターを使用すると、widget クラスに明示的なデストラクターが不要になります。

#include <memory>
class widget
{
private:
    std::unique_ptr<int[]> data;
public:
    widget(const int size) { data = std::make_unique<int[]>(size); }
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data gadget member
    // ...
    w.do_something();
    // ...
} // automatic destruction and deallocation for w and w.data

メモリ割り当てにスマート ポインターを使用すると、メモリ リークの可能性を排除できます。 このモデルは、ファイル ハンドルやソケットなどの他のリソースに対しても有効です。 クラスでも同様の方法で独自のリソースを管理できます。 詳細については、「スマート ポインター」を参照してください。

C++ の設計により、オブジェクトはスコープ外に出ると破棄されます。 つまり、オブジェクトはブロックが終了すると、構築時とは逆の順序で破棄されます。 オブジェクトが破棄されると、そのベースとメンバーは特定の順序で破棄されます。 グローバル スコープにおいてブロックの外部で宣言されたオブジェクトが問題を引き起こす可能性があります。 グローバル オブジェクトのコンストラクターが例外をスローした場合は、デバッグが困難になる可能性があります。

関連項目

C++ へようこそ
C++ 言語リファレンス
C++ 標準ライブラリ