元の例外の再スローに関するC ++例外の質問


117

キャッチ内の次の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?
}

回答:


150

どちらの場合も、参照でキャッチするため、元の例外オブジェクトの状態を効果的に変更します(これは、次の巻き戻し中も有効な魔法のメモリロケーションにあると考えることができます- 0x98e7058以下の例で)。しかしながら、

  1. あなたが再スローするので最初のケースでは、throw;(とは違った、throw err;、で言った「魔法の場所」で、あなたの修正を加えて、オリジナルの例外オブジェクトを保持する0x98e7058します(アペンドへの呼び出しを反映)
  2. あなたが明示的に何かを投げるので、後者の場合は、コピーのは、errその後、別の「魔法の場所」で(新たに投げ作成される0x98e70b0すべてのコンパイラを知っているので- errのように、unwindedされようと、スタック上のオブジェクト可能性があり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

こちらもご覧ください:


24

この質問はかなり古く、質問された時間に適切な答えがあります。ただし、C ++ 11以降の適切な例外処理の方法に関するメモを追加したいと思います。これは、追加機能で達成しようとしていたことに非常によく対応していると思います。

使用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"

8

はい、再スローすると、参照によって変更した元の例外オブジェクトが再スローされます。また、基本クラス参照をキャッチして変更し、元の派生例外タイプをによって再スローすることもできthrow;ます。


1

はい、最初の質問です。

2番目に、Vladの回答を参照してください。コピートラクターを処理するには、例外オブジェクトを注意深く設計する必要があります。慣例により、基本クラスはその子を認識しないため、派生クラスが保持する追加データを失う可能性が最も高くなります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.