2011-01-25 2 views
5

私はC++でエラー処理を理解しようとしています。C++初心者の質問 - try、throw、catchを使った基本的なエラー処理

私はtry、throw、catchを使ってより良いスタイルであり、戻り値を持つif文を使うよりも複雑ではないことを読んでいます。しかし、私は実際にどのように試して、投げて、キャッチするか分かりません。私は下の簡単な例を作りました。問題や悪いスタイルについてのフィードバックを得ることは素晴らしいことです。私の目標は、別の計算の結果をチェックする例から関数を作ることです。

私はtry、throw、catchに関する質問があります: (1)catchステートメントは自分の関数に含めるべきですか?あるいは、main()や初期計算が行われている関数のように、別の場所に置かなければなりませんか?

(2)このシンプルなものを試して、キャッチして投げるのは大変ですか(私のスタイルを改善したいですか?)

(3)エラーが発生した場合は、プログラムを終了します。どうすればいい?あるいは、「キャッチ」とはそれが自動的に行われることを意味しますか?

(4)私はcerrの使用を理解していません。どうして吹き出しを使用しないのですか?私はここで正しくcerrを使いましたか? if/elseステートメントでも使用する必要がありますか?

ありがとうございました。

double calculated = 10.2; // from previous calculation 
double tolerance = 0.3; // I can set this in this function 
double valueWanted = 10.0; // from previous calculation 

const int calcError = 5; // I picked this number randomly to use for indicating an error 

try 
{ 
    if (fabs(fTargetValue - fCalculated) <= fTolerance) 
    cout << "Result is within range."; 

    else 
    cout << "Failed."; 
    throw calcError; 
} 

catch (const int calcError) 
{ 
    cerr << "The calculation failed.\n" << endl; 
} 
+3

注意してください。 「例外は何のために良いの?」のような質問をするときあなたは宗教的議論の領域に入る。一部の人は、「例外は例外的な状況のためのものだ」という忠実な助言を与えるでしょう。他の人は(私自身も含めて)これはナンセンスだと言います。あなたが本当に例外を理解する前にいずれかのアドバイスを盲目的に従えば、あなたが賢明になるまでに数年かかることがあり、議論の両面を見ることができます。このアドバイスを塩の穀物で取り、自分のために決めてください。 –

答えて

4

よくある質問です。

(1)あなたの関数にtry-catchを含めないでください。例外を投げることは、何か起こったことを外の世界に伝えるために行われます。あなたの関数内で問題を処理できるなら、まったく投げないでください^^良いエラー処理は一般的に、(呼び出し元の)ASAPエラーや、mainのような汎用ハンドラで、正常に処理されなかったエラー。

(2)目安として、例外的なものに例外を使用してください。エラーは例外的なものの良い候補です。オーバーフローや数学ライブラリのゼロ除算などの例外が発生する可能性があります。あなたは決定する必要がありますが、一般的に例外を伴うエラーを処理することは良いことです。

(3)catchは、プログラムが終了することを意味しません。実際それは反対です。例外をキャッチすることで、問題を処理すると言います^^終了したい場合、単純なプログラムの簡単な方法は、例外をキャッチしないことです。キャッチされない例外のデフォルトの動作はプログラム終了です^^代わりに、明示的にプログラムをいくつかのキャッチブロックで終了させます。

(4)cerrはcoutと似ていますが、別のファイル記述子です。これは、外部プログラムがcoutとcoutを区別できることを意味します。これはエラーのために使用されますが、それは外部プログラムにとっては重要ではありません。

my2c

+0

明確にするために、ゼロ除算とオーバーフローは例外をスローしません。それらは未定義の動作を引き起こします(一部のプラットフォームでは、未定義の動作がランタイム生成の例外として現れることがあります)。 –

+0

@James:精度に感謝します。私の考えは、あなたがそのようなものの例外を投げることができることを伝えることでした。私は明確にするために私の答えを変更しました... – neuro

2

あなたはelse後にスコープブレースを持っていないので、[OK]を、まずあなたの例では、すべての時間がスローされます。

ここで私が作った例です。したがって、結果が範囲内であったかどうかにかかわらず、が実行され、毎回throw calcErrorが実行されます。これを変更します。例外がスローされた場合には

else 
{ 
    cout << "Failed."; 
    throw calcError; 
} 

、コードは計算が失敗した旨、あなたが定義したcatchブロック内で開始されます。

結果が範囲内にある場合(throwは呼び出されません)、コードは catchブロックの直後にに直接実行されます。

タイプをスローすると、そのタイプはキャッチハンドラに到着します。これにより、異なるタイプのキャッチハンドラを定義できます。この場合、あなたはconst intを投げつけています(そして捕まえています)。それはすべて良いです。一般的には、std::exceptionまたはこれを派生します。独自の例外クラスには、エラーに関連する情報を含めることができます。あなたの場合は、範囲外だったという単純なメッセージを含めるか、実際には失敗したconst intを含めることができます。

2
  1. catchステートメントは、例外から回復し、プログラムが正常に継続できるようにすることができます(多分スロー機能で)スロー1から第一の機能アップにする必要があります。

  2. はい、実際にそれをキャッチしていると思われる点はありません。また、あなたの通常のプログラムフローは投げてはいけません。経験則として、実際に起こるはずのない状況に陥ったときに投げてください。例外は、例外的な状況で発生するため、例外と呼ばれます。多くの場合、プログラム環境と対話するときに例外を使用するのがよいでしょう。通常、メモリの割り当て、ファイルのオープン、ネットワークデバイスからの完全なデータパケットの受信など、特定のことがうまくいくことが期待されます。これらのすべての場合、例外がスローされるはずです。また、プログラムが入力を受け取った場合は、最初にそれを検証する必要があります。しかし、後で、処理中に、奇妙な入力データのために発生するゼロによる除算のように、検証によって既に拒否されているはずのデータに何か問題がある場合、それは例外的な状況になります。予期したことが起こったときに例外に頼りすぎると、プログラムのフローとロジックが推論するのが非常に困難になり、プログラムのメンテナンスが不必要に難しくなります。

  3. エラーがある場合は、キャッチしないでください。キャッチがない場合、例外はメイン関数までずっと行き、そこからランタイムに移動してプログラムを終了します。そして、ウィンドウのようないくつかのO.S.sではミニダンプファイルが作成され、プログラムをデバッグしてどのような例外によって終了するのかを知ることができます。

  4. cerrとcoutは、プログラムから情報を出力する2つの方法を提供します。別のプログラムがあなたのプログラムの出力を消費して何かをする場合、それはcoutを読み、それを理解することを期待します。つまり、消費しているプログラムが理解できないというエラーや警告を書き出す場合は、通常のプログラム出力を読み込んでいる2番目のプログラムを混乱させないように、それらをcerrに書き込まなければなりません。

1

あなたは他のケース

try 
{ 
    if (fabs(fTargetValue - fCalculated) <= fTolerance) 
    cout << "Result is within range."; 

    else { 
    cout << "Failed."; 
    throw calcError; 
    } 

} 
2

C++標準では、すべてから誘導されているいくつかの例外クラスを持って周りのブロックを忘れませんでした。私はあなたがPODを投げつけて捕まえるのではなく、そうすることをお勧めします。それはどちらか、難しいことではありませんし、例外階層の概要がhttp://www.richelbilderbeek.nl/CppExceptionHierarchy.htm

問題に行くために改善(およびエラーの種類を指定する)ので

class CalculationError : std::invalid_argument 
{ 
public: 
    CalculationError(std::string const& msg) 
     : std::invalid_argument(msg) 
    {} 
}; 

たいです:あなたはPODを投げるときタイプ、添付メッセージはありません。例外を投げることの大部分は、何が間違っているのか、それを修正する方法についてのメッセージを書く能力です。これはintを投げるときには不可能です。

C++にはlog、cerr、およびcoutの3つの出力ストリームがあります。それぞれが異なって表現されています。つまり、プログラムを起動するときに、コマンドラインを使用してこれらの各ストリームをフィルタリングすることができます。これは、cerrでフィルタリングして、プログラムがテストに失敗したかどうかを確認できるので、デバッグには最適です。

例:my_program > out.txt 2> log.txt

しかし、私はちょうどcerringない推薦する(coutでは、他の人がLOG.TXTするout.txtをすることになります)。通常、キャッチのポイントはプログラムの状態を反転させることです!たとえば、動的配列を割り当てようとしたときに失敗した場合、キャッチは再スローさせる前に配列を再度破棄する責任があります。そうしないと、メモリリークなどのようなものがたくさんあります。

注意が必要なことは、一度捕まえられると、例外が「飲み込まれる」ということです。あなたはこのことについていくつかの良い文学をしたい場合は、またはここでエラーに対処する必要はありませんができない場合、それは

catch(/* Error to be caught */) 
{ 
    throw; // Rethrows original exception, propagating it upwards 
} 

を書くことが最善である、ハーブサッターはExceptional C++という本を書き、それが実用的で例外安全性をカバーと啓蒙的な方法(イモ)。例外をスローする必要があるときと理由を知りたいのであれば、チェックアウトするのは間違いありません。

希望すると便利です。

+0

Uhmと 'cin'? – Puppy

+0

@DeadMG:ああ、何を意味するのか分かります。私は出力ストリームを意味しました。変更されます。 – IAE

+0

これは非常に役に立ちます。どうもありがとうございました! – Ant