Вопрос 23: Управление доступом к объектам в иерархии. Виртуальные функции. Абстрактные классы. Идентификаторы override и final.
Управление доступом к объектам в иерархии осуществляется при помощи оператор расширения области видимости :: задаёт область видимости, которой принадлежит некоторый член. Этот оператор имеет следующую форму записи.
имя::имя_члена
Здесь элемент имя задаёт имя класса или пространства имён, в котором содержится член, заданный элементом имячлена. Другими словами, имя определяет область видимости, внутри которой может находиться идентификатор, заданный элементом имячлена. При ссылке на глобальную область видимости элемент имя задавать не нужно. Например, чтобы обратиться к глобальной переменной с именем count, которая скрыта за локальной переменной с таким же именем count, необходимо использовать оператор :: следующем образом.
::count
Виртуальные функции
Виртуальные функции — специальный вид функций-членов класса. Виртуальная функция отличается об обычной функции тем, что для обычной функции связывание вызова функции с ее определением осуществляется на этапе компиляции. Для виртуальных функций это происходит во время выполнения программы. Для объявления виртуальной функции используется ключевое слово virtual. Функция-член класса может быть объявлена как виртуальная, если
- класс, содержащий виртуальную функцию, базовый в иерархии порождения;
- реализация функции зависит от класса и будет различной в каждом порожденном классе. Виртуальная функция — это функция, которая определяется в базовом классе, а любой порожденный класс может ее переопределить. Виртуальная функция вызывается только через указатель или ссылку на базовый класс. Определение того, какой экземпляр виртуальной функции вызывается по выражению вызова функции, зависит от класса объекта, адресуемого указателем или ссылкой, и осуществляется во время выполнения программы. Этот механизм называется динамическим (поздним) связыванием или разрешением типов во время выполнения. Указатель на базовый класс может указывать либо на объект базового класса, либо на объект порожденного класса. Выбор функции-члена зависит от того, на объект какого класса при выполнении программы указывает указатель, но не от типа указателя. При отсутствии члена порожденного класса по умолчанию используется виртуальная функция базового класса.
Абстрактные классы
Базовый класс иерархии типа обычно содержит ряд виртуальных функций, которые обеспечивают динамическую типизацию. Часто в самом базовом классе сами виртуальные функции фиктивны и имеют пустое тело. Определенное значение им придается лишь в порожденных классах. Такие функции называются чистыми виртуальными функциями.
Чистая виртуальная функция — это функция-член класса, тело которой не определено. В базовом классе такая функция записывается следующим образом:
virtual ПрототипФункции = 0;
Например
virtual void func() = 0;
Чистая виртуальные функции используются для того, чтобы отложить решение задачи о реализации функции на более поздний срок. В терминологии ООП это называется отсроченным методом. Класс, имеющий по крайней мере одну чистую виртуальную функцию, называется абстрактным классом. Для иерархии типа полезно иметь абстрактный базовый класс. Он содержит общие свойства иерархии типа, но каждый порожденный класс реализует эти свойства по-своему.
Индификаторы overried и final
Ключевое слово override можно использовать для обозначения функций-членов, переопределяющих виртуальную функцию в базовом классе, то есть мы явно указываем то, что мы реализуем виртуальную функцию базового класса в производном классе.
Ключевое слово final можно использовать для назначения виртуальных функций, которые невозможно переопределить в производном классе. Можно также использовать это ключевое слово для назначения классов, которые невозможно наследовать.
class Base
{
public:
virtual void doSomething(int x);
};
// ...
class Derived : public Base
{
public:
virtual void doSomething(long x) override;
};
`
Иными словами, компилятор, обнаружив override, проверяет существование метода с данной сигнатурой в базовом классе. Если же такого метода нет — выдает ошибку. Спецификатор final С++11 позволяет запрещать в классах-наследниках переопределение определенных методов. Достигается это за счет применения спецификатора final рядом с сигнатурой метода.
class Base
{
public:
virtual void doSomething(int x) final;
};
// ...
class Derived : public Base
{
public:
virtual void doSomething(int x); // ошибка!
};
Данный спецификатор также позволяет запрещать наследование от некоторого класса.
class Base final {};
class Derived : public Base {}; // ошибка!