ScopeGuard を使用すると、本当にコードが改善されるのでしょうか?
-
09-06-2019 - |
質問
私は遭遇しました この記事 Andrei Alexandrescu と Petru Marginean によって何年も前に書かれたこの本では、例外安全なコードを作成するための ScopeGuard と呼ばれるユーティリティ クラスが紹介され、説明されています。これらのオブジェクトを使用したコーディングが本当により良いコードにつながるのか、それともガードのコールバックを catch ブロックで表現した方がよいという点でエラー処理が難読化されるのかを知りたいのですが。実際の実稼働コードでこれらを使用した経験のある人はいますか?
解決
それは間違いなくコードを改善します。それは不明瞭であり、コードは解決する価値があるという暫定的に定式化された主張 catch
RAII は確立されたイディオムであるため、block は単に C++ では真ではありません。C++ でのリソース処理 は リソースの取得によって行われ、ガベージ コレクションは暗黙のデストラクター呼び出しによって行われます。
一方で、明示的には catch
コード フローがはるかに複雑になり、リソース処理を明示的に行う必要があるため、ブロックを使用するとコードが肥大化し、微妙なエラーが発生します。
RAII(含む) ScopeGuard
s) は C++ のあいまいなテクニックではなく、しっかりと確立されたベスト プラクティスです。
他のヒント
はい。
すべての C++ プログラマーに 10 分間かけて学習することをお勧めできる C++ コードが 1 つあるとしたら、それは ScopeGuard (現在は無料で利用できるツールの一部です) ロキ図書館).
私は、作業中の小さな Win32 GUI プログラムに、ScopeGuard の (わずかに変更された) バージョンを使用してみることにしました。ご存知のとおり、Win32 にはさまざまな種類のリソースがあり、それらをさまざまな方法で閉じる必要があります (例:カーネルハンドルは通常、次のように閉じられます CloseHandle()
, 、GDI BeginPaint()
と組み合わせる必要があります EndPaint()
, など)これらすべてのリソースで ScopeGuard を使用し、作業バッファを割り当てるためにも使用しました。 new
(例えば。Unicode との間の文字セット変換用)。
驚いたのはその金額です 短い プログラムはそうでした。 基本的に、それは双方に利益をもたらします。コードは短くなり、同時により堅牢になります。将来のコード変更 何も漏らすことはできない. 。それができないのです。なんてクールなんでしょう?
私はメモリ使用量、つまり OS から返された解放する必要があるものを保護するためによく使用します。例えば:
DATA_BLOB blobIn, blobOut;
blobIn.pbData=const_cast<BYTE*>(data);
blobIn.cbData=length;
CryptUnprotectData(&blobIn, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &blobOut);
Guard guardBlob=guardFn(::LocalFree, blobOut.pbData);
// do stuff with blobOut.pbData
この特定のテンプレートを使用したことはありませんが、以前に同様のものを使用したことがあります。はい、さまざまな方法で実装された同様に堅牢なコードと比較すると、より明確なコードになります。
上記の回答には重要な注意点が 1 つ欠けていると思います。他の人が指摘したように、使用できます ScopeGuard
障害に関係なく、割り当てられたリソースを解放するため (例外)。しかし、スコープ ガードを使用する目的はそれだけではない可能性があります。実際、リンクされた記事の例では、 ScopeGuard
別の目的のために:取引。つまり、複数のオブジェクト (それらのオブジェクトが適切に RAII を使用している場合でも) があり、それらのオブジェクトを何らかの形で関連付けられた状態に維持する必要がある場合に便利です。これらのオブジェクトのいずれかの状態の変更によって例外が発生した場合 (これは、通常、その状態が変更されなかったことを意味すると思います)、すでに適用されているすべての変更をロールバックする必要があります。これにより、独自の問題が発生します (ロールバックも失敗したらどうなるでしょうか?)。このような相関オブジェクトを管理する独自のクラスを展開しようとすることもできますが、それらの数が増えると面倒になり、おそらく を使用することになるでしょう。 ScopeGuard
とにかく内部的に。
はい。
これは C++ では非常に重要だったので、D では特別な構文が作られました。
void somefunction() {
writeln("function enter");
// c++ has similar constructs but not in syntax level
scope(exit) writeln("function exit");
// do what ever you do, you never miss the function exit output
}
いや、そんなことはないと言わざるを得ません。ここでの答えは、なぜそれが本当にひどいアイデアであるかを示すのに役立ちます。リソースの処理は、再利用可能なクラスを通じて行う必要があります。スコープ ガードを使用して彼らが達成した唯一のことは、リソースを処理する 1 つのクラスを作成してすべてを完了するのではなく、Wazoo で DRY に違反し、リソースを解放するコードをコードベース全体に複製することです。
もし スコープ ガードには実際の用途があり、リソースの処理は ない それらの中の一つ。この場合、RAII は重複排除され、自動でスコープ ガードが手動でコードを複製するか無効になるため、プレーン RAII よりも大幅に劣ります。