Виртуальные методы и механизм позднего связывания 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Виртуальные методы и механизм позднего связывания



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

Monstr *p;

p = new daemon;

Вызов методов объекта производится в соответствии с типом указателя, а не фактическим типом объекта, на который он ссылается.

Пример

p - > draw (1, 1, 1, 1);

// вызывается метод класса monstr, а не класса daemon. Этот процесс называется ранним связыванием и выполняется на этапе компоновки программы. Для того чтобы вызвать метод класса daemon, можно использовать явное преобразование типа указателя:

((daemon *p)) - > draw (1, 1, 1, 1);

Это не всегда возможно, поскольку указатель в разное время может ссылаться на разные объекты разных классов иерархии.

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

Для определения виртуального метода используется спецификатор virual:

virtual void draw (int x, int y, int scale, int position);

 

Правила описания и использования виртуальных методов:

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

- виртуальные методы наследуются;

- если виртуальный метод переопределен в производном классе, объекты этого класса могут получить доступ к методу базового класса с помощью операции доступа к области видимости;

- виртуальный метод не может объявляться с модификатором static, но может быть объявлен как дружественный;

- если в классе вводится описание виртуального метода, он должен быть определен хотя бы как чисто виртуальный (см. далее “Абстрактные классы”);

Чисто виртуальный метод содержит признак = 0 вместо тела, например:

virtual void f (int) = 0;

Если определить метод draw в классе как виртуальный, решение о том, метод какого класса вызывать, решается в зависимости от типа объекта, на который ссылается указатель:

Monstr *r, *p;

r = new monstr;

p = new daemon;

r - > draw (1, 1, 1, 1); // вызывается метод класса monstr

p - > draw (1, 1, 1, 1) // вызывается метод класса daemon

p - > monstr:: draw (1, 1, 1, 1); // обход механизма виртуальных методов

Если объект класса daemon будет вызывать метод draw не непосредственно, а косвенно (т.е. из другого метода, определенного в классе monstr), будет вызван метод draw класса daemon.

 

Абстрактные классы

Класс, содержащий хотя бы один чисто виртуальный метод, называется абстрактным. Абстрактные классы предназначены для представления общих понятий, которые предполагается конкретизировать в производных классах. Абстрактный класс может использоваться только в качестве базового класса для других классов – нельзя создавать объекты абстрактного класса.

 

Обработка исключительных ситуаций

 

Исключительная ситуация, или исключение, – это возникновение непредвиденного или аварийного события, которое может порождаться некорректным использованием аппаратуры. Например, это деление на ноль или обращение по несуществующему адресу памяти. Обычно эти события приводят к завершению программы с системным сообщением об ошибке. С++ дает программисту возможность восстановить программу и продолжить ее выполнение.

Исключения С++ не поддерживают обработку асинхронных событий, таких как ошибки оборудования или обработку прерываний, например нажатие <Crtl> +<C>. Механизм исключений предназначен только для событий, которые происходят в результате работы самой программы и указываются явным образом.

Исключения позволяют логически разделить вычислительный процесс на две части – обнаружение аварийной ситуации и ее обработку.

 

Синтаксис исключений

Место, в котором может произойти ошибка, должно входить в контролируемый блок – составной оператор, перед которым записано ключевое слово try:

try {

}

Обработка исключения начинается с появления ошибки. Функция, в которой она возникла, генерирует исключение. Для этого используется ключевое слово throw с параметром, определяющим вид исключения. Параметр может быть константой, переменной или объектом и используется для передачи информации об исключении его обработчику:

throw [выражение];

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

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

Обработчики исключений начинаются со слова catch, за которым в скобках следует тип обрабатываемого исключения. Они должны располагаться непосредственно за try-блоком. Можно записывать один или несколько обработчиков в соответствии с типами обрабатываемых исключений. Синтаксис обработчиков имеет три формы:

Catch (тип имя) {/*тело обработчика */}

Catch (тип) {/*тело обработчика */}

Catch (…) {/*тело обработчика */}

Первая форма используется, когда имя параметра используется в теле обработчика для выполнения каких-либо действий (например, вывод информации об исключении). Вторая форма не предполагает использования информации об исключении. Многоточие вместо параметра обозначает, что обработчик перехватывает все исключения. Обработчик в этой форме следует помещать после всех остальных.

Пример

catch (int i) {

}

catch (const *char) {

}

catch (Overflow) {

// обработка исключений класса Overflow

}

catch (j) {

// обработка всех необслуженных исключений

}

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

 

Перехват исключений

Когда с помощью throw генерируется исключение, функции исполнительной библиотеки С++ выполняют следующие действия:

1) создают копию параметра throw в виде статического объекта, который существует, пока исключение не будет обработано;

2) в поисках подходящего обработчика раскручивает стек, вызывая деструкторы локальных объектов, выходящих из области действия;

3) передают объект и управление обработчику, имеющему параметр, совместимый по типу с этим объектом.

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

Обработчик считается найденным, если тип объекта, указанного после throw:

- тот же, что и указанный в параметре catch;

- является указателем, который может быть преобразован по стандартным правилам преобразования указателей в тип указателя в параметре cacth.

Из вышеизложенного следует, что обработчики производных классов следует размещать до обработчиков базовых, иначе им никогда не будет передано управление. Обработчик указателя типа void следует размещать после обработчиков указателей конкретного типа.

Пример

# include <fstream.h>

class Hello { // Класс, информирующий о своем создании и уничтожении

public:

Hello () {cout << “Hello!” << endl; }

~Hello () {cout << “Bye!” << endl; }

}

void f1 () {

ifstream ifs (“\\INVALD\FILE\NAME”); // открываем файл

if (!ifs) {

cout << “Генерируем исключение” << endl;

throw “Ошибка при открытии файла”; }

}

void f2 () {

Hello H; // создаем локальный объект

f1 (); // вызываем функцию, генерирующую исключение

}

int main () {

try {

cout << “Входит в try-блок” << endl;

}

catch (int i) {

cout << “Вызван обрабочик int, исключение – ” << I << endl;

return -1;

}

catch (const char *p) {

cout << “Вызван обработчик const char *, исключение - ” << p << endl;

return -1;

}

catch (j) {

cout << “Вызван обработчик всех исключений” << endl;

return -1;

}

return 0;

}

 

Результат выполнения программы:

Входим в try-блок

Hello!

Генерируем исключение

Bye!

Вызван обработчик const char *, исключение – Ошибка при открытии файла

!!! Обратите внимание, что после порождения исключения был вызван деструктор локального объекта, хотя управление из функции f1 было передано обработчику, находящемуся в функции main.

 

Список исключений

В заголовке функции можно задать список исключений, которые она может прямо или косвенно порождать.

Типы исключений перечисляются в скобках через запятую после ключевого слова throw, расположенного за списком параметров функции, например:

void f1 () throw (int, const char*) {/*Тело функции */}

void f2 () throw (Oops*) {/* */} // Oops* - указатель на класс Oops

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

void f () throw () {

}

Указание списка исключений ни к чему не обязывает – функция может породить исключение, которое она обещала не использовать. Эта ситуация обнаруживается во время исполнения программы и приводит к вызову стандартной функции unexpected, которая по умолчанию вызывает функцию terminate. Функция terminate по умолчанию вызывает функцию abort, которая зварешает выполнение программы.

 

Иерархии исключений

Исключения могут быть как стандартного, так и определенного пользователем типа. Использование собственных классов исключений предпочтительнее применения стандартных типов данных. С помощью классов можно более гибко организовать передачу информации об исключении, легче дифференцировать обработку исключений, а кроме того, появляется возможность использовать иерархии классов.

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

Существует ряд стандартных исключений, которые генерируются операциями или функциями С++. Все они являются производными от библиотечного класса exception, описанного в заголовочном файле <stdexcept>. Например, операция new при неудачном выделении памяти генерирует исключение bad_alloc.

Можно определять собственные исключения, производные от стандартных.

 

Задание

Добавьте в описание “функционального” калькулятора обработку исключительных ситуаций (недопустимый тип данных, операнд, являющийся делителем в операции деления, равен нулю, аргумент логарифма отрицателен или равен нулю и т. д.).

 


Библиографический список

 

 

1. Павловская Т.А. С / C++. Программирование на языке высокого уровня

/ Т.А. Павловская. СПб, 2002.

2. Павловская Т.А. С / C++. Структурное программирование: практикум

/ Т.А.Павловская, Ю.А. Щупак. СПб, 2002.

3. Павловская Т.А. С / C++. Объектно-ориентированное программирование: практикум / Т.А.Павловская, Ю.А.Щупак СПб, 2002.

4. Культин Н.Б. С++ Builder./ Н.Б.Культин. СПб, 2004.


ОГЛАВЛЕНИЕ

ВВЕДЕНИЕ. 2

Классы.. 4

Описание класса. 4

Описание объектов. 6

Указатель this. 7

Конструкторы.. 8

Конструктор копирования. 11

Статические элементы класса. 12

Статические поля. 12

Статические методы.. 13

Дружественные функции и классы.. 14

Дружественные функции. 14

Дружественный класс. 15

Деструкторы.. 16

Перегрузка унарных операций. 18

Перегрузка бинарных операций. 20

Перегрузка операции присваивания. 20

Перегрузка операции приведения типа. 21

Наследование. 22

Ключи доступа. 22

Простое наследование. 23

Правила наследования методов. 27

Виртуальные методы и механизм позднего связывания. 28

Абстрактные классы.. 30

Обработка исключительных ситуаций.. 30

Синтаксис исключений. 31

Перехват исключений. 32

Список исключений. 35

Иерархии исключений. 35

Библиографический список.. 37

 


 

 

ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ С++



Поделиться:


Последнее изменение этой страницы: 2016-08-15; просмотров: 430; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.16.69.143 (0.048 с.)