例外スライス - これは生成されたコピー コンストラクターが原因ですか?
-
11-09-2019 - |
質問
例外のスライスによって引き起こされたコード内の非常に微妙なバグを修正したところです。次に、何が起こっているのかを正確に理解したいと考えています。
基本例外クラス、派生クラス、および関連関数を次に示します。
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」に変更することで)。何が起こっているのかを確認したかっただけです。
解決
、あなたは実際にはオブジェクトではなく、オリジナルのコピーを投げています。それについて考える - 元のオブジェクトがスタックにあるが、スタックが巻き戻さおよび無効化されている。
。私は、これが標準の一部であると信じて、私は参照するためのコピーを持っていません。
catchブロックでスローされた例外の種類は、キャッチの基本型ではなく、スローされたオブジェクトの種類です。この問題を回避する方法は、元のキャッチ例外がスローされますthrow;
ではなくthrow e;
することです。
他のヒント
<のhref = "http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/cplr153.htm" のrel =「noreferrer」>はい、あなたが必要とされるコピーコンストラクタを投げているし、公共でなければならないことを示唆しているを簡単にGoogle。 (あなたはどちらe
のコピーを初期化して、それを投げているように、理にかなっています。)
とにかく、ちょうどthrow
に巻き込まれたものを再スローし、例外オブジェクトを指定せずにcatch
を使用しています。その問題を解決するべきではないでしょうか。
catch(Exception& e)
{
printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
throw;
}
はい。
throw e;
の例外をスローします 静的 の種類 e
, 、何があっても e
実際にそうです。この場合、 Derived
例外はにコピーされます Exception
コピーコンストラクターを使用します。
この場合、次のようにすることができます
throw;
を得るために Derived
例外が正しくバブルアップします。
他のケースでのポリモーフィカルなスローに興味がある場合は、を参照してください。 いつもとても便利な C++ FAQ Lite.
C ++は私を驚かせたことがありません。私はたくさんのお金がこの行動だったものに賭けていた負けていただろう!
例外オブジェクトは、第1の一時的にコピーされ、あなたがthrow
を使用している必要があります。標準15.1 / 3を引用すると:
スロー発現は、一時的なオブジェクトを初期化し、例外オブジェクトと呼ばれるタイプは、スローのオペランドの静的タイプから任意のトップレベルのCV-修飾子を除去し、Tの配列」からタイプを調整することによって決定されます」または 『機能はそれぞれ、『Tを返す機能するTへのポインタ 『または』ポインタ』が』 Tを返す。
私は、これは非常に便利なコーディング標準ルールを生じさせると考えます:
例外階層の基本クラスは純粋仮想デストラクタを持っている必要があります。
または
例外階層のベース・クラスのコピーコンストラクタは保護されなければならない。
のどちらか、あなたはコピーコンストラクタを呼び出すことはできませんので、あなたが抽象クラスのインスタンスと第二を作成することはできません最初のケースであるため、「Eを投げる」しようとすると、コンパイラが警告するという目標を達成します。