web-dev-qa-db-ja.com

元の例外の再スローに関する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?
}
103
WilliamKF

どちらの場合も、参照でキャッチするため、元の例外オブジェクトの状態を効果的に変更しています(これは その後の巻き戻し中に有効なままになる魔法のメモリの場所 - -下の例の0x98e7058)。しかしながら、

  1. 最初のケースでは、throw;で再スローするため(throw err;とは異なり、0x98e7058の「魔法の場所」に変更を加えて元の例外オブジェクトを保持します)willはappend()への呼び出しを反映します
  2. 2番目のケースでは、何かを明示的にスローするので、errの-​​copyが作成され、次に(別の「魔法の場所」0x98e70b0でスローされます。コンパイラは、err0xbfbce430の「魔法の場所」ではなく0x98e7058にあったように、eが解かれようとしているスタック上のオブジェクトである可能性があることを知っています。 )、そのため派生クラス固有のデータを失うことになります基本クラスインスタンスのコピー構築中。

何が起こっているかを説明する簡単なプログラム:

#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

参照:

139
vladr

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

つかいます - std::nested_exception および std::throw_with_nested

StackOverflow here および here で説明されており、どのようにすれば例外のバックトレースを取得できますかネストされた例外を再スローする適切な例外ハンドラーを記述するだけで、デバッガーや面倒なロギングを必要とせずにコード内で実行できます。

派生した例外クラスでこれを行うことができるため、このようなバックトレースに多くの情報を追加できます!また、私の 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"
14
GPMueller

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

8
Tronic

最初の質問については、はい。

しかし、2番目については、Vlad answerを参照してください。コピーctorを処理する例外オブジェクトを慎重に設計する必要があります。慣例により、基本クラスはその子を認識しないため、派生クラスによって運ばれる追加データを失う可能性が高くなります。

1
YeenFei