Вопросы об исключениях C ++ по повторному удалению исходного исключения

StackOverflow https://stackoverflow.com/questions/2360597

  •  23-09-2019
  •  | 
  •  

Вопрос

Приведет ли следующее append() в catch к тому, что исключение rethrown увидит эффект вызова 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?
}
Это было полезно?

Решение

В обоих случаях, поскольку вы перехватываете по ссылке, вы фактически изменяете состояние исходного объекта исключения (который вы можете считать находящимся в волшебное место памяти, которое останется действительным во время последующего раскручивания -- 0x98e7058 в примере ниже).Однако,

  1. В первом случае, поскольку вы повторно бросаете с помощью throw; (что, в отличие от throw err;, сохраняет исходный объект исключения с вашими изменениями в указанном «магическом месте» по адресу 0x98e7058) воля отразить вызов add()
  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

Также см:

Другие советы

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top