С++ для начинающих


Объекты-исключения и виртуальные функции


Если сгенерированный объект-исключение имеет тип производного класса, а обрабатывается catch-обработчиком для базового, то этот обработчик не может использовать особенности производного класса. Например, к функции-члену value(), которая объявлена в классе pushOnFull, нельзя обращаться в catch-обработчике Excp:

catch ( const Excp &eObj ) {

   // ошибка: в классе Excp нет функции-члена value()

   cerr << "попытка поместить значение " << eObj.value()

        << " в полный стек\n";

}

Но мы можем перепроектировать иерархию классов исключений и определить виртуальные функции, которые можно вызывать из catch-обработчика для базового класса Excp с целью получения доступа к функциям-членам более специализированного производного:

// новые определения классов, включающие виртуальные функции

class Excp {

public:

   virtual void print( string msg ) {

      cerr << "Произошло исключение"

           << endl;



   }

};

class stackExcp : public Excp { };

class pushOnFull : public stackExcp {

public:

   virtual void print() {

      cerr << "попытка поместить значение " << _value

           << " в полный стек\n";

   }

   // ...

};

Функцию print() теперь можно использовать в catch-обработчике следующим образом:

int main() {

   try {

      // iStack::push() возбуждает исключение pushOnFull

   } catch ( Excp eObj ) {

     eObj.print();    // хотим вызвать виртуальную функцию,

                      // но вызывается экземпляр из базового класса

   }

}

Хотя возбужденное исключение имеет тип pushOnFull, а функция print() виртуальна, инструкция eObj.print() печатает такую строку:

Произошло исключение

Вызываемая print() является членом базового класса Excp, а не замещает ее в производном. Но почему?

Вспомните, что объявление исключения в catch-обработчике ведет себя почти так же, так объявление параметра. Когда управление попадает в catch-обработчик, то, поскольку в нем объявлен объект, а не ссылка, eObj инициализируется копией подобъекта Excp базового класса объекта исключения. Поэтому eObj – это объект типа Excp, а не pushOnFull. Чтобы вызвать виртуальные функции из производных классов, в объявлении исключения должен быть указатель или ссылка:

int main() {

   try {

      // iStack::push() возбуждает исключение pushOnFull

   } catch ( const Excp &eObj ) {

     eObj.print();   // вызывается виртуальная функция

                      // pushOnFull::print()

   }

}

Объявление исключения в этом примере тоже относится к базовому классу Excp, но так как eObj – ссылка и при этом именует объект-исключение типа pushOnFull, то для нее можно вызывать виртуальные функции, определенные в классе pushOnFull. Когда catch-обработчик обращается к виртуальной функции print(), вызывается функция из производного класса, и программа печатает следующую строку:

попытка поместить значение 879 в полный стек

Таким образом, ссылка в объявлении исключения позволяет вызывать виртуальные функции, ассоциированные с классом объекта-исключения.



Содержание раздела