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

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

class Base {
public:
    void hi() { cout << "Hi, I'm base class" << endl; }
};

class Derived : public Base {
public:
    void hi() { cout << "Hi, I'm derived class" << endl; }
};

// ...

Derived d;
Base b = d;
d.hi(); // "Hi, I'm derived class"
b.hi(); // "Hi, I'm base class"

Наша из экземпляра наследника вызвать переопределенную функцию. Достигается это посредством определения виртуальных функций (virtual functions).

По умолчанию объектная модель C++ работает с невиртуальными функциями.

Чтобы сделать функцию виртуальной необходимо добавить ключевое слово virtual и она станет виратульной для всех наследниках на всех уровнях.

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

class Base {
public:
    virtual void hi() { cout << "Hi, I'm base class" << endl; }
};

class Derived : public Base {
public:
    void hi() override { cout << "Hi, I'm derived class" << endl; }
};

// ...

Derived d;
Base& b = d; // нужна именно ссылка
d.hi(); // "Hi, I'm derived class"
b.hi(); // "Hi, I'm derived class"

Чистые виртуальные функции

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

virtual void foo() = 0;

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

#include <iostream>
using namespace std;

class A {
public:
   virtual string name() = 0;
};

class B: public A {
public:
   string name() {return "B";}
};

class C: public A {
public:
   string name() {return "C";}
};

void hi(A& a) {
   cout << "Hi " << a.name() << "!" << endl;
}

int main()
{
   B b;
   C c;
   hi(b);
   hi(c);
   return 0;
}