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


15

私の最初の「深刻な」C ++ライブラリを設計するとき、私は次のことを自問しています:

例外を派生させるのは良いスタイルstd::exceptionですか?それは子孫ですか?!

読んだ後でも

まだわかりません。というのは、一般的な(ただし、良くないかもしれない)プラクティスに加えて、ライブラリユーザーとしてstd::exception、ライブラリ実装で標準ライブラリ関数が失敗した場合にのみライブラリ関数がs をスローし、それについて何もできないと仮定するからです。しかし、それでも、アプリケーションコードを記述するとき、私にとっては非常に便利であり、また、単にをスローするだけでも見栄えが良いstd::runtime_errorです。また、私のユーザーは、定義された最小限のインターフェース(what()コードやコードなど)に依存することもできます。

また、たとえば、ユーザーが誤った引数を指定した場合、をスローするよりも便利なのは何std::invalid_argumentですか?だから、std :: exceptionのまだ一般的な使用と組み合わせて、他のコードで見ます:さらに進んで、カスタム例外クラス(例えばlib_foo_exception)から、そしてから派生してみませんかstd::exception

考え?


従うかどうかわかりません。あなたが継承という理由だけでstd::exceptionあなたが意味するものではありません投げますstd::exception。また、最初std::runtime_errorから継承しstd::exceptionwhat()メソッドはからstd::exceptionではなくから来std::runtime_errorます。また、などの一般的な例外をスローする代わりに、独自の例外クラスを作成する必要がありますstd::runtime_error
ビンセントサバード

3
違いは、lib_foo_exceptionクラスがから派生するstd::exception場合、ライブラリユーザーはライブラリをキャッチlib_foo_exceptionするだけでなく、ライブラリクラスのみをキャッチすることによってキャッチすることstd::exceptionです。そのため、ライブラリ例外ルートクラスがstd :: exceptionを継承するかどうかを尋ねることもできます
Superlokkus

3
@LightnessRacesinOrbit「…に加えて」という意味lib_foo_exceptionです。「キャッチする方法はいくつありますか?」から継承するstd::exceptionと、catch(std::exception)ORで実行できますcatch(lib_foo_exception)。から派生せずにstd::exception、場合によってのみ、それをキャッチしますcatch(lib_foo_exception)
スーパーロックス

2
@Superlokkus:無視しcatch(...)ます。この言語は、あなたが検討しているケース(および「誤動作」ライブラリー)を考慮しているためですが、それは現代のベストプラクティスではありません。
モニカとの明るさのレース

1
C ++での例外処理の設計の多くは、より粗く、より一般的なcatchサイトを促進する傾向があり、同様に、ユーザーエンド操作をモデル化するより粗いトランザクションを促進する傾向があります。の一般化されたキャッチングのアイデアを促進しない言語と比較するとstd::exception&、たとえば、try/catch非常に特定のエラーに関係する中間ブロックを含むコードが多く含まれていることが多く、例外処理の一般性がやや低下します手動のエラー処理、および発生する可能性のあるあらゆる種類のエラーをさらに重視します。

回答:


29

すべての例外はから継承する必要がありstd::exceptionます。

たとえば、を呼び出す必要がありComplexOperationThatCouldFailABunchOfWays()、スローされる可能性のある例外を処理したいとします。すべてがから継承される場合std::exception、これは簡単です。必要なcatchブロックは1つだけwhat()で、詳細を取得するための標準インターフェイス()があります。

try {
    ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
    cerr << e.what() << endl;
}

例外がから継承しない場合std::exception、これは非常にいものになります。

try {
    ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
    cerr << e.what() << endl;
} catch (Exception& e) {
    cerr << e.Message << endl;
} catch (framework_exception& e) {
    cerr << e.Details() << endl;
}

スローするruntime_errorinvalid_argument、独自のstd::exceptionサブクラスを作成してスローするかについて:私の経験則では、特定のタイプのエラーを他のエラーとは異なる方法で処理する必要がある場合(つまり、別のcatchブロックが必要な場合)に新しいサブクラスを導入します。

  • 考えられるすべてのタイプのエラーに新しい例外サブクラスを導入すると、それらを個別に処理する必要がない場合でも、クラスの拡散が多くなります。
  • 既存のサブクラスを再利用して特定のことを意味する場合(つまり、ここでruntime_errorスローされることは一般的な実行時エラーとは異なることを意味する場合)、既存のサブクラスの他の使用と競合するリスクがあります。
  • 私は場合はしていない私が投げてるというエラーが正確に(のような既存の標準ライブラリのエラーのいずれかに一致する場合、具体的にエラーを処理する必要がある、とinvalid_argument)、その後、私は既存のクラスを再利用します。この場合、新しいクラスを追加してもあまりメリットがありません。(C ++コアガイドラインはここで私に同意しません-常に独自のクラスを使用することをお勧めします。)

C ++コアガイドラインは、さらなる議論と例があります。


これらすべてのアンパサンド!C ++は奇妙です。
SuperJedi224

2
@ SuperJedi224とそれらのすべての異なる文字!英語は変です。
ヨハネス

この論理的根拠は私には意味がありません。これはcatch (...)(リテラルの省略記号を使用して)何のためですか?
Maxpm

1
@Maxpm catch (...)は、スローされたもので何もする必要がない場合にのみ役立ちます。私の例のように、特定のエラーメッセージを表示または記録するなど、何かをしたい場合は、それが何であるかを知る必要があります。
ジョシュケリー

9

ライブラリユーザーとして、標準のライブラリ関数がライブラリ実装で失敗した場合にのみライブラリ関数がstd :: exceptionsをスローし、それについて何もできないと仮定します

それは間違った仮定です。

標準的な例外タイプは、「一般的な」使用のために提供されています。これらは、標準ライブラリでのみ使用されるようには設計されていません。

はい、すべてを最終的にから継承させstd::exceptionます。多くの場合、それはstd::runtime_errorまたはからの継承を伴いますstd::logic_error。実装している例外のクラスに適切なものは何でも。

これはもちろん主観的なものです。いくつかの一般的なライブラリは、標準ライブラリから標準ライブラリを切り離すために、標準例外タイプを完全に無視します。個人的には、これは非常に利己的だと思います!例外をキャッチするのがはるかに難しくなります。

個人的に言えば、私はしばしばただ投げるだけで終わりstd::runtime_errorです。しかし、それはあなたがあなたが求めているものではない、あなたの例外クラスを作るためにどのくらいきめ細かいかについての議論に入っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.