元の例外の再スローに関する C++ 例外の質問
質問
catch 内の次の append() は、呼び出された append() の効果を再スローされた例外に表示させますか?
try {
mayThrowMyErr();
} catch (myErr &err) {
err.append("Add to my message here");
throw; // Does the rethrow exception reflect the call to append()?
}
同様に、このように書き換えた場合、実際の例外が myErr によって派生された場合にビット スライスが発生しますか?
try {
mayThrowObjectDerivedFromMyErr();
} catch (myErr &err) {
err.append("Add to my message's base class here");
throw err; // Do I lose the derived class exception and only get myErr?
}
解決
どちらの場合も、参照によってキャッチするため、元の例外オブジェクトの状態を効果的に変更することになります (例外オブジェクトは、 その後の巻き戻し中も有効なままになる魔法の記憶場所 -- 0x98e7058
以下の例では)。しかし、
- 最初のケースでは、次のように再スローするので、
throw;
(それとは異なり、throw err;
, 、元の例外オブジェクトを変更して、前述の「魔法の場所」に保存します。0x98e7058
) 意思 append() の呼び出しを反映します。 - 2 番目のケースでは、明示的に何かをスローするため、 コピー の
err
作成され、新たに(別の「魔法の場所」に)スローされます0x98e70b0
-- なぜならコンパイラはすべてを知っているからですerr
スタック上の、巻き戻されようとしているオブジェクトである可能性があります。e
にいた0xbfbce430
, の「魔法の場所」ではありません。0x98e7058
)、 それで 派生クラス固有のデータが失われます 基本クラスのインスタンスのコピー構築中。
何が起こっているかを説明する簡単なプログラム:
#include <stdio.h>
struct MyErr {
MyErr() {
printf(" Base default constructor, this=%p\n", this);
}
MyErr(const MyErr& other) {
printf(" Base copy-constructor, this=%p from that=%p\n", this, &other);
}
virtual ~MyErr() {
printf(" Base destructor, this=%p\n", this);
}
};
struct MyErrDerived : public MyErr {
MyErrDerived() {
printf(" Derived default constructor, this=%p\n", this);
}
MyErrDerived(const MyErrDerived& other) {
printf(" Derived copy-constructor, this=%p from that=%p\n", this, &other);
}
virtual ~MyErrDerived() {
printf(" Derived destructor, this=%p\n", this);
}
};
int main() {
try {
try {
MyErrDerived e;
throw e;
} catch (MyErr& err) {
printf("A Inner catch, &err=%p\n", &err);
throw;
}
} catch (MyErr& err) {
printf("A Outer catch, &err=%p\n", &err);
}
printf("---\n");
try {
try {
MyErrDerived e;
throw e;
} catch (MyErr& err) {
printf("B Inner catch, &err=%p\n", &err);
throw err;
}
} catch (MyErr& err) {
printf("B Outer catch, &err=%p\n", &err);
}
return 0;
}
結果:
Base default constructor, this=0xbfbce430
Derived default constructor, this=0xbfbce430
Base default constructor, this=0x98e7058
Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
Derived destructor, this=0xbfbce430
Base destructor, this=0xbfbce430
A Inner catch, &err=0x98e7058
A Outer catch, &err=0x98e7058
Derived destructor, this=0x98e7058
Base destructor, this=0x98e7058
---
Base default constructor, this=0xbfbce430
Derived default constructor, this=0xbfbce430
Base default constructor, this=0x98e7058
Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
Derived destructor, this=0xbfbce430
Base destructor, this=0xbfbce430
B Inner catch, &err=0x98e7058
Base copy-constructor, this=0x98e70b0 from that=0x98e7058
Derived destructor, this=0x98e7058
Base destructor, this=0x98e7058
B Outer catch, &err=0x98e70b0
Base destructor, this=0x98e70b0
以下も参照してください。
他のヒント
この質問はかなり古いものであり、質問された当時にふさわしい答えがあります。ただし、適切な例外処理を行う方法についてメモを追加したいだけです。 C++11 これは、append 関数で達成しようとしていたことに非常によく対応していると思います。
使用 std::nested_exception
そして std::throw_with_nested
StackOverflowに記載されています ここ そして ここ, 、どうすればできるか 例外のバックトレースを取得する デバッガや面倒なロギングを必要とせずに、ネストされた例外を再スローする適切な例外ハンドラを記述するだけで、コード内で実行できます。
これは任意の派生例外クラスで実行できるため、このようなバックトレースに多くの情報を追加できます。私のものも見てみてください GitHub 上の MWE, ここで、バックトレースは次のようになります。
Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
はい、再スローはあなたが参照することによって変更した元の例外オブジェクトを、再スロー。あなたはまた、throw;
によって、元の派生例外タイプを再スローすることができ、まだそれにより、変更と、基本クラス参照をキャッチすることができます。
最初の質問のために、はいています。
が、第2のために、ヴラドの答えを参照してください。 あなたは慎重にハンドルコピーCTORにあなたの例外オブジェクトを設計する必要があります。あなたが最も可能性の高い派生クラスによって運ばれる追加データを失うことになるので、慣例により、基底クラスは、その子を認識しません。