Виртуальное наследование имеет значение только в том случае, если классы наследуются от Foo
.Если я определю следующее:
class B {};
class L : virtual public B {};
class R : virtual public B {};
class D : public L, public R {};
, тогда конечный объект будет содержать только одну копию B
, совместно используемую L
и R
.Без virtual
объект типа D
будет содержать две копии B
, одну в L
и одну в R
.
Существует некоторый аргумент, что все наследование должно быть виртуальным (потому что в случаях, когда оно имеет значение, это то, что вы хотите большую часть времени).Однако на практике виртуальное наследование является дорогостоящим и в большинстве случаев необязательным: в хорошо спроектированной системе большая часть наследования будет просто иметь конкретный класс, наследующий от одного или нескольких «интерфейсов»;такой конкретный класс обычно не предназначен для того, чтобы быть производным от самого себя, поэтому проблем нет.Но есть важные исключения: если, например, вы определяете интерфейс, а затем расширения для интерфейса, расширения должны наследоваться практически от базового интерфейса, поскольку конкретная реализация может захотеть реализовать несколько расширений.Или, если вы разрабатываете миксины, в которых определенные классы реализуют только часть интерфейса, а последний класс наследует от нескольких из этих классов (по одному на часть интерфейса).В конце концов, критерий относительно того, наследовать ли виртуально или нет, не слишком сложен:
, если наследование не является общедоступным, оно, вероятно, не должно быть виртуальным (ямы никогда не видели исключения), в противном случае
, если класс не предназначен для использования в качестве базового класса, виртуальное наследование не требуется, в противном случае
наследование должно быть виртуальным.
Есть несколько исключений, но вышеприведенные правила допускают ошибки на стороне безопасности;обычно «правильно» наследовать виртуально даже в тех случаях, когда виртуальное наследование не требуется.
Один последний момент: виртуальная база всегда должна инициализироваться самым производным классом, , а не класс, который напрямую наследует (и объявляет, что наследование является виртуальным).Однако на практике это не проблема.Если вы посмотрите на случаи, когда виртуальное наследование имеет смысл, это всегда случай наследования от интерфейса, который не будет содержать данных и, следовательно, будет иметь (только) конструктор по умолчанию.Если вы обнаружите, что наследуете практически от классов с конструкторами, которые принимают аргументы, пришло время задать несколько серьезных вопросов о дизайне.