Урок №169. Виртуальный базовый класс

  Юрий Ворон  | 

    | 

  Обновлено 26 Окт 2018  | 

 454

 ǀ   2 

В уроке о множественном наследовании, мы говорили о проблеме «алмаза смерти». В этом уроке мы продолжим эту тему.

Алмаз смерти

Код из того же урока, иллюстрирующий «алмаз смерти» (мы добавили еще конструкторы):

Хотя вы можете ожидать, что диаграмма наследования будет следующая:

На самом деле, это не так. Если вы создадите объект класса Copier, то получите две копии класса PoweredDevice: одну от Printer и одну от Scanner. Диаграмма получится следующая:

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

Результат:

PoweredDevice: 3
Scanner: 1
PoweredDevice: 3
Printer: 2

Как вы видите, PoweredDevice создается дважды. Иногда так и нужно, а иногда нужно, чтобы была одна копия PoweredDevice, общая как для Scanner, так и для Printer.

Виртуальные базовые классы

Чтобы сделать родительский (базовый) класс общим, используется ключевое слово «virtual» в строке объявления дочернего класса. Виртуальный базовый класс – это класс, объект которого является общим для использования всеми дочерними классами. Вот пример (без конструкторов для простоты) создания общего родительского класса:

Теперь, при создании класса Copier, мы получим только одну копию PoweredDevice, которая будет общей как для Scanner, так и для Printer.



Следует вопрос: «Если Scanner и Printer совместно используют родительский класс PoweredDevice, то кто ответственный за его создание?». Оказывается, Copier. Конструктор Copier отвечает за создание объекта PoweredDevice. Это один из тех случаев, когда дочернему классу разрешено вызывать конструктор родительского класса, который не является его непосредственным родителем. Следующий код:

Производит следующий результат:

PoweredDevice: 3
Scanner: 1
Printer: 2

Здесь уже PoweredDevice создается только один раз.

Обсудим несколько деталей.

Во-первых, виртуальные базовые классы всегда создаются перед не виртуальными базовыми классами, что обеспечивает построение всех базовых классов до построения их производных классов.

Во-вторых, конструкторы Scanner и Printer по-прежнему вызывают конструктор PoweredDevice. При создании объекта Copier эти вызовы конструктора просто игнорируются, так как именно Copier отвечает за создание PoweredDevice, а не Scanner или Printer. Однако, если бы мы создавали объекты Scanner или Printer, то эти конструкторы вызывались бы и применялись бы обычные правила наследования.

В-третьих, если класс наследует один или несколько классов, которые, в свою очередь, имеют виртуальные родительские классы, то наиболее дочерний класс отвечает за создание виртуального родительского класса. В программе выше Copier наследует Printer и Scanner, оба из которых имеют общий виртуальный родительский класс PoweredDevice. Copier, наиболее дочерний класс, отвечает за создание PoweredDevice. Это работает даже в случае одиночного наследования: когда Copier наследует только Printer, а Printer виртуально наследует PoweredDevice, то Copier по-прежнему ответственный за создание PoweredDevice.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (8 оценок, среднее: 5,00 из 5)
Загрузка...

Комментариев: 2

  1. Danila:

    Огромнейшее спасибо за перевод!
    Такой стиль изложения воспринимается гораздо проще, чем в учебнике.

    1. Юрий Ворон Юрий Ворон:

      Пожалуйста 🙂

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

ВОЛШЕБНАЯ ТАБЛЕТКА ПО С++