10

C++ コードを使用すると、呼び出し元に予期せず例外がスローされるという問題が発生しました。使用しているモジュールのすべての行を読んで、例外をスローするかどうか、またスローする場合はどのタイプの例外かを確認することは、常に可能または実用的ではありません。

この問題に対処するための確立されたイディオムまたは「ベスト プラクティス」はありますか?

私は次のことを考えました:

  1. doxygen のドキュメントでは、例外をスローすると予想されるすべての関数とその型にコメントを追加できます。

    • プラス:シンプル。
    • マイナス: ユーザー エラーの対象となります。
  2. try/catch(...)安全 のためにアプリ全体を使用できます。

    • 利点: キャッチされない例外はもうありません。
    • 短所: スローから離れた場所で例外がキャッチされます。何をすべきか、何が間違っていたのかを理解するのは難しいです。
  3. 例外指定を使用する

    • プラス: これは、この問題に対処する言語認定の方法です。
    • マイナス: これを有効にするために必要な問題ライブラリのリファクタリング。コンパイル時に強制されないため、違反は実行時の問題に変わります。これは私が回避しようとしている問題です!

これらの方法の経験、または私が知らない追加の方法はありますか?

4

11 に答える 11

9

タイトルの質問に対する簡単な答え-関数がスローできることを示すイディオムは、「この関数はスローしない」ことを文書化することではありませんつまり、デフォルトですべてをスローできます。

C ++はJavaではなく、コンパイラーによってチェックされる例外はありません。C ++には、コードがスローしないと主張していることをコンパイラーが通知できるようにするものはありませんが、スローする可能性のあるものを呼び出します。したがって、これが実行時の問題であることを完全に回避することはできません。静的分析ツールが役立つかもしれませんが、確かではありません。

MSVCのみに関心がある場合は、空の例外仕様を使用するか__declspec(nothrow)、スローしない関数、およびスローする関数を使用することを検討できthrow(...)ます。MSVCは、nothrowと宣言された関数が実際にスローしないことを確認するコードを発行しないため、これによって非効率的なコードが生成されることはありません。GCCはで同じことを行うことができ-fno-enforce-eh-specsます。コンパイラのドキュメントを確認してください。その後、すべてが自動的に文書化されます。

オプション2、アプリ全体のtry-catchは、実際には「安全のため」ではありません。C++ランタイムを呼び出すよりも、例外を除いて(何かを印刷してきれいに終了するなど)もっと便利なことができると思うからですterminate。何かがスローされないという前提でコードを記述していて、実際にスローされる場合、たとえば、デストラクタが一貫性のある状態を誤って想定している場合など、どちらかが実際に発生する前に未定義になっている可能性があります。

私は通常、(1)の変形を行います。各関数ドキュメントに対して、それが提供する例外保証(nothrow、strong、weak、またはnone)。最後はバグです。最初のものは貴重ですがまれであり、優れたコーディングはスワップ関数とデストラクタにのみ厳密に必要です。はい、ユーザーエラーが発生する可能性がありますが、例外を除いたC++コーディングの手段はユーザーエラーが発生する可能性があります。次に、その上で、(1)を実施するのに役立つ場合は、(2)および/または(3)も実行します。

Symbianには、いくつかの点で例外のような「脱退」と呼ばれるメカニズムを備えた、C++の先行標準方言があります。Symbianの規則では、終了する可能性のある関数には、末尾にLを付けて名前を付ける必要があります:CreateL、、ConnectLなど。終了する可能性のあるものを呼び出しているかどうかをより簡単に確認できるため、平均してユーザーエラーが減少します。ご想像のとおり、アプリのハンガリアン記法を嫌うのと同じ人々がそれを嫌い、ほとんどすべての機能がなくなると、それは役に立たなくなります。また、ご想像のとおり、名前にLが含まれていない関数を作成する場合は、デバッガーで問題を特定するまでに長い時間がかかる可能性があります。これは、仮定が実際のバグから離れていることを示しているためです。

于 2009-08-11T17:06:21.843 に答える
7

この問題を解決する慣用的な方法は、コードが例外をスローできることを示すのではなく、オブジェクトに例外の安全性を実装することです。標準では、オブジェクトが実装する必要があるいくつかの例外保証が定義されています。

  • 非スロー保証: 関数は決して例外をスローしません
  • 強力な例外安全性の保証: 例外がスローされた場合、オブジェクトは初期状態のままになります。
  • 基本的な例外の安全性の保証: 例外がスローされた場合、オブジェクトは有効な状態のままになります。

そしてもちろん、標準はすべての標準ライブラリ クラスの例外安全性のレベルを文書化しています。

これは、C++ で例外を処理する実際の方法です。どのコードが例外をスローできるかできないかをマークするのではなく、RAII を使用してオブジェクトがクリーンアップされるようにし、適切なレベルの例外安全性を RAII オブジェクトに実装することを検討してください。例外がスローされます。

例外は、オブジェクトが無効な状態のままになることを許可する場合にのみ、実際に問題を引き起こします。それは決して起こらないはずです。オブジェクトは、少なくとも基本的な保証を常に実装する必要があります。(そして、適切なレベルの例外安全性を提供するコンテナー クラスを実装することは、啓発的な C++ の演習です ;))

ドキュメントに関しては、関数がどの例外をスローする可能性があるかを特定できる場合は、ぜひドキュメント化してください。しかし、一般に、他に何も指定されていない場合、関数がスローされる可能性があると想定されます。空のスロー仕様は、関数が決してスローしない場合を文書化するために使用されることがあります。そこにない場合は、関数がスローされる可能性があると想定します。

于 2009-08-11T23:59:05.630 に答える
6

率直に言って、ほぼすべての C++ 関数が例外をスローする可能性があります。これを文書化することについてあまり心配する必要はありませんが、代わりに、RAII などのイディオムを使用してコード例外を安全にします。

于 2009-08-11T16:49:42.097 に答える
2

ドキュメントは私が知っている唯一の合理的な方法のようです。

例外仕様に関しては、Herb Sutterによる古い(2002)記事があります。http: //www.ddj.com/cpp/184401544この言語の機能がコンパイル時の安全性をもたらさず、最終的には結果として生じる理由について説明しています。結論として:

それで、これが私たちがコミュニティとして今日学んだ最高のアドバイスであると思われるものです:

道徳#1:例外仕様を書かないでください。

道徳#2:おそらく空のものを除いて、しかし私があなたならそれさえ避けたいでしょう。

于 2009-08-11T22:44:14.360 に答える
2

C ++11より前のC++は、スロー仕様を定義していました。この質問を参照してください。マイクロソフトのコンパイラがC++のスロー仕様を無視するのは、過去の私の経験です。「チェックされた例外」の概念全体は、特にJavaの領域では物議を醸しています。多くの開発者は、それを失敗した実験だと考えています。

于 2009-08-11T17:13:06.183 に答える
2

1と2両方使ってます。

1 について: ユーザー エラーを回避または防止することはできません。doxygen をパルプに適切に書き込めなかった開発者を知っていれば、その開発者を打ち負かすことができます。しかし、ユーザー エラーを回避または防止することはできないため、パラノイアは捨ててください。ユーザーがエラーを起こした場合、あなたではなく、ユーザーがそれを行いました。

2 について: C# には、未処理の例外をキャッチする方法が組み込まれています。だから「悪いこと」ではありませんが、においがすることには同意します。一貫性を欠いて実行するよりもクラッシュする方が良い場合もありますが、未処理の例外をログに記録してからクラッシュするようにしました。これにより、ログを送信してくれるので、スタック トレースを確認して問題を追跡できます。このようにして、各修正の後、クラッシュがますます少なくなります。

于 2009-08-11T16:53:39.920 に答える
1
  1. 関数が保証する例外安全性のレベルを文書化します。スティーブ・ジェソップが彼の答えで指摘したように、レベルの数があります。理想的には、それらがすべてのインターフェースについて文書化されているか、少なくとも必要な最小限の場合: a)決してスローしない、または b)スローする可能性がある

    Herb Sutterによって説明されたAbrahams の例外的な安全性の保証について読んでください。

  2. 大きなコードベースでそれが実用的であるかどうかは非常に疑わしい. スローから遠く離れていて、どうすればよいかわかりにくい場合は、例外を処理したい場所でのみキャッチし、それ以外の場合はバブルアップさせることをお勧めします。例外が発生したらすぐに例外をキャッチして消すのは得策ではありません。なぜなら、それは例外だからです。複雑なシステムでは、問題を追跡しやすくするため、ロギング メカニズムがあると便利です。

  3. やらないでください。Herb Sutter のA Pragmatic Look at Exception Specificationsと関連記事を読んでください。

于 2010-02-05T23:27:47.363 に答える
1

doxygen のドキュメントを使用して、メソッドを説明してください。それらを使用するときは、このドキュメントをチェックして、それらのパラメーターが何であるか、およびそれらがスローする例外を確認する必要があります。

例外がスローされた場合にコードを実行する単体テストを記述します。

于 2009-08-11T22:10:07.657 に答える
0

常に例外を予期して処理する必要があります。残念ながら、コンピューターがデューデリジェンスを行うための自動化された方法はありません。

于 2009-08-11T17:04:53.080 に答える
0

いいえ
。唯一の一般的なことは、何も投げないことを示すことです。
そして、例外が実際にメソッド/関数をエスケープできないことを手動で確認する必要があります。注: 例外がメソッドをエスケープした場合、アプリケーションは終了します。

#include <stdexcept>

class MyException: public std::exception
{
    public:
    ~MyException() throw() {}
     char* what() const throw()
     {
        try
        {
            // Do Stuff that may throw
            return "HI";
        }
        // Exceptions must not escape this method
        // So catch everything.
        catch(...)
        {
           return "Bad Stuff HAppening Cant Make Exception Message.";
        }
     }
};
于 2009-08-11T23:33:17.023 に答える
-1

関数がC++でスローできる例外を指定する方法があります。Stroustrupの本から抜粋した例は次のとおりです。

void f(int a) throw (x2, x3);

これは、fがタイプx2またはx3、あるいは派生タイプの例外のみをスローできることを指定します。この構文を使用すると、関数宣言を簡単に調べて、法的にスローが許可されている例外を確認し、それに応じてコーディングできます。

于 2009-08-12T00:59:58.273 に答える