원래 예외의 재발행에 대한 C ++ 예외 질문


117

catch의 다음 append ()로 인해 다시 throw 된 예외가 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) 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

참조 :


24

이 질문은 다소 오래되었으며 질문을받은 시간에 적절한 답변을 제공합니다. 그러나 C ++ 11 이후 적절한 예외 처리를 수행하는 방법에 대한 메모를 추가하고 싶습니다. 하고 싶습니다. 이것이 추가 기능으로 달성하려는 작업과 매우 잘 일치한다고 생각합니다.

사용 std::nested_exceptionstd::throw_with_nested

StackOverflow herehere 에서 예외에 대한 역 추적을 얻는 방법에 대해 설명합니다. 되어 있으며, 중첩 된 예외를 다시 발생시키는 적절한 예외 처리기를 작성하여 디버거 나 번거로운 로깅없이 코드 내부의 .

파생 된 예외 클래스를 사용하여이 작업을 수행 할 수 있으므로 이러한 역 추적에 많은 정보를 추가 할 수 있습니다! 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

첫 번째 질문입니다.

그러나 두 번째로 Vlad 답변을 참조하십시오. copy ctor를 처리하려면 예외 객체를 신중하게 디자인해야합니다. 규칙에 따라 기본 클래스는 자식을 인식하지 못하므로 파생 클래스가 전달하는 추가 데이터를 잃을 가능성이 높습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.