std :: exceptionから継承する必要がありますか?


98

少なくとも1つの信頼できるソース(私が採用したC ++クラス)で、C ++のアプリケーション固有の例外クラスがから継承することを推奨しているのを見てきましたstd::exception。このアプローチの利点ははっきりしません。

C#から継承する理由ApplicationExceptionは明らかです。便利なメソッド、プロパティ、コンストラクターをいくつか取得し、必要なものを追加またはオーバーライドするだけです。ではstd::exception、それあなたが得るすべてであると思わwhat()あなただけのようにも自分で作成することができオーバーライドする方法。

それではstd::exception、アプリケーション固有の例外クラスの基本クラスとして使用することにはどのような利点がありますか?継承しない理由はありますstd::exceptionか?


あなたはこれを見てみたいかもしれません:stackoverflow.com/questions/1605778/1605852#1605852
sbi

2
特定の質問とは無関係の副次的注意として、あなたが取るC ++クラスは、必ずしもそれら自身の権利から、優れた実践に関する信頼できる情報源である必要はありません。
Christian Rau 2013年

回答:


69

主な利点は、あなたのクラスを使用して、そのコードが何の正確な型を知っている必要はありませんでthrow、それではなく、単にすることができます。 編集:マーティンと他の人が述べたように、実際にはヘッダーで宣言されたのサブクラスの1つから派生したいとします。catchstd::exception

std::exception<stdexcept>


21
メッセージをstd :: exceptionに渡す方法はありません。std :: runtime_errorは文字列を受け入れ、std :: exceptionから派生します。
マーティンヨーク

14
メッセージを例外タイプコンストラクターに渡すべきではありません(メッセージをローカライズする必要があることを考慮してください)。代わりに、エラーを意味的に分類する例外タイプを定義し、ユーザーフレンドリーなメッセージをフォーマットするために必要なものを例外オブジェクトに格納します。 、それから漁獲場所でそうしてください。
Emil

std::exceptionされている定義<exception>ヘッダ(プルーフ)。 -1
Alexander Shukaev

5
だからあなたのポイントは何ですか?定義は宣言を意味しますが、ここで何が間違っていると思いますか?
Nikolai Fetissov 2016年

40

の問題std::exceptionは、メッセージを受け入れるコンストラクター(標準に準拠したバージョン)がないことです。

結果として、から派生することを好みますstd::runtime_error。これはから派生してstd::exceptionいますが、そのコンストラクターを使用すると、C-Stringまたはa std::stringを、が呼び出されたchar const*ときに(として)返されるコンストラクターに渡すことができますwhat()


11
スローの時点でユーザーフレンドリーなメッセージをフォーマットすることはお勧めできません。これは、ローカライズ機能などと低レベルのコードを組み合わせるためです。代わりに、すべての関連情報を例外オブジェクトに格納し、例外タイプとそれに含まれるデータに基づいて、キャッチサイトがユーザーフレンドリーなメッセージをフォーマットできるようにします。
Emil

16
@ Emil:例外ではありませんが、ユーザーが表示できるメッセージを運んでください。ただし、これらは通常、ログの記録のみを目的としています。スローサイトでは、ユーザーメッセージを作成するためのコンテキストがありません(これはとにかくライブラリの可能性があります)。キャッチサイトにはより多くのコンテキストがあり、適切なメッセージを生成できます。
マーティンヨーク

3
例外はロギングの目的のみであるという考えがどこで得られたのかわかりません。:)
Emil

1
@エミル:私たちはすでにこの議論を経てきました。私が指摘したところ、それはあなたが例外に入れているものではありません。あなたは主題をよく理解しているように見えますが、あなたの主張には欠陥があります。ユーザー向けに生成されるエラーメッセージは、開発者向けのログメッセージとは完全に異なります。
マーティンヨーク

1
@エミル。このディスカッションは1年前のものです。この時点で独自の質問を作成します。私に関する限り、あなたの議論は間違っています(そして投票に基づいて、人々は私に同意する傾向があります)。私のライブラリに実装するための「適切な数の」例外は何ですか?を参照してくださいそして、一般的な例外をキャッチすることは本当に悪いことですか?以前のダイアトリブ以降、nw情報を提供していません。
マーティンヨーク

17

継承の理由はstd::exception、例外の「標準」基本クラスであるため、たとえば、チームの他の人がそれを予期してbaseをキャッチするのは自然なことですstd::exception

便利さを求めているなら、コンストラクタstd::runtime_errorを提供するものから継承できますstd::string


2
std :: exception以外の標準の例外タイプから派生することは、おそらく良い考えではありません。問題は、それらがstd :: exceptionから非仮想的に派生することです。これにより、std :: exceptionへの変換が原因で、catch(std :: exception&)が暗黙的に例外をキャッチできないという多重継承を含む微妙なバグが発生する可能性があります。曖昧。
Emil

2
私はこの問題に関する後押しについての記事を読みました。それは純粋な論理の意味で合理的だと思われます。しかし、私はMHを例外的に野生で見たことがありません。例外でMHを使用することも考えないので、存在しない問題を修正するのは大槌のようです。これが本当の問題だったとしたら、そのような明らかな欠陥を修正するために標準委員会からのアクションを見たはずです。ですから、私の意見では、std :: runtime_error(およびファミリ)は、スローまたは派生元として完全に受け入れ可能な例外です。
マーティンヨーク、

5
@Emil:ほかの標準の例外から派生std::exceptionすることは優れたアイデアです。すべきではないことは、複数から継承することです。
Mooing Duck 2013

@MooingDuck:複数の標準の例外タイプから派生した場合、std :: exceptionが(非仮想的に)複数回派生することになります。boost.org/doc/libs/release/libs/exception/doc/…を参照してください。
エミール

1
@エミル:それは私が言ったことから来ています。
Mooing Duck 2013

13

私はかつて、以前の作者がint、HRESULTS、std :: string、char *、ランダムクラスなど、さまざまなものをスローしていた大きなコードベースのクリーンアップに参加しました。タイプに名前を付けるだけで、おそらくどこかでスローされました。そして、共通の基本クラスはまったくありません。私を信じて、スローされたすべての型がキャッチできる共通のベースを持ち、何もなくなることはないとわかった時点で、物事はより整然としていました。ですから、自分自身(そして将来的にコードを保守する必要がある人)に好意を示し、最初からそうしてください。


12

boost :: exceptionから継承する必要があります。これは、追加のデータを運ぶためのより多くの機能と十分に理解された方法を提供します...もちろん、Boostを使用していない場合は、この提案を無視してください。


5
ただし、boost :: exception自体はstd :: exceptionから派生しないことに注意してください。boost :: exceptionから派生する場合でも、std :: exceptionから派生する必要があります(別の注記として、例外タイプから派生する場合は常に、仮想継承を使用する必要があります。)
Emil

10

はい、あなたは由来するべきstd::exceptionです。

std::exceptionテキストメッセージを渡すことができないという問題があると回答した人もいますが、一般的に、スローした時点でユーザーメッセージをフォーマットすることはお勧めできません。代わりに、例外オブジェクトを使用して、すべての関連情報をキャッチサイトに転送します。キャッチサイトは、ユーザーフレンドリーなメッセージをフォーマットできます。


6
+1、良い点。懸念事項の分離と国際化の観点の両方から、プレゼンテーション層にユーザーメッセージを作成させることは間違いなく優れています。
ジョンMガント2010

@JohnMGantとEmil:興味深いことに、これをどのように行うことができるかについての具体的な例を挙げてください。std::exception例外の情報を導き出し、伝えることができることを理解しています。しかし、誰がエラーメッセージを作成/フォーマットする責任がありますか?::what()機能または他の何か?
alfC 2014年

1
キャッチサイトで、おそらくアプリケーション(ライブラリではなく)レベルでメッセージをフォーマットします。タイプごとにキャッチし、情報を調べてから、適切なユーザーメッセージをローカライズ/フォーマットします。
Emil

9

継承したい理由std::exceptionは、そのクラスに応じてキャッチされる例外をスローできるためです。

class myException : public std::exception { ... };
try {
    ...
    throw myException();
}
catch (std::exception &theException) {
    ...
}

6

継承に関して知っておくべき問題は、オブジェクトのスライスです。throw e;throw式を作成すると、例外オブジェクトと呼ばれる一時オブジェクトが初期化されます。そのオブジェクトのタイプは、のオペランドの静的タイプからトップレベルのcv修飾子を削除することによって決定されthrowます。それはあなたが期待しているものではないかもしれません。ここで見つけることができる問題の例。

これは継承に反対する議論ではなく、単に「知っておくべき」情報です。


3
私が思うに、「eを投げる」ということです。悪であり、「投げる」。大丈夫です。
James Schek、2009年

2
はい、throw;大丈夫ですが、このようなものを書く必要があるかどうかは明らかではありません。
キリルV.リヤドビンスキー2009年

1
これは、「throw e;」を使用して再スローが行われるJava開発者にとって特に痛いものです。
James Schek、2009年

抽象基本型からの派生のみを検討してください。抽象基本型を値でキャッチすると、エラーが発生します(スライスを回避)。それはあなたにエラーを与えるのコピーを投げしようとし、その後、参照によって抽象基本型を引く(再び回避スライシング。)
エミール

この問題は、メンバー関数を追加することによっても解決できraise()ますvirtual void raise() { throw *this; }。派生した各例外でオーバーライドすることを忘れないでください。
ジャスティンタイム-モニカ

5

違い:std :: runtime_errorとstd :: exception()

あなたがするかどうかをする必要があり、それを継承するかは、あなた次第です。標準std::exceptionとその標準の子孫は、1つの可能な例外階層構造(logic_errorサブ階層とruntime_errorサブ階層への分割)と1つの可能な例外オブジェクトインターフェイスを提案します。気に入ったら-使ってください。何らかの理由で別のものが必要な場合-独自の例外フレームワークを定義してください。


3

言語はすでにstd :: exceptionをスローしているため、適切なエラーレポートを提供するには、とにかくそれをキャッチする必要があります。独自のすべての予期しない例外に対して同じキャッチを使用することもできます。また、例外をスローするほとんどすべてのライブラリは、それらをstd :: exceptionから派生させます。

つまり、そのいずれか

catch (...) {cout << "Unknown exception"; }

または

catch (const std::exception &e) { cout << "unexpected exception " << e.what();}

そして、2番目のオプションは間違いなく優れています。


3

考えられるすべての例外がから派生しているstd::exception場合、catchブロックはcatch(std::exception & e)すべてを確実にキャプチャすることができます。

例外whatを取得したら、そのメソッドを使用して詳細情報を取得できます。C ++はダックタイピングをサポートしていないため、whatメソッドを持つ別のクラスは、それを使用するために別のキャッチと別のコードを必要とします。


7
std :: exceptionをサブクラス化してからキャッチしないでください(std :: exception e)。std :: exception&(またはポインター)への参照をキャッチする必要があります。それ以外の場合は、サブクラスデータをスライスします。
jmucchiello 2009年

1
今、それは馬鹿げた間違いでした-確かに私はよりよく知っています。stackoverflow.com/questions/1095225/...
マーク身代金

3

標準の例外タイプから派生するかどうかは、最初の問題です。そうすることで、すべての標準ライブラリ例外と独自の例外の単一の例外ハンドラーが有効になりますが、そのようなすべてのキャッチハンドラーも推奨されます。問題は、処理方法がわかっている例外のみをキャッチすることです。たとえばmain()では、what()文字列が終了前の最後の手段としてログに記録される場合、すべてのstd :: exceptionsをキャッチすることはおそらく良いことです。しかし、他の場所では、それが良い考えになることはまずありません。

標準の例外タイプから派生するかどうかを決めたら、どちらをベースにするかが問題になります。アプリケーションが国際化を必要としない場合、呼び出しサイトでのメッセージのフォーマットは、情報を保存して呼び出しサイトでメッセージを生成するのと同じくらい良いと思うかもしれません。問題は、フォーマットされたメッセージが不要な場合があることです。レイジーメッセージ生成スキームを使用する方が良い-おそらく事前に割り当てられたメモリで。次に、メッセージが必要な場合は、アクセス時に生成されます(場合によっては、例外オブジェクトにキャッシュされます)。したがって、スロー時にメッセージが生成される場合、基本クラスとしてstd :: runtime_errorのようなstd :: exceptionの派生物が必要です。メッセージが遅延生成される場合、std :: exceptionが適切なベースです。


0

例外をサブクラス化するもう1つの理由は、大規模なカプセル化システムで作業するときの設計面の改善です。検証メッセージ、ユーザークエリ、致命的なコントローラーエラーなどに再利用できます。メッセージのようなすべての検証を書き直したり再フックしたりする代わりに、メインのソースファイルで単純に「キャッチ」できますが、クラスセット全体のどこかにエラーをスローします。

たとえば、致命的な例外はプログラムを終了し、検証エラーはスタックをクリアするだけで、ユーザークエリはエンドユーザーに質問します。

この方法でこれを行うと、同じクラスを再利用できるが、異なるインターフェースで再利用できるようになります。たとえば、Windowsアプリケーションはメッセージボックスを使用でき、Webサービスはhtmlを表示し、レポートシステムはそれをログに記録します。


0

この質問はかなり古く、すでに十分に回答されていますが、C ++ 11で適切な例外処理を実行する方法に関するメモを追加したいのは、例外に関する議論でこれが欠けているためです。

使用std::nested_exceptionしてstd::throw_with_nested

StackOverflowのここここで、ネストされた例外を再スローする適切な例外ハンドラーを作成するだけで、デバッガーや面倒なロギングを必要とせずに、コード内の例外のバックトレースを取得する方法について説明しています。

派生した例外クラスでこれを行うことができるため、そのようなバックトレースに多くの情報を追加できます!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"

std::runtime_error例外がスローされたときに多くの情報を取得するためにサブクラスを作成する必要すらありません。

(を使用する代わりにstd::runtime_error)サブクラス化で私が見る唯一の利点は、例外ハンドラーがカスタム例外をキャッチして特別なことを実行できることです。例えば:

try
{
  // something that may throw
}
catch( const MyException & ex )
{
  // do something specialized with the
  // additional info inside MyException
}
catch( const std::exception & ex )
{
  std::cerr << ex.what() << std::endl;
}
catch( ... )
{
  std::cerr << "unknown exception!" << std::endl;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.