C ++ Питання про винятки щодо повторного скидання вихідного винятку


117

Чи спричинить наступне додаток () у запиті повторне виняток, щоб побачити ефект викликання додавання ()?

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було at 0xbfbce430, а не в "магічному розташуванні" at 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 , як ви можете отримати зворотний зв'язок за своїми винятками всередині свого коду без необхідності налагодження чи громіздкого журналу, просто написавши належний обробник винятків, який повторно скине вкладені винятки.

Оскільки ви можете це зробити з будь-яким похідним класом винятків, ви можете додати багато інформації до такого зворотного треку! Ви також можете поглянути на мій MWE на GitHub , де зворотній слід виглядатиме приблизно так:

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

для першого питання, так.

але по-друге, зверніться до відповіді Влада. Вам потрібно буде ретельно розробити об'єкт виключення для обробки копіювального механізму. за умовою базовий клас не розпізнає свою дитину, тому ви, швидше за все, втратите додаткові дані, що передаються похідним класом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.