Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции ВМИиП.docx
Скачиваний:
8
Добавлен:
18.04.2019
Размер:
233 Кб
Скачать
☆

1.3.8. Шаблоны.

В языке С функции, реализация которых не зависит от типа данных называют шаблонами или родовыми функциями.

Отдельной строкой следует выделить шаблоны (template) или родовые классы и функции, реализация которых логически не зависит от типа данных.

Пример программы обмена значениями между переменными

Inline void swap (char &x, char &y)

{ char t=x; x=y; y=t; } //Использование Inline необязательно

Inline void swap (double &x, double &y)

{ double t=x; x=y; y=t; }

Если отвлечься от типа данных, то можно ввести следующее макроопределение:

#define SWAP(T) Inline void swap (T &x, T &y) { T r=x; x=y; y=r; }

Тогда, если в строке С-программы встретится вызов функции вида

SWAP(float);

Она будет проинтерпретирована препроцессором следующим образом

Inline void swap (float &x, float &y) { float r=x; x=y; y=r; }

1.3.9. Виртуальные (virtual) функции

Для иллюстрации применения виртуальных методов рассмотрим пример на языке С++.

#include <iostream.h> // подключение стандартной библиотек С++, в

// которой описаны некоторый функции, применяемые в программе

class vehicle // класс "транспортное средство"

{

int wheels;

float weight;

public: // начало публичного(открытого) раздела класса

virtual void message(void) {cout << "Транспортное средство\n";}

// описание виртуальной функции message класса vehicle и реализация этой функции. При вызове функции message класса vehicle на экран монитора будет выведена строка "Транспортное средство"

};

class car : public vehicle // класс "легковая машина", унаследованный из класса "транспортное средство"

{

int passenger_load;

public: // начало публичного(открытого) раздела класса

void message(void) {cout << "Легковая машина\n";}

// описание виртуальной функции message класса car и реализация этой функции. При вызове функции message класса car на экран монитора будет выведена строка " Легковая машина "

};

class truck : public vehicle // класс "грузовая машина", унаследованный из класса "транспортное средство"

{

int passenger_load;

float payload;

public: // начало публичного(открытого) раздела класса

int passengers(void) {return passenger_load;}

};

class boat : public vehicle // класс "лодка", унаследованный из класса "транспортное средство"

{

int passenger_load;

public: // начало публичного(открытого) раздела класса

int passengers(void) {return passenger_load;}

void message(void) {cout << "Лодка\n";}

// описание виртуальной функции message класса boat и реализация этой функции. При вызове функции message класса boat на экран монитора будет выведена строка "Лодка"

};

void main() // основной исполняемый блок программы

{

vehicle *unicycle; // описываем переменной unicycle как

// указатель на объект класса vehicle

unicycle = new vehicle; // Создаем объект класса vehicle, указатель unicycle указывает на этот объект

unicycle->message(); // вызываем метод message объекта

delete unicycle; // удаляем объект unicycle

// Все последующие блоки по 3 строки абсолютно идентичны первому блоку с той лишь разницей, что изменяется класс создаваемого объекта

// на car, truck, boat

unicycle = new car;

unicycle->message();

delete unicycle;

unicycle = new truck;

unicycle->message();

delete unicycle;

unicycle = new boat;

unicycle->message();

delete unicycle;

}

Результаты работы программы(вывод на экран):

Транспортное средство

Легковая машина

Транспортное средство

Лодка

Рассмотрим приведенный пример. У нас есть три класса car, truck и boat, которые являются производными от базового класса vehicle. В базовом классе vehicle описана виртуальная функция message. В двух из трех классов(car, boat) также описаны свои функции message, а в классе truck нет описания своей функции message. Все строки, к которым я не приводил комментарии, не имеют принципиального для данного примера значения. Теперь пробежимся по основному блоку программы - функции main(). Описываем переменную unicycle, как указатель на объект типа vehicle. Не буду вдаваться в подробности, почему именно указатель на объект. Так надо. В данном случае воспринимайте работу с указателем, как с самим объектом. Подробности работы с указателями можно найти в описаниях конкретного языка ООП. Затем, создаем объект класса vehicle, переменная unicycle указывает на этот объект. После этого вызываем метод message объекта unicycle, а в следующей строке удаляем этот объект. В следующих трех блоках по 3 строки проводим аналогичные операции, с той лишь разницей, что работаем с объектами классов car, truck, boat. Применение указателя позволяет нам использовать один и этот же указатель для всех производных классов. Нас интересует вызов функции message для каждого из объектов. Если бы мы не указали, что функция message класса vehicle является виртуальной(virtual), то компилятор статически(жестко) связал бы любой вызов метода объекта указателя unicycle с методом message класса vehicle, т.к. при описании мы сказали, что переменная unicycle указывает на объект класса vehicle. Т.е. произвели бы раннее связывание. Результатом работы такой программы был бы вывод четырех строк "Транспортное средство". Но за счет применения виртуальной функции в классе мы получили несколько другие результаты.

При работе с объектами классов car и boat вызываются их собственные методы message, что и подтверждается выводом на экран соответствующих сообщений. У класса truck нет своего метода message, по этой причине производится вызов соответствующего метода базового класса vehicle.

_____________________________________________________________________________

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

Скорость и эффективность работы программы выше при раннем связывании, чем при позднем. Позднее связывание придает программе определенную универсальность.

Цели виртуальных функций:

  1. Виртуальные методы существуют для того, чтобы наследник вел себя отлично от предка, сохраняя совместимость с ним.

  2. Виртуальные методы применяются для того, чтобы 2 и более наследника имели один общий класс.

  3. Применение виртуальных методов используют механизм позднего связывания, что обеспечивает обработку объектов, тип данных которых не известен во время компиляции.

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

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

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

Класс, который содержит виртуальные методы, называется полиморфным классом. Опять принцип полиморфизма >.<

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