Обработка исключений + полиморфизм, не работает, если метод исключения, не работает в том же классе - PullRequest
2 голосов
/ 23 февраля 2012

Я пытаюсь использовать некоторый код, подобный этому

//A.hpp
 class A{
   public:
       A() {} 
       virtual const char *message() const {return "A ERROR";}
 };

 //B.hpp

 #include "A.hpp"

 class B:public A {
   public:
       B() {}
       const char *message() const {return "B ERROR";}
 };

//main.cpp
 #include "A.hpp"
 #include "B.hpp"

void foo(const A& a) {
   /* case 1 */ throw a;   /* (or) */ /* case 2 */ throw B(); // LINE 100
}

int main() {

  B b;
  A &a(b);
  b.message(); // OUTPUT: B ERROR

  try {
      foo(a);
  } catch (const A& a) {
     std::cout<<"EXCEPTION CALLED "<<a.message()<<std::endl;
  }
  return 0;
}

, в этом случае, если я использую вариант 1: throw a;// a является ссылкой на B b;ВЫХОД: ОШИБКА

случай 2: бросок B ();// создает новый B;ВЫХОД: ОШИБКА B

Я не понимаю, почему между двумя случаями нет согласованности,

Если вы передаете по ссылке, все время должнысогласованность, если я создаю новую переменную внутри функции, которая вызывается в блоке try, то он вызывает правильный виртуальный метод, иначе это не так ... Кто-нибудь может сказать мне поток управления .... Пожалуйста,посоветуйте ....

Ответы [ 4 ]

5 голосов
/ 23 февраля 2012

Поскольку объект копируется перед броском.

Даже если параметр a из foo указывает на экземпляр B во время выполнения, важен тип выражения времени компиляции . Таким образом, фактически экземпляр B передается конструктору копирования A (который является законным, поскольку B наследует A), а новый экземпляр A создается и затем генерируется.

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

2 голосов
/ 23 февраля 2012

Для полноты картины я чувствую необходимость указать, что точно так же, как виртуальная копия (также известная как клонирование) решает копирование через базу, виртуальная (пере) броска решает бросание через базу:

struct base {
    virtual void
    rethrow() const
    { throw *this; }

    // it's also usual to make the base type abstract
    // so that users can't slice, e.g. boost::exception
    // it's also possible to make copying protected
};

struct derived: base {
    void
    rethrow() const override
    { throw *this; }
};

void
foo(base const& b)
{
    // no: slices
    // throw b;

    b.rethrow(); // Ok
}
1 голос
/ 23 февраля 2012

Броска копий переменных. Ваш throw a в foo на самом деле не выбрасывает ссылку на a из main, он фактически выбрасывает копию a. В заявлении catch вы получаете эту копию по ссылке. Поскольку в foo a является ссылкой на A, копия разрезает объект, и он становится A, теряя тем самым тот факт, что он когда-либо был B.

0 голосов
/ 23 февраля 2012

У меня небольшие затруднения с пониманием того, что вы спрашиваете, но ... Во-первых, не называйте переменную вашего улова тем же именем, что и ваша локальная переменная, то есть вы дважды используете «a» для представленияразные вещи.Вы можете подумать, что предмет, который вы ловите по ссылке, это a, который вы передали в foo, и это не будет правдой, если foo () выдает B. Просто для ясности измените ваш улов на

catch (const A& ex)
{
 ...ex.message()...
}

Я подозреваюв том случае, если объявлено вне попытки, a все еще находится в области видимости, а a.message () вызывает локальную переменную a .Если объявлено внутри try, a больше не находится в области видимости в улове, поэтому a , который вы перехватываете по ссылке, вызывает его сообщение.Изменение имени переменной улова должно устранить, казалось бы, неоднозначное поведение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...