すばらしい答えがあるので、忘れてしまったことをいくつか追加します。
0. RAIIはスコープに関するものです
RAIIは両方についてです:
- コンストラクターで(どのリソースでも)リソースを取得し、デストラクターでリソースの取得を解除します。
- 変数が宣言されたときにコンストラクターが実行され、変数がスコープ外になったときにデストラクターが自動的に実行されます。
他の人はすでにそれについて答えたので、詳しくは述べません。
1. JavaまたはC#でコーディングする場合、すでにRAIIを使用しています...
MONSIEUR JOURDAIN:なんと!「ニコール、スリッパを持ってきて、寝酒をくれ」って言ったら、それは散文ですか?
哲学のマスター:はい、サー。
MONSIEUR JOURDAIN:40年以上の間、私はそれについて何も知らずに散文を話してきました。
—モリエール:中産階級の紳士、第2幕、シーン4
Monsieur Jourdainが散文で行ったように、C#やJavaの人々でさえすでにRAIIを使用していますが、隠れた方法で使用されています。たとえば、次のJavaコード(C#ではで置き換えsynchronized
て同じ方法で記述しますlock
):
void foo()
{
// etc.
synchronized(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
...はすでにRAIIを使用しています。mutexの取得はキーワード(synchronized
またはlock
)で行われ、スコープの終了時に取得解除が行われます。
その表記法はとても自然で、RAIIについて聞いたことがない人でもほとんど説明を必要としません。
JavaとC#に対するC ++の利点は、RAIIを使用して何でも作成できることです。たとえば、C ++ にsynchronized
も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
ですか?
[叫ぶ]それは最後のカウントダウンです!
—ヨーロッパ:ファイナルカウントダウン(申し訳ありませんが、引用符が足りませんでした。ここで... :-)
このfinally
句はC#/ Javaで使用され、スコープが終了した場合(return
または例外がスローされた場合)にリソースを破棄します。
明敏な仕様の読者は、C ++にfinally句がないことに気づくでしょう。RAIIはすでにリソースの破棄を処理しているため、C ++では必要ないため、これはエラーではありません。(そして、私を信じて、C ++デストラクタを書くことは、適切なJava finally節を書くこと、またはC#の正しいDisposeメソッドを書くことよりもはるかに簡単です)。
それでも、時には、finally
節はクールです。C ++でできますか?はい、できます!また、RAIIの代替使用を使用します。
結論:RAIIはC ++の哲学以上のものです:それはC ++です
RAII?これはC ++です!!!
— C ++開発者の憤慨したコメント、あいまいなスパルタ王と彼の300人の友人によって恥知らずにコピーされた
C ++である程度の経験を積むと、RAIIの観点から、コンストラクタとデストラクタの自動実行の観点から考え始めます。
スコープの観点から考え始めると、{
and }
文字はコードで最も重要なものになります。
そして、RAIIの点でほぼすべてが適切に適合します:例外の安全性、ミューテックス、データベース接続、データベース要求、サーバー接続、クロック、OSハンドルなど、そして最後に、メモリです。
最終的にすべての変更をコミットするかどうかを決定するまで、行とコードの行を実行する「トランザクションプログラミング」スタイルで書き込むこともできるため、データベース部分は無視できません。または、不可能であれば、すべての変更を元に戻します(各行が少なくとも強力な例外保証を満たす限り)。(トランザクションプログラミングについては、このハーブのサッターの記事の 2番目の部分を参照してください)。
そして、パズルのように、すべてが収まります。
RAIIはC ++の大部分を占めているため、C ++はそれなしではC ++にはなり得ません。
これは、経験豊富なC ++開発者がRAIIに夢中になっている理由と、RAIIが別の言語を試すときに最初に検索する理由であることを説明しています。
そしてそれは、ガベージコレクター自体が素晴らしい技術であるにもかかわらず、C ++開発者の観点からはそれほど印象的でない理由を説明しています。
- RAIIは、GCで処理されるほとんどのケースをすでに処理しています
- GCは、純粋に管理されたオブジェクトの循環参照を使用して、RAIIよりも適切に処理します(弱いポインターのスマートな使用によって軽減されます)。
- それでも、GCはメモリに制限されていますが、RAIIはあらゆる種類のリソースを処理できます。
- 上記のように、RAIIはさらに多くのことができます...