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

Полиморфизм на основе виртуальных функций

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

В языке С++ программист может переопределить (переписать) функцию базового класса в дочернем классе. Например, в следующих строках кода показана эта возможность:

nopoly.cpp
#include <iostream>

struct A{
    A() {}
    void print() const { std::cout << "A"; }
};

struct B:public A{
    B(){}
    void print() const { std::cout << "B"; }
};

int main(){
     A* obj = new B;
     obj->print();
     obj = new A;
     obj->print();
     return 0; 
}

Однако, ожидаемый результат не будет достигнут - на экране в обоих случаях будет напечатана буква A, а не B. Это связано с тем, что компилятор не может определить, какого типа является объект, находящийся в переменной obj, поэтому вызывается функция родительского объекта. Данная проблема может быть решена при помощи виртуальных функций. В случае использования виртуальных функций компилятор гарантирует вызов своего варианта функции для каждого объекта класса из иерархии.

poly01.cpp
#include <сstdio>

struct A{
     A() {}
     virtual void print() const { puts("A"); }
};

struct B:public A{
     B(){}
     virtual void print() const { puts("B"); }
};

int main(){
     A* obj = new B;
     obj->print();
     obj = new A;
     obj->print();
     return 0;
}

В примере poly01.cpp мы получим желаемый результат: на экране будет напечатано "BA".

Данное свойство может быть использовано при реализации полиморфизма - возможности использования одной и той же функции (одного и того же имени функции) с различными типами параметров. Пример динамического полиморфизма с классами из показан в листинге poly02.cpp

poly02.cpp
#include <сstdio>

struct A{
     A() {}
     virtual void print() const { puts("A"); }
};

struct B:public A{
     B(){}
     virtual void print() const { puts("B"); }
};

void print(A* a){
     a->Print();
 }

int main(){
     A* obj = new B;
     print(obj);
     return 0;
 }

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

Виртуальные функции могут быть проинициализированы нулём. Такая функция называется чистой виртуальной функцией или абстрактной функцией. Если класс содержит хотя бы одну виртуальную функцию, то такой класс называется абстрактным. Использование абстрактных классов (в других объектно-ориентированных языках такие классы называются интерфейсами) связано с тем, что некоторые наборы классов должны обладать похожим поведением. Например, любой графический объект можно отображать на экране, но делается это для каждого объекта различным образом. Листинг abstract01.cpp демонстрирует пример определения абстрактного класса и его использования.

abstract01.cpp
struct Shape{
     virtual void Draw() = 0;
     virtual void Hide() = 0;
 };

struct Point: Shape{
     virtual void Draw(){
         // draw this point
     }
     virtual void Hide(){
         // hide this point
     }
 };

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

Last updated