6

つまり、throw、try {} catch {} に関するすべての言語ルールを知っていましたが、実際にそれらを正しく使用しているかどうかはわかりません。次の例を参照してください。

私たちは、あらゆる種類の画像処理を行う大きな科学的コードを持っています。最近、それを改良してより堅牢にすることにしました。頻繁に使用されるルーチンの 1 つはvoid rotate_in_place(float* image, image_size sz)です。

より堅牢にするために、コードの先頭にサニティ チェックを追加します。

void rotate_in_place(float* image, image_size sz) {
    // rotate_in_place does not support non-square image;
    if (sz.nx != sz.ny)  throw NonSquareImageError;
    // rotate_in_place does not support image too small or too large
    if (sz.nx <= 2 || sz.nx > 1024)  throw WrongImageSizeError;
    // Real rode here
    .....
}

問題は、rotate_in_place() が 1000 を超える場所で使用されていることです。rotate_in_place() の各呼び出しを try{} catch {} でラップする必要があります。これにより、コードが非常に肥大化するように見えます。もう 1 つの可能性は、try{} catch{} をラップせずにプログラムを終了させることですが、これは単に使用することとどう違うのですか

if (sz.nx != sz.ny) {
    cerr << "Error: non-squared image error!\n";
    exit(0);
}

要するに、throw、try、catch を使用することの本当の利点についてはよくわかりませんが、何か良い提案はありますか?

4

8 に答える 8

6

エラーを処理するすべてのサイトには、ブロックが必要tryです。catchそれはすべてあなたのデザインに依存します、しかし私はあなたがすべてのrotate_in_placeコールサイトでエラーを処理する必要があるとは思えません、あなたはおそらくほとんどの場合上向きに伝播することから逃れるでしょう。

エラーの印刷と使用exitは、次の3つの理由で不適切です。

  1. エラーを処理することはできません。exitは処理していません(エラーが絶対に重大な場合に実行されたが、関数がそれを認識できない場合を除きます—呼び出し元には回復する方法がある可能性があります)。
  2. ハードコードされたストリームに書き込むことで関数の責任を拡張しますが、これは利用できない場合もあります(これはrotate_in_place、そうではありませんrotate_in_place_and_print_errors_and_kill_the_program_if_something_is_wrong)—これは再利用性を損ないます。
  3. このアプローチではすべてのデバッグ情報が失われます(未処理の例外からスタックトレースを生成できます。毎回ベイルアウトする関数では何もできません。未処理の例外はバグですが、ソースをたどることができるバグです) 。
于 2011-10-01T16:34:13.323 に答える
5

例外の一般的なルールは、「即時呼び出しサイトはここで何が起こっているかを気にしますか?」です。呼び出しサイトが気にする場合は、ステータスコードを返すことはおそらく理にかなっています。そうでなければ、投げることはより理にかなっています。

このように考えてください。確かに、rotate in placeメソッドには、無効な引数タイプがいくつかあります。その場合は、おそらくをスローする必要がありますstd::invalid_argument。たとえば、の呼び出し元がrotate_in_place、画像が正方形ではない場合に対処したい、または対処方法を知っている可能性は低いため、例外として表現する方が適切です。

もう1つの可能性は、try {} catch {}をラップせずにプログラムを終了させることですが、これは単に使用する場合とどのように異なりますか

if (sz.nx != sz.ny) {
    cerr << "Error: non-squared image error!\n";
    exit(0);
}

後で誰かがあなたの関数を取得して、たとえばGUIアプリケーションに入れたい場合、エラーに基づいてプログラムを終了する必要がないため、これは異なります。彼らはその例外をユーザーにとってきれいなもの、またはそのようなものに変えることができます。

<iostream>また、今のところ、エラー書き込みを行うためにその翻訳ユニットに引き込む必要がないという利点もあります。

私は通常、次のようなパターンを使用します。

int realEntryPoint()
{
    //Program goes here
}

int main()
{
    //Allow the debugger to get the exception if this is a debug binary
    #ifdef NDEBUG
    try
    #endif
    {
      return realEntryPoint();
    }
    #ifdef NDEBUG
    catch (std::exception& ex)
    {
      std::cerr << "An exception was thrown: " << ex.what() << std::endl;
    }
    #endif
}
于 2011-10-01T16:32:44.077 に答える
4

ここで問題となるのは、rotate_in_place()が1000以上の場所で使用されていることです。の各呼び出しをでラップするrotate_in_place()try{} catch {}、コードが非常に肥大化するように見えます。

それはそうなるでしょう、そしてそれはそもそも例外を使うという目的を打ち負かします。

もう1つの可能性は、try {} catch {}をラップせずにプログラムを終了させることですが、これは単に[...]を使用する場合とどのように異なりますか。

後でいつでも例外処理の場所を変更できること。ある時点で、エラーを適切に処理する(おそらくエラーから回復する)ためのより良い場所を見つけた場合は、それを配置するポイントですcatch。時々、それはあなたが例外を投げるまさにその関数にあります。コールチェーンの上位にある場合もあります。

catch念のため、 -allを入れてくださいmain。などの標準的な例外から例外を派生さstd::runtime_errorせると、これをはるかに簡単に行うことができます。

于 2011-10-01T16:32:32.597 に答える
3

例外処理を使用する際のポイントは、次の簡単なルールに当てはまります。

  • 悪いユーザー入力が原因で何か悪いことが起こる可能性があるとすぐに(内部ロジックはアサーション/ロギングを介して処理する必要があります)、例外をスローします。できるだけ早く、そしてできるだけ多く投げてください:C ++の例外は、通常、.Netの例外と比較してかなり安価です。
  • エラーを処理できない場合は、例外を伝播させます。これはほとんど常に意味します。

覚えておくべきことは次のとおりです。例外は、処理できるポイントまでバブルする必要があります。これは、エラーのフォーマットが設定されたダイアログボックスを意味する場合もあれば、重要でないロジックが結局実行されないことを意味する場合もあります。

于 2011-10-01T16:33:05.537 に答える
1

例外を使用すると、呼び出し元はエラーの処理方法を決定できます。関数内で直接呼び出した場合exit、呼び出し元がエラーの処理方法を決定できずにプログラムが終了します。また、を使用するとexit、スタックオブジェクトは巻き戻されません。:-(

于 2011-10-01T16:31:45.297 に答える
1

関数呼び出しが成功した場合、rotate_in_placeがブール値を返すようにすることができます。そして、関数パラメータを介して回転した画像を返します。

bool rotate_in_place(float* image, image_size sz, float** rotated_image) {
    // rotate_in_place does not support non-square image;
    if (sz.nx != sz.ny)  return false;
    // rotate_in_place does not support image too small or too large
    if (sz.nx <= 2 || sz.nx > 1024)  return false;
    // Real rode here
    .....
    return true;
}
于 2011-10-01T16:32:48.510 に答える
0

場合によります。

例外は通常、キャッチ/処理することを目的としています。あなたの場合、例外を処理することは可能ですか(たとえば、ユーザーが非正方形の画像を提供したため、再試行するように依頼します)。しかし、それについてあなたができることが何もないなら、それcerrは行く方法です。

于 2011-10-01T16:32:29.500 に答える
0

そうですね、例外を実際に使用するとコードが肥大化することに同意します。それが私が彼らを好きではない主な理由です。

とにかく、あなたの例に関して:例外をスローすることとexit()を使用することの主な違いは、例外の処理がエラー/例外を生成したプログラムフラグメントの外部で発生する(または発生するはずである)ため、関数/クラスのユーザーがエラーを処理する方法を指定しないでください。例外を使用することで、プログラムの中止、エラーの報告、特定のエラーからの回復など、さまざまな処理を許可できます。

TLDNR:例外を使用する場合、コードの例外生成部分で、例外ケースの処理方法を指定する必要はありません。これは外部プログラムで発生し、コードの使用方法に応じて変更できます。

于 2011-10-01T16:36:51.173 に答える