Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект лекций ООПро.doc
Скачиваний:
43
Добавлен:
13.04.2015
Размер:
431.62 Кб
Скачать

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

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

class employee_v{

public:

virtual void print()const;

};

При этом определение виртуальной функции синтаксически такое же, как и у обычной невиртуальной функции-члена:

void employee_v::print()const{

cout<<"employee_v::print"<<endl;

}

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

class programmer_v:public employee_v{

public:

virtual void print()const;

};

void programmer_v::print()const{

cout<<"programmer_v::print"<<endl;

}

Вызов виртуальной функции синтаксически такой же, как и обычной функции:

employee_v r1;

programmer_v p1;

r1.print();//employee_v::print

p1.print();//programmer_v::print

В этих двух случаях вызовутся функцииprintсоответствующих классов. Так было бы и в случае невиртуальных функцийprint(т.е. без ключевого словаvirtual).

void print_v(employee_v* pr){

pr->print();//employee_v::printилиprogrammer_v::print

}

В этом случае мы вызываем функцию-член (print) по указателюprнаemployee_v. В случае, если бы функция-членprintбыла объявлена невиртуальной, то однозначно вызвалась бы функцияemployee_v::print(что определяется типом указателя). Однако, так как функцияprintвиртуальна, вызывается либоemployee_v::print, либоprogrammer_v::print, в зависимости от того, указывает prна объект типаemployee_vлибо на объект типаprogrammer_v. Например:

employee_v r1,r2;

programmer_v p1,p2;

print_v(&r1);// employee_v::print

print_v(&p1);// programmer_v::print

Теперь мы можем запомнить указатели на различные объекты в массиве и вызвать print_vв цикле:

employee_v *record[4]={&r1,&p1,&r2,&p2};

for(int i=0;i<4;++i){

print_v(record[i]);

}

Вызовутся функции в таком порядке: employee_v::print, programmer_v::print, employee_v::print, programmer_v::print.

Так как наша функция print_vвыродилась в единственный вызов функции-членаprint, мы можем написать непосредственно (что эквивалентно предыдущему примеру):

for(int i=0;i<4;++i){

record[i]->print();

}

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

Полиморфизм

Когда функции базового класса (employee_v) ведут себя «правильно» независимо от того, какой конкретно производный класс используется, это называетсяполиморфизмом. Тип, имеющий виртуальные функции, называетсяполиморфным типом. Для достижения полиморфного поведения вC++ вызываемые функции-члены должны быть виртуальными, и доступ к объекту должен осуществляться через ссылки или указатели. При непосредственных манипуляциях с объектом (без помощи указателя или ссылки) его точный тип известен компилятору, и поэтому полиморфизм времени выполнения не требуется.