Должен ли я почти всегда использовать виртуальное наследование в C ++?

Из этой записи я вижу, что виртуальное наследование добавляет sizeof (указатель) к объекту объем памяти. Есть ли у меня какие-либо недостатки, просто используя виртуальное наследование по умолчанию, а обычное наследование только при необходимости? Похоже, что это приведет к более перспективному дизайну классов, но, возможно, я упускаю какую-то ловушку.


person SuperElectric    schedule 28.12.2010    source источник
comment
Для большинства людей порядок инициализации базовых классов становится неочевидным. Таким образом увеличивается стоимость обслуживания.   -  person Martin York    schedule 28.12.2010
comment
Виртуальное наследование добавляет внутренний указатель только в некоторых реализациях. Itanium ABI не использует внутренние указатели, только vptr.   -  person curiousguy    schedule 09.09.2015


Ответы (2)


Недостатки в том, что

  1. Все классы должны будут инициализировать все свои виртуальные базы все время (например, если A является виртуальной базой B, а C происходит от B, он также должен инициализировать сам A).
  2. Вы должны использовать более дорогие dynamic_cast везде, где вы используете static_cast (может быть, а может и не быть проблемой, в зависимости от вашей системы и того, требует ли это ваш дизайн).

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

person Alex B    schedule 28.12.2010
comment
О боже (1) ужасно. Спасибо! Не могли бы вы подробнее рассказать о (2)? Вы имеете в виду, что компилятор выдает ошибку, если я пытаюсь использовать static_cast ‹Derived *› (my_virtual_base_instance)? - person SuperElectric; 28.12.2010
comment
@SuperElectric: почему (1) ужасно? Было бы лучше, если бы базовый подобъект не инициализировался? ;) - person Yttrill; 29.12.2010
comment
Комментарии вроде того, что это не стоит того, показывают полное непонимание цели виртуальных баз: виртуальное наследование должно всегда использоваться, когда вы создаете подкласс абстракции. Нет никакого обходного пути или другой возможности, если вы не сможете его использовать, вы не сможете использовать несколько подклассов абстракций, не вернувшись назад и не исправив ошибку дизайна, тем самым нарушив инкапсуляцию. - person Yttrill; 29.12.2010
comment
@Yttril: Возможно, Алекс просто предполагает, что не стоит делать алмазное наследование классов с данными, чего, как предполагает ответ Грега, почти всегда можно избежать. Хотя, возможно, я что-то упускаю, и я открыто признаю, что полностью не понимаю, о чем вы говорите! (Слава богу, я учу себя шепелявить; я думаю, что C ++ может что-то сделать с личностью через некоторое время.) - person SuperElectric; 29.12.2010
comment
@Yttrill @SuperElectric Под этим не стоит я имел в виду попасть в ситуацию, когда вам понадобится множественное виртуальное наследование. К этому моменту ваш дизайн слишком сложен. Что касается пункта (2), он не будет компилироваться с static_cast. - person Alex B; 29.12.2010
comment
@AlexB Чепуха; виртуальное наследование не сложно, это очень естественно. - person curiousguy; 14.09.2015
comment
@AlexB Я не понимаю (1). Все классы не инициализируют всю свою базу все время (не виртуальная база)? - person Pedro Reis; 16.02.2017
comment
@AlexB Хорошо, это означает, что конструктор C должен явно инициализировать A (не только B). - person Pedro Reis; 16.02.2017
comment
@AlexB @Pedro Не могли бы вы подробнее рассказать, почему вы не можете скрыть свои виртуальные базы? В этом примере, хотя он и неэффективен, я думаю, что TA не знать о существовании Person, следовательно, следует принципу инкапсуляции? - person wlnirvana; 27.06.2018

По моему опыту, виртуальное наследование (в отличие от виртуальных методов) почти никогда не требуется. В C ++ он используется для решения «проблемы алмазного наследования», которая, если вы избегаете множественного наследования, на самом деле не может произойти.

Я почти уверен, что никогда не сталкивался с виртуальным наследованием вне книг по C ++, которое включает в себя как код, который я пишу, так и системы из миллиона строк, которые я поддерживаю.

person Greg Hewgill    schedule 28.12.2010
comment
Стандартная библиотека потоков ввода-вывода C ++ использует виртуальное наследование. Но да, в остальном это вообще довольно редко. - person Charles Salvia; 28.12.2010
comment
Грег: +1 отличное обобщение всего беспорядка виртуального наследования. - person ; 28.12.2010
comment
Хм ... Я уже знаю, для чего это нужно (думаю использовать), и понимаю, что это редко (я кодирую на C ++), но вопрос был задан каковы недостатки виртуального наследования. - person SuperElectric; 28.12.2010
comment
Другой возможный ответ заключается в том, что виртуальное наследование затрудняет чтение вашего кода. Тем людям, которые не часто сталкиваются с виртуальным наследованием, придется узнать о нем и приложить дополнительную когнитивную нагрузку, вспоминая, что оно означает, всякий раз, когда они читают ваш код. Те, кто действительно понимает это, могут удивиться, почему вы используете ненужную функцию, если ни один из ваших классов на самом деле не использует наследование алмаза. В этом случае применяется принцип YAGNI. - person Greg Hewgill; 28.12.2010