COM-интерфейсы в некотором роде похожи на JAVA-интерфейсы - в них нет элементов данных. Это означает, что при использовании множественного наследования наследование интерфейса отличается от наследования классов.
Для начала рассмотрим не виртуальное наследование с ромбовидными шаблонами наследования ...
- B наследует A
- C наследует A
- D наследует B и C
Экземпляр D содержит два отдельных экземпляра членов данных A. Это означает, что когда указатель на A указывает на экземпляр D, он должен определить, какой экземпляр A в D это означает - указатель отличается в каждом случае, и приведение указателей не является простым повторным обозначением типа - адрес тоже меняется.
Теперь рассмотрим тот же бриллиант с виртуальным наследованием. Все экземпляры B, C и D содержат один экземпляр A. Если вы думаете, что B и C имеют фиксированный макет (включая экземпляр A), это проблема. Если макет Bs [A, x] и макет Cs [A, y], то [B, C, z] недопустим для D - он будет содержать два экземпляра A. То, что вам нужно использовать, это что-то вроде [ A, B ', C', z], где B '- все от B, кроме унаследованного A и т. Д.
Это означает, что если у вас есть указатель на B, у вас нет единой схемы для разыменования членов, унаследованных от A. Поиск этих членов отличается в зависимости от того, указывает ли указатель на чистый B или B-внутри-D или B-внутри-что-то еще. Компилятору требуется некоторая подсказка времени выполнения (виртуальные таблицы), чтобы найти унаследованные члены от A. В конечном итоге вам понадобится несколько указателей на несколько виртуальных таблиц в экземпляре D, поскольку существует vtable для унаследованного B, унаследованного C и т. Д., Что подразумевает некоторые накладные расходы памяти.
Одиночное наследование не имеет этих проблем. Структура памяти экземпляров остается простой, а виртуальные таблицы также проще. Вот почему Java запрещает множественное наследование для классов. В наследовании интерфейса нет элементов данных, поэтому опять эти проблемы просто не возникают - нет проблемы, которая-унаследована-с-D, и нет разных способов найти A-в-B, в зависимости от того, что этот конкретный B бывает внутри. И COM, и Java могут разрешать множественное наследование интерфейсов, не справляясь с этими сложностями.
EDIT
Я забыл сказать - без элементов данных нет реальной разницы между виртуальным и не виртуальным наследованием. Однако в Visual C ++ компоновка, вероятно, отличается, даже если нет элементов данных - используются одинаковые правила для каждого стиля наследования последовательно, независимо от того, присутствуют ли какие-либо элементы данных.
Кроме того, макет памяти COM соответствует макету Visual-C ++ (для поддерживаемых типов наследования), поскольку он предназначен для этого. Нет причины, по которой COM не мог бы быть разработан для поддержки множественного и виртуального наследования «интерфейсов» с элементами данных. Microsoft могла бы разработать COM для поддержки той же модели наследования, что и C ++, но предпочла этого не делать - и нет никаких причин, почему они должны были поступить иначе.
Ранний COM-код часто был написан на C, что означало рукописные структурные макеты, которые должны были точно соответствовать макету Visual-C ++ для работы. Макеты для множественного и виртуального наследования - ну, я бы не стал делать это вручную. Кроме того, COM всегда был своим собственным средством, предназначенным для связи кода, написанного на разных языках. Он никогда не был предназначен для привязки к C ++.
ЕЩЕ БОЛЬШЕ РЕДАКТИРОВАНИЯ
Я понял, что упустил ключевой момент.
В COM единственной проблемой компоновки, которая имеет значение, является виртуальная таблица, которая должна обрабатывать только диспетчеризацию метода. Существуют значительные различия в компоновке в зависимости от того, используете ли вы виртуальный или не виртуальный подход, аналогично компоновке объекта с элементами данных ...
- Для не-виртуальных D-VAB содержит V-A-V-B и V-C-A-V.
- Для виртуального, A встречается только один раз в Ds vtable, но объект содержит несколько vtables, и приведение указателей требует изменения адреса.
С наследованием интерфейса, это в основном детали реализации - есть только один набор реализаций метода для A.
В не виртуальном случае две копии виртуальной таблицы A будут идентичны (что приведет к одинаковым реализациям методов). Это немного большая виртуальная таблица, но накладные расходы для каждого объекта меньше, а приведения указателя - просто перенос типа (без изменения адреса). Это более простая и эффективная реализация.
COM не может обнаружить виртуальный случай, потому что в объекте или vtable нет индикатора. Кроме того, нет смысла поддерживать оба соглашения, когда нет элементов данных. Он просто поддерживает одно простое соглашение.