Resource Acquisition is Initialization (RAII) とはどういう意味ですか?
10 に答える
これはプログラミングのイディオムで、簡単に言うと、
- リソースをクラスにカプセル化します (通常、そのコンストラクターはリソースを取得しますが、必ずしもそうとは限りません** - リソースを取得し、そのデストラクタは常にそれを解放します)
- クラスのローカル インスタンスを介してリソースを使用する*
- オブジェクトがスコープ外になると、リソースは自動的に解放されます
これにより、リソースの使用中に何が起こっても、最終的には解放されることが保証されます (通常の復帰、含まれているオブジェクトの破壊、またはスローされた例外のいずれによる場合でも)。
これは C++ で広く使用されている優れた方法です。これは、リソースを安全に処理する方法であるだけでなく、エラー処理コードと主要な機能を混在させる必要がないため、コードがよりクリーンになるためです。
*
更新:「ローカル」は、ローカル変数、またはクラスの非静的メンバー変数を意味する場合があります。後者の場合、メンバー変数は初期化され、所有者オブジェクトで破棄されます。
**
Update2: @sbi が指摘したように、リソースはコンストラクター内で割り当てられることが多いですが、外部で割り当てられ、パラメーターとして渡される場合もあります。
「RAII」は「Resource Acquisition is Initialization」の略で、実際にはかなりの誤称です。これは、リソースの取得(およびオブジェクトの初期化) ではなく、リソースの解放(オブジェクトの破棄による) であるためです。 )。
しかし、RAII は私たちが付けた名前であり、定着しています。
この慣用句は、リソース (メモリのチャンク、開いているファイル、ロックされていないミューテックス、名前を付ける) をローカルの自動オブジェクトにカプセル化し、オブジェクトが破棄されたときにそのオブジェクトのデストラクタがリソースを解放することを特徴としています。それが属するスコープの終わり:
{
raii obj(acquire_resource());
// ...
} // obj's dtor will call release_resource()
もちろん、オブジェクトが常にローカルの自動オブジェクトであるとは限りません。クラスのメンバーになることもできます。
class something {
private:
raii obj_; // will live and die with instances of the class
// ...
};
このようなオブジェクトがメモリを管理する場合、それらはしばしば「スマート ポインター」と呼ばれます。
これには多くのバリエーションがあります。たとえば、最初のコード スニペットでは、誰かが をコピーしようとするとどうなるかという疑問が生じますobj
。最も簡単な方法は、単純にコピーを禁止することです。std::unique_ptr<>
次の C++ 標準で取り上げられている標準ライブラリの一部であるスマート ポインターは、これを行います。
このような別のスマート ポインターは、std::shared_ptr
それが保持するリソース (動的に割り当てられたオブジェクト) の "共有所有権" を備えています。つまり、自由にコピーでき、すべてのコピーが同じオブジェクトを参照します。スマート ポインターは、同じオブジェクトを参照するコピーの数を追跡し、最後のオブジェクトが破棄されるときに削除します。
3 番目のバリアントは、std::auto_ptr
オブジェクトは 1 つのポインターのみによって所有され、オブジェクトをコピーしようとすると、(構文ハッカーによって) オブジェクトの所有権がコピー操作のターゲットに転送されます。
本C++ Programming with Design Patterns Revealed では、RAII を次のように説明しています。
- すべてのリソースを取得する
- リソースの使用
- リソースの解放
どこ
リソースはクラスとして実装され、すべてのポインターにはそれらを囲むクラス ラッパーがあります (スマート ポインターになります)。
リソースは、コンストラクターを呼び出すことによって取得され、デストラクタを呼び出すことによって暗黙的に (取得とは逆の順序で) 解放されます。
手動のメモリ管理は、コンパイラの発明以来、プログラマが回避する方法を考案してきた悪夢です。ガベージ コレクターを備えたプログラミング言語は作業を楽にしますが、パフォーマンスは犠牲になります。この記事 -ガベージ コレクターの排除: RAII の方法では、Toptal のエンジニアである Peter Goodspeed-Niklaus がガベージ コレクターの歴史をのぞき見し、所有権と借用の概念が安全性の保証を損なうことなくガベージ コレクターを排除するのにどのように役立つかを説明しています。
多くの人が RAII は誤称であると主張していますが、実際にはこのイディオムの正しい名前であり、よく説明されていません。
ウィキペディアリソースの取得は初期化 (RAII) は、特定の言語の動作を記述するために、いくつかのオブジェクト指向の静的に型指定されたプログラミング言語で使用されるプログラミング イディオムです。RAII では、リソースの保持はクラス不変であり、オブジェクトの有効期間に関連付けられています。リソースの割り当て (または取得) は、オブジェクトの作成 (具体的には初期化) 中にコンストラクターによって行われ、リソースの割り当て解除 (解放) はオブジェクトの破棄中に行われます (具体的にはファイナライズ)、デストラクタによって。つまり、初期化が成功するには、リソースの取得が成功する必要があります。したがって、リソースは、初期化が終了してからファイナライズが開始されるまでの間に保持されることが保証され (リソースの保持はクラス不変です)、オブジェクトが生きている場合にのみ保持されます。したがって、オブジェクト リークがなければ、
名前については、単に「リソース取得」のアクションが初期化のアクションであり、リソース クラス オブジェクトの初期化/コンストラクターの一部であることを意味します。つまり、このイディオムを使用すると、リソースを操作するということは、リソース クラスを作成してリソースを保持し、クラス オブジェクトの構築時にリソースを初期化することを意味します。暗黙のうちに、リソースの割り当て解除がリソース クラス デストラクタで対称的に発生する必要があることを示唆しています。
これはどのように役立ちますか? もちろん、この慣用句を使用しないことを選択することもできますが、この慣用句で何が得られるか疑問に思っている場合は、
RAII 大規模な C++ プロジェクトでも、コンストラクターとデストラクターのペアの外側に、new または delete (または malloc/free) への単一の呼び出しが含まれていないことは非常に一般的です。またはまったく、実際には。
そして、あなたは避けることができます
exit: free_resouce() // exit 関数の前にリソースをクリーンアップします
またはRAII ロックを使用して、ロックを解除するのを忘れないようにします。
私はこの質問に何度か戻って読んだことがありますが、投票数が最も多かった回答は少し誤解を招くものだと思います。
RAII のキーは次のとおりです。
「それは(ほとんど)例外をキャッチすることではなく、主にリソースの所有権を管理することです。」
最も投票された回答はexception-safeを誇張しているため、混乱しました。
事実は次のとおりです。
try catch
catch
ブロックで RAII を使用してこれらのクラスのリソースを解放することを心配する必要がないことを除いて、例外を処理するためにまだ書く必要があります (以下の 2 つのコード例を確認してください)。それ以外の場合は、RAII 以外の各クラスの API を検索して、取得したリソースをcatch
ブロックで解放するために呼び出す関数を見つける必要があります。RAII はこれらの作業を保存するだけです。上記と同様に、RAII でコーディングする場合、記述するコードが少なくて済み、リソース解放関数を呼び出す必要がありません。すべてのクリーンアップはデストラクタで行われます。
また、上記のコメントで役立つと思われる 2 つのコード例を確認してください。
https://ideone.com/1Jjzuc,https://ideone.com/xm2GR9
PS One は python ステートメントと比較できます。ブロックwith .. as
内でも発生する可能性のある例外をキャッチする必要があります。with