いつ、どのように例外処理を使用する必要がありますか?


83

例外処理について読んでいます。例外処理とは何かについていくつかの情報を入手しましたが、いくつか質問があります。

  1. いつ例外をスローしますか?
  2. 例外をスローする代わりに、戻り値を使用してエラーを示すことはできますか?
  3. try-catchブロックですべての関数を保護すると、パフォーマンスが低下しませんか?
  4. いつ例外処理を使用しますか?
  5. そのプロジェクトのすべての関数にtry-catchブロックが含まれているプロジェクトを見ました(つまり、関数全体のコードがtry-catchブロックで囲まれています)。これは良い習慣ですか?
  6. try-catchと__try__exceptの違いは何ですか?


「いつ例外をスローするか」よりも具体的な質問をする必要があります。何か例外が発生したときに例外をスローします。
falmarri 2010

これについてはPHPに関連して書きましたが、ほとんどすべてがC ++に適用できると思います。ブログ投稿をチェックしください。
ircmaxell 2013年

回答:


96

これは私が必読だと思う例外に関する非常に包括的なガイドです:

例外とエラー処理-C ++ FAQまたはC ++ FAQ lite

一般的な経験則として、プログラムが外部を識別できる場合は例外をスローします実行を妨げる問題。サーバーからデータを受信し、そのデータが無効な場合は、例外をスローします。ディスク容量が不足していますか?例外をスローします。宇宙線はあなたがデータベースに問い合わせることを妨げますか?例外をスローします。ただし、独自のプログラム内から無効なデータを取得した場合でも、例外をスローしないでください。問題が自分の悪いコードに起因する場合は、ASSERTを使用してそれを防ぐことをお勧めします。例外処理は、プログラムが処理できない問題を特定し、ユーザーが処理できるため、ユーザーについて通知するために必要です。ただし、プログラムのバグはユーザーが処理できるものではないため、プログラムがクラッシュすると、「answer_to_life_and_universe_and_everythingの値は42ではありません!これは決して発生しないはずです!!!! 11」という例外が発生します。

メッセージボックスを表示するなど、何か便利なことができる例外をキャッチします。どういうわけかユーザー入力を処理する関数内で一度例外をキャッチすることを好みます。たとえば、ユーザーが「すべてのhunamを全滅させる」ボタンを押すと、annihilateAllHunamsClicked()関数内に、「できません」と言うtry ... catchブロックがあります。hunamkindの消滅は、数十の関数を呼び出す必要がある複雑な操作ですが、ユーザーにとってはアトミック操作であるため、1回のtry ... catchしかありません。ボタンを1回クリックするだけです。すべての関数の例外チェックは冗長で醜いです。

また、RAIIに精通すること、つまり、初期化されたすべてのデータが自動的に破棄されることを確認することはお勧めできません。そして、それはスタックで可能な限り初期化することで達成できます。ヒープで何かを初期化する必要がある場合は、ある種のスマートポインターを使用してください。例外がスローされると、スタックで初期化されたものはすべて自動的に破棄されます。Cスタイルのダムポインタを使用すると、例外がスローされたときにメモリリークが発生するリスクがあります。これは、例外時にクリーンアップする人がいないためです(Cスタイルのポインタをクラスのメンバーとして使用できますが、必ずデストラクタで世話をしました)。


貴重なご意見をありがとうございます。>「プログラムが処理できない問題を特定し、ユーザーが処理できるため、例外処理が必要です」例外をスローする代わりに、エラー値を返し、呼び出し元の関数でエラーをチェックできます。ユーザーがそれを処理するのに役立つメッセージを表示します。
Umesha MS 2010

特にこのセクションの3番目と4番目のコードスニペットでFAQをもう一度見てください。parashift.com / c ++ - faq-lite / exceptions.html #faq-17.2例外は素晴らしいです。なぜなら、エラーはどの深さからでも表面化するからです。コールスタックの...エラーコードが水域であり、例外が潜水艦である場合のようです:)
セプタグラム2010

2
@Septagram「問題が独自の不正なコードに起因する場合は、ASSERTを使用してそれを防ぐことをお勧めします。」:アサートでは、バグのあるブランチに依存しないタスクを続行できません(プログラムに1つの関数のバグは、他の便利なことを実行できないことを意味するわけではありません)。カスタムロガーを使用することはできません。RAIIを使用することで恩恵を受けるデストラクタをバイパスします(プログラムが終了する前にファイルバッファをフラッシュしたいですか?それでそれらのデストラクタをスキップしない方がよいfcloseです!)。それはあなたに完全なスタックトレースを与えません。
demospring 2017

RAIIと「ダムポインター」について言うこと。「ダムポインタ」を使用しているからといって、RAIIをフォローしていないわけではありません。システムリソースを割り当てるときだけ、それらが割り当て解除されていることを確認する必要があることを忘れないでください。ポインタは、メモリアドレスを保持するスタック上の変数です。「ダムポインタ」を使用するために、オブジェクトの割り当てと初期化は必要ありません。「ダムポインタ」は、他の場所に割り当てられているオブジェクトのアドレスを保持するだけで、まったく問題なく使用できます。(次のコメントの続き)
SeanRamey 2017年

Displayに物を描画するために、Displayオブジェクトのアドレスをポインターに格納するRendererオブジェクトがある場合がありますが、Rendererオブジェクトを破棄すると、スマートポインターがDisplayオブジェクトを破棄するため、スマートポインターを使用できません。 、あなたが起こりたくないこと。この場合、「ダムポインタ」または参照のいずれかが唯一の方法です。
SeanRamey 2017年

12

例外は、さまざまな状況で役立ちます。

まず、前提条件の計算コストが非常に高いため、計算を実行し、前提条件が満たされていないことが判明した場合は例外を除いて中止する方がよい関数がいくつかあります。たとえば、特異行列を反転することはできませんが、それが特異であるかどうかを計算するには、非常に高価な行列式を計算します。とにかく関数内で実行する必要がある可能性があるため、行列を反転してレポートするだけです。例外をスローしてできない場合はエラー。これは基本的に、負の前提条件の使用法として例外です。

次に、コードがすでに複雑で、エラー情報をコールチェーンに渡すことが難しい場合もあります。これは、CとC ++がデータ構造モデルを壊していることが一因です。他のより良い方法がありますが、C ++はそれらをサポートしていません(Haskellでモナドを使用するなど)。この使用法は基本的に私がそれを正しく行うことを気にすることができなかったので、例外をスローします:それは正しい方法ではありませんが、それは実用的です。

次に、例外の主な用途があります。メモリやディスク容量などの十分なリソースなど、外部の前提条件または不変条件が利用できない場合にレポートすることです。この場合、通常、プログラムまたはその主要なサブセクションを終了します。例外は、問題に関する情報を送信するための優れた方法です。C ++例外は、プログラムの続行を妨げるエラーを報告するために設計されました

C ++を含むほとんどの現代言語で使用されている例外処理モデルは壊れていることが知られています。それは非常に強力すぎます。理論家は、完全にオープンな「何でも投げる」モデルや「多分それを捕まえないかもしれない」モデルよりも優れたモデルを開発しました。さらに、型情報を使用して例外を分類することは、あまり良い考えではありませんでした。

あなたができる最善のことがあるので、実際にエラーがあるとき、控えめにスロー例外、および他のそれに対処する方法はありませんとき、できるだけスロー点に近いようにキャッチ例外


15
「例外処理が壊れていることがわかっている」および「理論家がより良いモデルを開発した」というリンク/参照を追加してください。
Juraj Blaho 2010

1
例外をキャッチするときによく知っておく必要があるが、ほとんどの例外が有用なデータを提供しない重要なことは、例外をスローしたコードがシステムの状態に影響を与えたかどうかです。理論家のモデルのいずれかがこれに対処していますか?
スーパーキャット2011年

@JurajBlaho私はあなたが話しているリンク/参照を見つけようとしましたが成功しませんでした。彼の答えを編集して最後に追加できますか?
ForceMagic 2012年

例1と例3に同意します。ただし、コードが複雑すぎて例外をスローすることを考えている場合は、リファクタリングが必要になると思います。アサートは、コードが正しく実行されているかどうかを確認するための優れた方法(さらに優れた方法)でもあり、実際には例外よりも安価です。このテーマに興味のある人には、本の2つの章(アサーティブプログラミングと例外を使用する場合)をお勧めします。
ForceMagic 2012年

1
リンクは必要ありません。頭脳を使用します。キャッチされない例外をスローできます。それは良くないね。gotoは少なくとも常にどこかに行くので、gotoよりも悪いです。静的型付けは例外仕様に対応できません。ポリモーフィック関数が型変数の特定のインスタンスに依存しているため、例外仕様を持つことができる健全なシステムはありません。これらは新しいC ++標準から削除されていることに注意してください。新しいモデルは「区切られた継続」と呼ばれます:en.wikipedia.org/wiki/Delimited_continuation
Yttrill 2012年

4

問題が自分の悪いコードに起因する場合は、ASSERTを使用してそれを防ぐことをお勧めします。例外処理は、プログラムが処理できない問題を特定し、ユーザーが処理できるため、ユーザーについて通知するために必要です。しかし、プログラムのバグはユーザーが処理できるものではないため、プログラムのクラッシュはあまりわかりません

は受け入れられた答えのこの側面に同意しません。アサーションは、例外をスローするよりも優れたものではありません。例外が実行時エラー(または「外部の問題」)にのみ適している場合、何のstd::logic_errorためにありますか?

論理エラーは、ほとんど定義上、プログラムの続行を妨げる種類の条件です。プログラムが論理構造であり、その論理のドメイン外で条件が発生した場合、どのように続行できますか?あなたがたがそうするかもしれない間にあなたがたの入力を集めて、例外を投げなさい!

先行技術がないわけではありません。 std::vector、1つだけ挙げると、論理エラー例外、つまりstd::out_of_range。標準ライブラリを使用していて、標準例外をキャッチするためのトップレベルハンドラがない場合(what()を呼び出し(3)を終了するだけの場合)、プログラムは突然サイレントに終了します。

アサートマクロははるかに弱いガードです。回復はありません。つまり、いない限り、あなたはありません、その場合、デバッグビルド、実行していない何の実行を。アサートマクロは、計算が今日より6桁遅くなった時代に属します。論理エラーをテストするのに苦労するが、それが重要なときにそのテストを使用しない場合、本番環境では、コードに大きな自信を持っている方がよいでしょう!

標準ライブラリは論理エラー例外を提供し、それらを採用しています。それらは理由があります:論理エラーが発生し、例外的であるためです。Cがアサーションを備えているという理由だけで、例外がジョブをはるかにうまく処理する場合、そのような原始的な(そしておそらく役に立たない)メカニズムに依存する理由はありません。


3

これを読むのに最適

例外処理については、過去10年半にわたって多くのことが話題になっています。ただし、例外を適切に処理する方法についての一般的なコンセンサスにもかかわらず、使用法の違いは引き続き存在します。不適切な例外処理は、簡単に見つけて回避でき、単純なコード(および開発者)の品質指標です。絶対的なルールは気を配ったり誇張したりすることは知っていますが、原則として、try / catchを使用するべきではありません

http://codebetter.com/karlseguin/2010/01/25/don-t-use-try-catch/


3
その記事は本当に言おうとしているのではありません、try/を使用しないでくださいcatch {}
Craig McQueen

2

1.結果として、または問題の間のどこかで例外が発生する可能性がある場合、例外チェックがコードに含まれます。

2.try-catchブロックは、必要な場合にのみ使用してください。各try-catchブロックを使用すると、条件チェックが追加され、コードの最適化が確実に減少します。

3._try_exceptは有効な変数名だと思います...。


3
参考までに、__ tryおよび__exceptは、Microsoftの構造化例外処理(SEH)用です。msdn.microsoft.com/en-us/library/s58ftw19(v=vs.80).aspx
axw 2010

0

基本的な違いは次のとおりです。

  1. 1つはあなたのためにエラー処理を行います。
  2. 1つは、自分で行うことです。

    • たとえば、式を作成できます0 divide error。try catch1.を使用すると、エラーが発生したときに役立ちます。またはあなたif a==0 then..2.

    • 例外をキャッチしようとしない場合は、高速だとは思いません。単にバイパスするだけです。error発生した場合はthrew、外部ハンドラーに送られます。

自分自身を手渡すということは、問題がそれ以上進まないことを意味し、多くの場合、速度に利点がありますが、常にではありません。

提案:単純で論理的な場合は、自分で処理するだけです。


-3

多くのC / C ++純粋主義者は、例外を完全に思いとどまらせます。主な批判は次のとおりです。

  1. 遅い-もちろん、実際には「遅い」わけではありません。ただし、純粋なc / c ++と比較すると、かなりのオーバーヘッドがあります。
  2. バグが発生します-例外を適切に処理しないと、例外をスローする関数のクリーンアップコードを見逃す可能性があります。

代わりに、関数を呼び出すたびに戻り値/エラーコードを確認してください。


15
ナンセンス。まず、C ++について話します。「C / C ++」はありません。もちろん、Cのみのプログラマーは例外を除いて不快です。「pureC / C ++」は存在せず、例外は「pureC ++」の一部です。これらは標準に含まれています。バグのある例外処理コードを記述したり、バグのあるエラーを返すコードやバグのあるエラー状態コードを記述したりできます。例外を一般的にエラーが発生しやすいと見なすのは誤解を招きます。申し訳ありませんが、これは私がSOで見た中で最悪のアドバイスの一部です
Tony Delroy 2010

1
よろしければマークを付けてください。ただし、Cのバックグラウンドから来ているので、私自身や他の多くの人は例外の使用を完全に控えていると言えます。
スピードプレーン2010

4
1.例外が実際にスローされた場合にのみ遅くなります。また、例外は例外的な状況でのみスローされるため、例外と呼ばれます。1秒あたり9000を超える例外をスローするプログラムは、それを間違っています。2.純粋なnew-deleteでメモリ管理を行わないように注意すると、クリーンアップが自動的に行われます。Cスタイルのメモリ管理は、例外よりもはるかにエラーが発生しやすくなります。3.戻り値をチェックすると、コードが大量に追加されるため、読みにくく、保守しにくくなります。
セプタグラム2010

3
1)コンパイラによっては正しくない場合があります。また、すべての例外により、実行可能ファイルのサイズが大きくなり、処理速度が低下するバイトコードが大量に追加されます。2)純粋なnew-deleteが常に使用され、すべてがスタックオブジェクトになるわけではありません。3)戻り値のチェックはコードの行数が増える可能性がありますが、間違いなく保守性が高くなります。同意できないかもしれませんが、多くの場合、C ++の例外は悪いというのが一般的に受け入れられている合理的な信念です。例としてembedded-C ++を見てください。
スピードプレーン2010

11の反対票と8の賛成票。この意見は一般的なコンセンサスを反映していませんが、私が説明した理由で例外を警戒している開発者の大規模な派遣団がいることは明らかだと思います。
スピードプレーン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.