7

何年にもわたって C++ で科学的ソフトウェアのコーディングを行ってきましたが、いまだに例外に慣れていないようで、例外をいつ使用すればよいかわかりません。プログラムフローを制御するためにそれらを使用することは大したことではありませんが、それ以外の場合は...次の例を検討してください(イメージマスクを表し、ユーザーが領域をポリゴンとして追加できるようにするクラスからの抜粋):

class ImageMask
{
public:
    ImageMask() {}
    ImageMask(const Size2DI &imgSize);

    void addPolygon(const PolygonI &polygon);

protected:
    Size2DI imgSize_;
    std::vector<PolygonI> polygons_;
};

このクラスのデフォルトのコンストラクターは、画像サイズが定義されていない、役に立たないインスタンスを作成します。ユーザーがそのようなオブジェクトにポリゴンを追加できるようにしたくありません。しかし、私はその状況をどのように処理するかわかりません。サイズが未定義で、addPolygon() が呼び出された場合、次のことを行う必要があります。

  1. 黙って帰って、
  2. assert(imgSize_.valid) は、このクラスを使用してコードの違反を検出し、リリース前に修正します。
  3. 例外をスローしますか?

ほとんどの場合、私は 1) または 2) のいずれかを使用します (気分によって異なります)。なぜなら、このような単純なシナリオでは、例外はコストがかかり、面倒で、単純にやり過ぎのように思われるからです。いくつかの洞察をお願いします。

4

4 に答える 4

6

原則として、目的の操作を実行できない場合は例外をスローします。したがって、あなたの場合、はい、addPolygonが呼び出され、サイズが未定義または一貫性がない場合に例外をスローすることは理にかなっています。

黙って戻ることは、ほとんどの場合、間違ったことです。assertは適切なエラー処理手法ではありません(設計/文書化手法に近いものです)。

ただし、あなたの場合、エラー状態を不可能または起こりそうにないようにインターフェースを再設計する方が良いかもしれません。たとえば、次のようなものです。

class ImageMask
{
public:
    // Constructor requires collection of polygons and size.
    // Neither can be changed after construction.
    ImageMask(std::vector<PolygonI>& polygons, size_t size);
}

またはこのように

class ImageMask
{
public:
    class Builder
    {
    public:
        Builder();
        void addPolygon();
    };

    ImageMask(const Builder& builder);
}

// used like this
ImageMask::Builder builder;
builder.addPolygon(polyA);
builder.addPolygon(polyB);
ImageMask mask(builder);
于 2012-07-13T13:15:46.477 に答える
2

ある種の役に立たない状態のデータを作成する可能性がある状況は避けようとします。空でないポリゴンが必要な場合は、空のポリゴンを作成しないようにすると、コンパイラが空のポリゴンが存在しないように強制するため、多くの手間を省くことができます。

サイレントリターンはバグを隠し、バグの発見を必要以上に複雑にするため、サイレントリターンは決して使用しません。

ソフトウェアにバグがある場合にのみ、プログラムが存在できる状態にあることを検出したときにアサートを使用します。あなたの例では、Size2DI を取る c'tor をチェックインすると、このサイズが空でないことを確認すると、保存されているサイズが空でないかどうかをアサートするよりも、バグを検出するのに役立ちます。アサートには副作用があってはならず、ソフトウェアの動作を変更せずにアサートを削除できる必要があります。自分のバグを見つけたり、オブジェクト/関数の現在の状態などを文書化したりするのに非常に便利です。

実行時エラーが関数の呼び出し元によって直接処理される可能性が非常に高い場合は、従来の戻り値を使用します。コール スタックでの複数の関数呼び出しでこのエラー状況を伝える必要がある可能性が非常に高い場合は、例外を優先します。疑わしいのは、2つの機能を提供することです。

よろしくトルステン

于 2012-07-13T13:28:31.313 に答える
1

私にとって、1はオプションではありません。2か3かは、プログラム/ライブラリの設計、デフォルトを検討(および文書化)するかどうかによって異なります。イメージマスクを作成してから、ポリゴンをコンポーネントの有効または無効な使用法として追加します。これは重要な設計上の決定です。MatthewWilsonによるこの記事を読むことをお勧めします。

より多くのオプションがあることに注意してください:

  • 常にstd::terminateを呼び出し、追加のロギングを行う独自のアサーションを考案します
  • デフォルトのコンストラクターを無効にします(他の人がすでに指摘しているように)-これは私のお気に入りです
于 2012-07-13T13:22:10.223 に答える
1
  1. 「黙って帰れ」――それこそが本当の「大ダメ」。プログラムは、何が問題なのかを認識している必要があります。
  2. 「アサート」 - 2 番目のルールは、通常のプログラムの流れを復元できなかった場合にのみ使用してアサートすることです。
  3. 「例外をスローする」 - はい、この正しくて優れた手法です。例外安全性に注意してください。GotWには、例外セーフ コーディングに関する記事が多数あります。

例外を恐れないでください。彼らは噛みません。:) このテクニックを十分に習得すれば、強力なコーダーになります。;)

于 2012-07-13T13:30:36.227 に答える