Вопросы об исключениях в C ++ при отбрасывании исходного исключения - PullRequest
102 голосов
/ 02 марта 2010

Приведет ли следующая функция 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?
}

Ответы [ 4 ]

135 голосов
/ 02 марта 2010

В обоих случаях, так как вы перехватываете по ссылке, вы фактически изменяете состояние исходного объекта исключения (о котором вы можете думать, как о находящемся в месте магической памяти, которое останется действительным во время последующего раскручивания - 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

Также см .:

13 голосов
/ 03 декабря 2017

Этот вопрос довольно старый и имеет ответ, соответствующий времени, когда он был задан. Однако я просто хочу добавить примечание о том, как правильно обрабатывать исключения, начиная с 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 голосов
/ 02 марта 2010

Да, rethrowing перебрасывает исходный объект исключения, который вы изменили по ссылке. Вы также можете перехватить ссылку на базовый класс, изменить его и при этом иметь возможность перебрасывать исходный производный тип исключения на throw;.

1 голос
/ 02 марта 2010

за первый вопрос, да.

но для второго обратитесь к ответу Влада. вам нужно будет тщательно спроектировать объект исключения для обработки копии ctor. условно, базовый класс не распознает своего потомка, поэтому вы, скорее всего, потеряете дополнительные данные, переносимые производным классом.

...