そこには優れた答えがあるので、忘れていたことをいくつか追加します。
0. RAII はスコープに関するものです
RAII は次の両方に関するものです。
- コンストラクターでリソースを取得し (リソースに関係なく)、デストラクタで取得を解除します。
- 変数が宣言されたときにコンストラクターが実行され、変数がスコープ外になったときにデストラクタが自動的に実行されます。
他の方が既に回答されているので、ここでは割愛します。
1. Java または C# でコーディングする場合、すでに RAII を使用しています...
ムッシュー・ジュルダン: なんと!「ニコール、スリッパを持ってきて、ナイトキャップをくれ」と言うのは散文ですか?
哲学の達人: はい、サー。
ムッシュー・ジュルダン: 40年以上、私は散文について何も知らずに話してきました。
— モリエール: 中流階級の紳士、第 2 幕、第 4 場
ムッシュ・ジュールデインが散文で行ったように、C# や Java の人々でさえ、すでに RAII を使用していますが、隠れた方法で使用しています。たとえば、次の Java コード (C# でも同じように に置き換えて記述synchronized
しますlock
):
void foo()
{
// etc.
synchronized(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
... is already using RAII: ミューテックスの取得はキーワード (synchronized
またはlock
) で行われ、取得解除はスコープを出るときに行われます。
RAIIを知らない人でも説明不要なほど自然な表記です。
ここで C++ が Java や C# より優れている点は、RAII を使用して何でも作成できることです。たとえば、C++ には直接的な組み込みのsynchronized
norはありませんlock
が、まだそれらを使用できます。
C++ では、次のように記述します。
void foo()
{
// etc.
{
Lock lock(someObject) ; // lock is an object of type Lock whose
// constructor acquires a mutex on
// someObject and whose destructor will
// un-acquire it
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
Java/C# の方法 (C++ マクロを使用) で簡単に記述できます。
void foo()
{
// etc.
LOCK(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
2. RAIIには別の用途があります
白うさぎ: [歌] 遅れました / 遅れました / 非常に重要なデートのために。/「こんにちは」と言う時間はありません。/ さようなら。/ 遅れました、遅れました、遅れました。
— アリス・イン・ワンダーランド (ディズニー版、1951年)
コンストラクターが呼び出されるタイミング (オブジェクト宣言時) と、それに対応するデストラクタが呼び出されるタイミング (スコープの終了時) がわかっているため、ほぼ魔法のようなコードを 1 行で記述できます。C++ のワンダーランドへようこそ (少なくとも、C++ 開発者の観点からは)。
たとえば、上記のロック オブジェクトが使用されたように、カウンター オブジェクトを記述し (演習としてそれを許可します)、その変数を宣言するだけでそれを使用できます。
void foo()
{
double timeElapsed = 0 ;
{
Counter counter(timeElapsed) ;
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
もちろん、これもマクロを使用して Java/C# の方法で記述できます。
void foo()
{
double timeElapsed = 0 ;
COUNTER(timeElapsed)
{
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
3. C++ に がないのはなぜfinally
ですか?
【悲鳴】ファイナルカウントダウンです!
— ヨーロッパ: ファイナル カウントダウン (申し訳ありませんが、引用符がありませんでした。
この句は、C#/Java で使用され、スコープが終了した場合 (例外またはスローfinally
された例外による) にリソースの破棄を処理します。return
鋭い仕様書の読者は、C++ に finally 節がないことに気付くでしょう。RAII は既にリソースの破棄を処理しているため、C++ では必要ないため、これはエラーではありません。(そして、私を信じてください。C++ デストラクタを記述することは、正しい Java の finally 句や、C# の正しい Dispose メソッドを記述するよりもはるかに簡単です)。
それでも、finally
節がクールな場合もあります。C++でできますか?はい、できます!また、RAII を別の方法で使用します。
結論: RAII は C++ の哲学以上のものです: それは C++ です
らい?これはC++です!!!
— C++ 開発者の憤慨したコメント、無名のスパルタ王と彼の 300 人の友人によって恥知らずにコピーされました
C++ である程度の経験を積むと、RAIIの観点から、コンストラクターとデストラクタの自動実行の観点から考えるようになります。
スコープの観点から考え始めると、{
および}
文字がコード内で最も重要なものになります。
そしてほとんどすべてが RAII に関して適切に適合します: 例外の安全性、ミューテックス、データベース接続、データベース要求、サーバー接続、クロック、OS ハンドルなど、そして最後になりましたが重要なメモリです。
データベースの部分は無視できません。代価を支払うことを受け入れれば、「トランザクション プログラミング」スタイルで記述し、最終的にすべての変更をコミットするかどうかを決定するまでコードの行と行を実行することさえできるからです。 、または、不可能な場合は、すべての変更を元に戻します (各行が少なくとも強力な例外保証を満たしている限り)。(トランザクショナル プログラミングについては、このHerb の Sutter 記事の第 2 部を参照してください)。
パズルのように、すべてが収まります。
RAII は C++ の大部分を占めており、C++ はそれなしでは C++ とは言えません。
これは、経験豊富な C++ 開発者が RAII に夢中になる理由と、別の言語を試すときに RAII が最初に検索される理由を説明しています。
また、ガベージ コレクター自体が優れたテクノロジであるにもかかわらず、C++ 開発者の観点からはそれほど印象的ではない理由を説明しています。
- RAII は、GC で処理されるほとんどのケースをすでに処理しています。
- GC は、純粋なマネージド オブジェクトの循環参照を RAII よりも適切に処理します (ウィーク ポインターのスマートな使用によって軽減されます)。
- それでも、GC はメモリに制限されていますが、RAII はあらゆる種類のリソースを処理できます。
- 上で説明したように、RAII はさらに多くのことができます...