Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции / LECS17.DOC
Скачиваний:
44
Добавлен:
16.04.2013
Размер:
151.55 Кб
Скачать

Виртуальные функции

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

Обычная виртуальная функция должна представлять собой исполняемый код. При вызове семантика ее точно такая же, как и у остальных функций. В производном типе она может замещаться (переписываться, переопределяться), и прототип производной функции должен иметь сигнатуру и возвращаемый тип , совпадающие с базовой. Выбор того, какое определение функции вызвать для виртуальной функции, происходит динамически (на этапе выполнения). Типичный случай – это когда базовый класс включает виртуальную функцию, а производные классы имеют свои версии этой функции. Указатель на базовый класс может указывать либо на объект базового класса, либо на объект производного класса. Выбор функции-члена будет зависит от класса объекта, на который фактически (на момент выполнения) направлен указатель, а не от типа указателя. При отсутствии члена производного типа используется виртуальная функция базового класса. Обратите внимание на различие между выбором надлежащей замещенной виртуальной функции и выбором перегруженной функции-члена. Перегруженная функция-член выбирается на этапе компиляции на основе сигнатуры; перегруженные функции могут иметь различные возвращаемые типы. Виртуальная функция выбирается на этапе выполнения на основе типа объекта, который передается ей в качестве аргумента-указателя this. Кроме того, будучи однажды объявленной как виртуальная, функция сохраняет это свойство во всех переопределениях в производных классах. В производных классах не обязательно использовать модификатор virtual.

Рассмотрим следующий пример:

В файле virt_sel.Cpp

//Выбор виртуальной функции

class B {

public:

int i;

virtual void print_i() const

{ count << i << “ внутри B” << end1; }

};

class d : public:B

public^

//тоже виртуальная

void print_i() const

{ count << i<< “ внутри D” << end1; }

};

int main()

{

B b;

B* pb = &b; //указывает на объект В

Dd;

d.1 = 1 + (b.i = 1);

pb -> print_i(); //вызов В::print_i()

pb = &b; //указывает на объект D

pb -> print_i(); //вызов D::print_i()

}

Вот что выведет эта программа:

1 внутри В

2 внутри D

Сравните это поведение с программой student из раздела 1.2, «Преобразования типов и видимость», на стр. 284. Там выбор функции print() был основан на типе указателя, известном на этапе компиляции. Здесь же функция print_i() выбирается на основе того, на что направлен указатель. При этом выполняется другая версия print_i(). Говоря языком ООП, объекту посылается сообщение print_i(), и задействует собственную версию соответствующего метода. Так, тип базового указателя не определяет выбор метода (функции). Объекты другого класса обрабатываются другими функциями, устанавливаемыми на этапе выполнения. Средства, позволяющие реализовывать АТД, наследование, а также возможность динамической обработки объектов, являются неотъемлемой частью ООП.

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

Соседние файлы в папке Лекции