RAII
Resource Acquisition Is Initialization、略してRAIIは、C++プログラミングにおけるテクニックです[1][2]。これは、使用前に取得しなければならないリソース(確保されたヒープメモリ、実行スレッド、開かれたソケット、開かれたファイル、ロックされたミューテックス、ディスク領域、データベース接続など、供給が限られているあらゆるもの)のライフサイクルを、オブジェクトのライフサイクルに結びつけます。
RAIIは、オブジェクトにアクセスする可能性のあるどの関数に対してもリソースが利用可能であることを保証します(リソースの利用可能性はクラス不変条件であり、冗長な実行時テストを不要にします)。また、制御するオブジェクトのライフサイクルが終了したときには、すべてのリソースが取得とは逆の順序で解放されることも保証します。同様に、リソースの取得が失敗した場合(コンストラクタが例外で終了した場合)、完全に構築されたすべてのメンバーおよび基底サブオブジェクトによって取得されたすべてのリソースは、初期化とは逆の順序で解放されます。これにより、コア言語機能(オブジェクトのライフサイクル、スコープからの脱出、初期化の順序、スタックアンワインディング)が活用され、リソースリークが排除され、例外安全性が保証されます。このテクニックは、RAIIオブジェクトのライフサイクルがスコープからの脱出によって終了するという基本的なユースケースから、Scope-Bound Resource Management(SBRM)とも呼ばれます。
RAIIは以下のように要約できます。
- 各リソースをクラスにカプセル化し、そこで
- コンストラクタがリソースを取得し、すべてのクラス不変条件を確立するか、それができない場合は例外をスローします。
- デストラクタがリソースを解放し、決して例外をスローしません。
- 常にRAIIクラスのインスタンスを介してリソースを使用します。そのインスタンスは、
- 自動ストレージ期間または一時的なライフサイクル自体を持つか、または
- 自動または一時オブジェクトのライフサイクルによって制限されるライフサイクルを持ちます。
|
ムーブセマンティクスにより、リソースの安全性(resource safety)を確保しながら、コンテナ内外およびスレッド間でリソースと所有権を転送することができます。 |
(C++11以降) |
open()/close()、lock()/unlock()、またはinit()/copyFrom()/destroy()といったメンバー関数を持つクラスは、RAIIではない典型的な例です。
std::mutex m; void bad() { m.lock(); // acquire the mutex f(); // if f() throws an exception, the mutex is never released if (!everything_ok()) return; // early return, the mutex is never released m.unlock(); // if bad() reaches this statement, the mutex is released } void good() { std::lock_guard<std::mutex> lk(m); // RAII class: mutex acquisition is initialization f(); // if f() throws an exception, the mutex is released if (!everything_ok()) return; // early return, the mutex is released } // if good() returns normally, the mutex is released
[編集] 標準ライブラリ
C++ライブラリクラスのうち、自身のリソースを管理するものはRAIIに従っています。std::string、std::vector、std::jthread(C++20以降)など多くのクラスは、コンストラクタでリソースを取得し(エラー時には例外をスロー)、デストラクタでリソースを解放し(決して例外をスローしない)、明示的なクリーンアップを必要としません。
|
さらに、標準ライブラリは、ユーザーが提供するリソースを管理するためのいくつかのRAIIラッパーを提供しています。
|
(C++11以降) |
[編集] 注記
RAIIは、使用前に取得されないリソース(CPU時間、コアの利用可能性、キャッシュ容量、エントロピープール容量、ネットワーク帯域幅、電力消費、スタックメモリなど)の管理には適用されません。そのようなリソースの場合、C++クラスのコンストラクタはオブジェクトのライフサイクル中にリソースの利用可能性を保証できないため、他のリソース管理手段を使用する必要があります。