RAII
Resource Acquisition Is Initialization またはRAIIは、C++のプログラミング技法 [1] [2] であり、使用前に取得する必要があるリソース(割り当てられたヒープメモリ、実行スレッド、オープンソケット、オープンファイル、ロックされたミューテックス、ディスクスペース、データベース接続など、有限の供給量が存在するもの)のライフサイクルを、オブジェクトの lifetime に結びつけるものです。
RAIIは、オブジェクトにアクセスする可能性のある任意の関数に対してリソースが利用可能であることを保証します(リソースの可用性は クラス不変条件 であり、冗長な実行時テストを排除します)。また、制御対象オブジェクトの寿命が終了する際に、すべてのリソースが獲得順と逆順で解放されることも保証します。同様に、リソースの獲得が失敗した場合(コンストラクタが例外で終了する場合)、完全に構築されたすべてのメンバおよび基底サブオブジェクトによって獲得されたリソースは、初期化順と逆順で解放されます。これはコア言語機能( オブジェクト寿命 、 スコープ終了 、 初期化順序 および スタックアンワインディング )を活用して、リソースリークを排除し、例外安全性を保証します。この技法の別名は スコープ境界リソース管理 (SBRM)であり、RAIIオブジェクトの寿命がスコープ終了によって終了する基本的なユースケースに由来します。
RAIIは以下のように要約できます:
- 各リソースをクラスにカプセル化します。ここで
-
- コンストラクタはリソースを取得し、すべてのクラス不変条件を確立する。これができない場合は例外をスローする、
- デストラクタはリソースを解放し、決して例外をスローしない;
- 常にRAIIクラスのインスタンスを介してリソースを使用すること
-
- 自動記憶域期間または一時的な寿命自体を持つ、または
- 自動オブジェクトまたは一時オブジェクトの寿命によって制限される寿命を持つ。
|
ムーブセマンティクスは、コンテナ内外のオブジェクト間やスレッド間でリソースと所有権の転送を可能にし、リソース安全性を確保します。 |
(since C++11) |
open()
/
close()
、
lock()
/
unlock()
、
または
init()
/
copyFrom()
/
destroy()
メンバー関数を持つクラスは、非RAIIクラスの典型的な例です:
std::mutex m; void bad() { m.lock(); // mutexを取得 f(); // f()が例外をスローした場合、mutexは解放されない if (!everything_ok()) return; // 早期リターン、mutexは解放されない m.unlock(); // bad()がこの文に到達した場合、mutexは解放される } void good() { std::lock_guard<std::mutex> lk(m); // RAIIクラス: mutexの取得は初期化時に実行 f(); // f()が例外をスローした場合、mutexは解放される if (!everything_ok()) return; // 早期リターン、mutexは解放される } // good()が正常にリターンした場合、mutexは解放される
標準ライブラリ
C++ライブラリクラスで自身のリソースを管理するものはRAIIに従います: std::string 、 std::vector 、 std::jthread (C++20以降) など多くのクラスは、コンストラクタでリソースを取得し(エラー時には例外をスロー)、デストラクタでそれらを解放し(決して例外をスローしない)、明示的なクリーンアップを必要としません。
|
さらに、標準ライブラリはユーザー提供のリソースを管理するためのいくつかのRAIIラッパーを提供します:
|
(C++11以降) |
注記
RAIIは、使用前に取得されないリソースの管理には適用されません:CPU時間、コア可用性、キャッシュ容量、エントロピープール容量、ネットワーク帯域幅、電力消費量、スタックメモリ。このようなリソースについては、C++クラスのコンストラクタがオブジェクトのライフタイム全体にわたるリソースの可用性を保証することはできず、他のリソース管理手段を使用する必要があります。