java - 範囲 - while try catch




"try"-"catch"ですべてのブロックをラップしないのはなぜですか? (10)

try-catchがコールスタックのさらに下の関数でスローされた未処理の例外を引き続き捕捉できるため、 すべてのブロックをtry-catchでカバーする必要はありません。 そのため、すべての関数にtry-catchを持たせるのではなく、アプリケーションの最上位レベルのロジックを持つことができます。 たとえば、他のメソッドなどを呼び出す多くのメソッドを呼び出すSaveDocument()トップレベルルーチンがあるかもしれません。これらのサブメソッドは、スローした場合でもSaveDocument()によって捕捉されるので、独自のtry-catchは必要ありませんSaveDocument()のキャッチ。

これは、次の3つの理由でうまくいきます。エラーを報告する場所が1つあるため、 SaveDocument() catchブロックが便利です。 これをすべてのサブメソッドで繰り返す必要はなく、何とかしたいものです。間違ったことについて有用な診断をユーザに提供する単一の場所です。

2つ目は、例外がスローされるたびに保存がキャンセルされることです。 すべてのサブメソッドtry-catchingで、例外がスローされると、そのメソッドのcatchブロックに入り、関数は実行されSaveDocument() 介して実行されます。 何かが既に間違っている場合は、おそらくすぐそこに止めたいと思うでしょう。

3つすべてのサブメソッドは、すべての呼び出しが成功したとみなすことができます。 呼び出しが失敗した場合、実行はcatchブロックにジャンプし、後続のコードは決して実行されません。 これにより、コードをもっときれいにすることができます。 たとえば、エラーコードは次のとおりです。

int ret = SaveFirstSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

ret = SaveSecondSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

ret = SaveThirdSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

それが例外で書かれる方法は次のとおりです:

// these throw if failed, caught in SaveDocument's catch
SaveFirstSection();
SaveSecondSection();
SaveThirdSection();

今何が起きているのかがはっきりしています。

例外セーフコードは、他の方法で記述するのが難しい場合があります。例外がスローされた場合には、メモリをリークさせたくありません。 オブジェクトが例外の前に常に破壊されるので、 RAII 、STLコンテナ、スマートポインタ、およびデストラクタでリソースを解放するその他のオブジェクトについて知っていることを確認してください。

私は、メソッドが例外をスローすることができれば、意味のあるtryブロックでこの呼び出しを保護しないことは無謀だと常に信じています。

私はちょうどあなたがブロックをキャッチしようとすることができる呼び出しをラップする必要があります投稿しました この質問に「非常に悪いアドバイス」と言われました。理由を理解したいと思います。


Mitch and others述べたように、あなたは何らかの方法で処理する予定はないという例外をキャッチすべきではありません。 例外を設計するときにアプリケーションが体系的に例外を処理する方法を検討する必要があります。 これは、通常、抽象概念に基づいたエラー処理のレイヤーを持っています。たとえば、データアクセスコード内のすべてのSQL関連のエラーを処理して、ドメインオブジェクトとやりとりしているアプリケーションの部分がそこにあるという事実にさらされないようにしますフードの下のどこかのDBです。

あなたが間違いなく"どこにでもすべてをキャッチする"匂いに加えて避けたいいくつかの関連するコードの匂いがあります。

  1. "catch、log、rethrow" :スコープベースのロギングが必要な場合は、例外(ala std::uncaught_exception() )のためにスタックがアンロールされているときに、そのデストラクタにログステートメントを発行するクラスを作成します。 興味のあるスコープにロギング・インスタンスを宣言するだけで、ロギングがあり、不要なtry / catchロジックは必要ありません。

  2. "catch、throw translated" :これは通常、抽象化の問題を指します。 特定の例外を複数の汎用的な例外に変換する連合ソリューションを実装している場合を除いて、おそらく不要な抽象レイヤーを持つことになるでしょう。 「明日必要とするかもしれません

  3. "キャッチ、クリーンアップ、再登場" :これは私のペットのおしっこの一つです。 これがたくさんある場合は、 Resource Acquisition is Initializationのテクニックを適用し、 janitorオブジェクトインスタンスのデストラクタにクリーンアップ部分を配置する必要があります。

try / catchブロックで散らばっているコードは、コードのレビューとリファクタリングのための良いターゲットになるtry私は考えています。 これは、例外処理が十分に理解されていないか、またはコードがアメブロになっており、リファクタリングを真剣に必要としていることを示しています。


この議論に追加したいのは、C ++ 11以降 、すべてのcatchブロックが例外を処理できる点まで例外的にrethrow起動する限り、多くの意味があります。 このようにバックトレースを生成することができます 。 したがって私は以前の意見は部分的に古いと考えています。

std::nested_exceptionstd::throw_with_nested使用する

のhereとhereこれを達成する方法について説明します。

派生した例外クラスでこれを行うことができるので、そのようなバックトレースに多くの情報を追加できます! また、GitHubのMWEを見てみましょう。バックトレースは次のようになります。

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

すべての関数の結果をテストする場合は、戻りコードを使用します。

例外の目的は、結果を頻繁にテストできるようにすることです。 アイデアは、あなたのより普通のコードから例外的な(珍しい、希少な)条件を分離することです。 これにより、普通のコードはよりクリーンでシンプルに保たれますが、それでも例外的な条件を処理することができます。

うまく設計されたコードでは、より深い関数がスローされ、より高い関数がキャッチする可能性があります。 しかし、重要なことは、 "間にある"多くの機能が例外的な条件を処理する負担から解放されることです。 彼らは「例外安全」でなければなりません。


ハーブ・サッターはこの問題についてhere書きhere 。 読書の価値は確かです。
ティーザー:

「例外的に安全なコードを書くことは、根本的に正しい場所に「試行」と「キャッチ」を書くことです。 話し合います。

この声明は、例外的安全性の根本的な誤解を反映している。 例外は単なるエラー報告の別の形式です。エラーセーフコードを書くことは、戻りコードをチェックしてエラー条件を処理する場所だけではありません。

実際には、例外的な安全性は、「試行錯誤」を書くことはめったにありません。 また、例外安全性がコードの設計に影響することを忘れないでください。 シーズニングの場合のようにいくつかの余分なcatchステートメントを追加することができるのは単なる補足事項ではありません。


メソッドは、ある感覚的な方法でそれを扱うことができるときにのみ例外をキャッチする必要があります。

それ以外の場合は、コールスタックの上位にあるメソッドがそれを理解できるようにするために、上に渡します。

他の人が指摘しているように、致命的なエラーが記録されるように、コールスタックの最高レベルに未処理例外ハンドラ(ロギングあり)を持たせることをお勧めします。


他の答えで述べたように、ある種のエラー処理をすることができるのであれば、例外をキャッチする必要があります。

例えば、あなたの質問を生み出した質問では、質問者は整数から文字列へのlexical_cast例外を無視するのが安全かどうかを尋ねます。 そのようなキャストは決して失敗してはならない。 それが失敗した場合、何かがプログラムでひどく間違っています。 あなたはその状況で回復するために何をする可能性がありますか? 信頼できない状態にあるので、おそらくプログラムを死に至らせるのが最善の方法です。 したがって、例外を処理するのが最も安全な方法です。


例外をスローできるメソッドの呼び出し側で例外を常に処理する場合、例外が無用になり、エラーコードを使用する方がよいでしょう。

例外の全ポイントは、コールチェーン内のすべてのメソッドで処理する必要がないという点です。


次の質問は「私は例外をキャッチしたので、次に何をしますか?」 あなたは何をしますか? あなたが何もしないならば、それはエラー隠しであり、何が起こったのかを見つける機会なしにプログラムは「うまくいかない」可能性があります。 あなたが例外をキャッチし、あなたが知っている場合にだけ捕まえたら、あなたが何をするのかを正確に理解する必要があります。


私が聞いた最善のアドバイスは、例外的条件について賢明に何かを行うことができ、「キャッチ、ログおよびリリース」は(ライブラリーでは時折避けられない)良い戦略ではない点でのみ例外をキャッチすることです。





try-catch