10

例外のスライスによって引き起こされた、コード内の非常に微妙なバグを修正したばかりで、何が起こっているのかを正確に理解したいと思っています。

基本例外クラス、派生クラス、および関連する関数は次のとおりです。

class Exception
{
public:
  // construction
  Exception(int code, const char* format="", ...);
  virtual ~Exception(void);

  <snip - get/set routines and print function>

protected:
private:
  int mCode;                // thrower sets this
  char mMessage[Exception::MessageLen]; // thrower says this FIXME: use String
};

class Derived : public Exception {
public:
  Derived (const char* throwerSays) : Exception(1, throwerSays) {};
};

void innercall {
  <do stuff>
  throw Derived("Bad things happened!");
}

void outercall {
  try {
    innercall();
  }
  catch(Exception& e)
  {
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
    throw e;
  }
}

バグはもちろん、outercall が Derived ではなく Exception をスローしてしまうことでした。私のバグは、Derived の失敗をキャッチしようとするコール スタックの試行回数が多いことが原因でした。

ここで、理解していることを確認したいだけです。「throw e」行で、デフォルトのコピー コンストラクターを使用して、新しい Exception オブジェクトが作成されていると思います。それは本当に起こっていることですか?

その場合、スローされるオブジェクトのコピー コンストラクターをロックアウトできますか? これが二度と起こらないことを本当に望んでおり、私たちのコードには Exception オブジェクトをコピーする理由がありません (私が知っていることです)。

独自の例外階層があるという事実についてはコメントしないでください。これは少し古い設計で、修正に取り組んでいます (順調に進んでいます。自家製の文字列クラスと、自家製のコンテナの多くを取り除きました)。

更新:明確にするために、質問をする前にバグを修正しました(「throw e」を「throw」に変更することにより)。何が起こっているかの確認を探していました。

4

4 に答える 4

22

オブジェクトを投げると、実際にはオリジナルではなくオブジェクトのコピーが投げられます。考えてみてください。元のオブジェクトはスタックにありますが、スタックは巻き戻されて無効化されています。

これは標準の一部だと思いますが、参照するコピーがありません。

catch ブロックでスローされる例外の型は、スローされたオブジェクトの型ではなく、catch の基本型です。この問題を回避する方法は、キャッチされた元の例外をスローするのではthrow;なく、を使用することです。throw e;

于 2009-07-07T22:43:18.613 に答える
10

簡単なグーグルは、はい、コピーコンストラクターをスローしていることを示唆しており、パブリックである必要があります。e(コピーを初期化してそれをスローしているので、これは理にかなっています。)

とにかく、throw例外オブジェクトを指定せずに使用して、 でキャッチされたものを再スローしcatchます。それは問題を解決するべきではありませんか?

  catch(Exception& e)
  {
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
    throw;
  }
于 2009-07-07T22:45:14.943 に答える
7

はい。

throw e;

実際の内容に関係なく、の静的型の例外をスローします。この場合、例外はコピー コンストラクターを使用して にコピーされます。eeDerivedException

この場合、あなたはただ

throw;

Derived例外バブルを正しく取得します。

他の場合の多態的なスローに興味がある場合は、常に非常に役立つ C++ FAQ Liteを参照してください。

于 2009-07-07T22:51:31.467 に答える
1

C++ は私をいつも驚かせてくれます。これがどんな行動に賭けていたとしても、私は多くのお金を失っていたでしょう!

例外オブジェクトは最初に一時オブジェクトにコピーされ、使用する必要がありthrowました。標準 15.1/3 を引用するには:

throw 式は、例外オブジェクトと呼ばれる一時オブジェクトを初期化します。その型は、throw のオペランドの静的型から最上位の cv 修飾子を削除し、「T の配列」または「」から型を調整することによって決定されます。 T を返す関数」を「T へのポインタ」または「T を返す関数へのポインタ」にそれぞれ変更します。

これにより、非常に有用なコーディング標準ルールが生まれていると思います。

例外階層の基本クラスには、純粋な仮想デストラクタが必要です。

また

例外階層の基底クラスのコピー コンストラクターは保護されます。

どちらも、「e をスロー」しようとするとコンパイラが警告するという目標を達成します。最初のケースでは抽象クラスのインスタンスを作成できず、2 番目のケースではコピー コンストラクタを呼び出せないためです。

于 2009-07-08T10:53:12.643 に答える