Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП (5 семестр) / Лабораторная работа 3.doc
Скачиваний:
25
Добавлен:
05.03.2016
Размер:
73.73 Кб
Скачать

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

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

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

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

Обычную функцию-метод можно переопределить в наследуемых классах. Однако без атрибута virtual такая функция-метод будет связана с сообщением на этапе компиляции. Атрибут virtual гарантирует позднее связывание в пределах полиморфического кластера.

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

С++ поддерживает virtual функции-методы, которые объявлены в основном классе и переопределены в порожденном классе. Иерархия классов, определенная общим наследованием, создает связанный набор типов пользователя, на которые можно ссылаться с помощью указателя базового класса. При обращении к виртуальной функции через этот указатель в С++ выбирается соответствующее функциональное определение во время выполнения. Объект, на который указывается, должен содержать в себе информацию о типе, поскольку различия между ними может быть сделано динамически. Это особенность типична для ООП кода. Каждый объект “знает” как на него должны воздействовать. Эта форма полиморфизма называется чистым полиморфизмом.

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

Пример:

class Base

{

public:

void Method ()

{

cout << "Базовый класс\n";

}

};

class Derived : public Base

{};

// внутри main

Base b;

Derived d;

b.Method();

d.Method();

//-------- Вывод:

Базовый класс

Базовый класс

На экран будет выведено две строки Базовый класс. На этапе компиляции память выделяется для двух копий Method - для базового класса и для производного. Оба адреса привязываются к именам методов: Base::Method, Derived::Method. Т.е. когда в коде мы вызываем Method, то вызывается метод, соответствующий типу объекта. Чтобы увидеть, что для каждого объекта вызывается свой метод, давайте переопределим метод Derived::Method:

public:

void Method ()

{

cout << "Производный класс\n";

}

// внутри main

Base b;

Derived d;

b.Method();

d.Method();

//-------- Вывод:

Базовый класс

Производный класс

Следующий пример. Определения классов оставим без изменений. Поработаем с указателями:

Base* b = new Derived;

Derived* d = new Derived;

b->Method();

d->Method();

//-------- Вывод:

Базовый класс

Производный класс

Позднее/динамическое связывание

class Base

{

public:

virtual void Method ()

{

cout << "Базовый класс\n";

}

};

Вносить изменения в производные классы не нужно. Хотя можно и там добавить ключевое слово virtual (это не обязательно). Теперь посмотрим на наш код:

Base* b = new Derived;

Derived* d = new Derived;

b->Method();

d->Method();

//-------- Вывод:

Производный класс

Производный класс

Теперь вызывается метод того класса, на который на самом деле указывает указатель.

Несколько замечаний по виртуальным функциям:

  • Виртуальные функции используются только в классах. Поэтому часто используется название - виртуальные методы.

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

  • В массив нужно объединять только те объекты, которые обладают методами с одинаковыми названиями, но разной реализацией (переопределение!!!).

Рассмотрим простой код:

class Base

{

public:

virtual void vf ()

{

cout << "Базовый класс\n";

}

};

class Derived : public Base

{

public:

void vf () // это тоже виртуальная функция

{

cout << "Производный класс\n";

}

};

Функции Base::vf и Derived::vf являются виртуальными. Об этом говорит ключевое слово virtual в базовом классе. А производный класс наследует это свойство для своего метода.

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

class Ancestor

{

public:

virtual void function1 () { printf("Ancestor::function1"); }

};

class Descendant : public Ancestor

{

public:

virtual void function1 () {

printf("Descendant::function1");

Ancestor::function1(); // здесь будет напечатано "Ancestor::function1"

}

};

Для вызова конструктора предка нужно указать конструктор:

class Descendant : public Ancestor

{

public:

Descendant(): Ancestor();

};

class Base

{

public:

virtual void f(int x=1){...}

...

};

class Derived:public Base

{

public:

virtual void f(int x=2){...}

...

}

int main()

{

Derived d;

d.f(); //Будет вызван Derived::f(1); // Т.е. с аргументом по умолчанию заданным в Base!!!

}