COM-интерфейсы в некотором смысле похожи на интерфейсы JAVA - у них нет данных. Это означает, что наследование интерфейса отличается от наследования классов при использовании множественного наследования.
Для начала, рассмотрим невиртуальном наследования с ромбовидными узорами наследования ...
- Б наследует
- C наследует
- D наследует В и С
Экземпляр D содержит два отдельных экземпляра элементов данных A. Это означает, что когда указатель-a указывает на экземпляр D, он должен идентифицировать, какой экземпляр A внутри D он означает, - pointe r различается в каждом случае, а указатели-указатели не являются простыми релябельными типами - также меняется адрес.
Теперь рассмотрим тот же алмаз с виртуальным наследованием. Экземпляры 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-in-D или B-in-something-else. Компилятору требуется некоторый ключ времени выполнения (виртуальные таблицы), чтобы найти унаследованные-из-членов. В конечном итоге вам понадобится несколько указателей на несколько виртуальных таблиц в экземпляре D, поскольку theres vtable для унаследованного B и для унаследованного C и т. Д., Что подразумевает некоторые издержки памяти.
Одиночное наследование не имеет этих проблем. Макет памяти экземпляров остается простым, а виртуальные таблицы также проще. Вот почему Java запрещает множественное наследование для классов. В наследовании интерфейсов нет элементов данных, поэтому снова эти проблемы просто не возникают - нет проблемы с унаследованным-A-with-D, а также разными способами найти A-in-B в зависимости от того, что этот конкретный B оказывается, внутри. Оба COM и Java могут допускать множественное наследование интерфейсов без необходимости обработки этих осложнений.
EDIT
Я забыл сказать - без элементов данных, нет никакой разницы между виртуальным и не виртуальным наследованием. Однако с Visual C++ макет, вероятно, отличается, даже если нет элементов данных - с использованием одинаковых правил для каждого стиля наследования, независимо от того, присутствуют ли какие-либо элементы данных или нет.
Кроме того, макет памяти COM соответствует макету Visual C++ (для поддерживаемых типов наследования), поскольку он был разработан для этого. Нет причин, по которым COM не мог быть разработан для поддержки множественного и виртуального наследования «интерфейсов» с элементами данных. Microsoft могла бы спроектировать COM для поддержки той же модели наследования, что и C++, но не выбрала - и нет причин, по которым они должны были сделать иначе.
Ранний код COM часто записывался на языке C, что означает рукописные макеты структур, которые должны были точно соответствовать макету Visual C++ для работы. Макеты для множественного и виртуального наследования - ну, я бы не стал добровольно делать это вручную. Кроме того, COM всегда был своей собственностью, предназначенный для связи кода, написанного на разных языках. Это никогда не предназначалось для привязки к C++.
ЕЩЕ БОЛЕЕ РЕДАКТИРОВАНИЕ
Я понял, что пропустил ключевой момент.
В COM единственная проблема с макетами, которая имеет значение, - это виртуальная таблица, которая должна обрабатывать только метод отправки. Есть существенные различия в зависимости от компоновки, берете ли вы виртуальный или не виртуальный подход, похожий на расположение на объекте с членами данных ...
- Для невиртуального, Д vtab содержит A- внутри-B vtab и A-in-C vtab.
- Для виртуальных, A встречается только один раз внутри Ds vtable, но объект содержит несколько vtables, а указатели-указатели нуждаются в изменениях адреса.
С интерфейсом-наследованием, это в основном деталь реализации - есть только один набор реализаций методы для А.
В невиртуальном случае две копии виртуальной таблицы А будет идентичной (что приводит к реализации того же метода). Его немного большая виртуальная таблица, но накладные расходы на объект меньше, а указатели-указатели - это только переименование типа (без изменения адреса). Это более простая и эффективная реализация.
COM не может обнаружить виртуальный случай, потому что нет индикатора в объекте или vtable. Кроме того, нет смысла поддерживать обе конвенции, когда нет элементов данных. Он просто поддерживает одно простое соглашение.
Мне очень неудобно получить согласие на этот ответ - как я могу его отнять? - ответил я, размышляя, отредактировал это, и здесь есть что-то, что, вероятно, объясняет ключевой аспект того, почему это произошло в первые дни COM, но у меня только очень ограниченное и датированное понимание COM, и это ответ просто не является точным. В DCOM это просто не может быть правильным, и даже в дни, когда он был только-OLE2-back-end, реализация QueryInterface, вероятно, могла бы делать все, что ему нравится в принципе. – Steve314
"_Настоящее наследование не имеет этих проблем." Очевидно, что с одним только один общий vptr и vtable. «В наследовании интерфейса нет элементов данных, поэтому снова эти проблемы просто не возникают», «если« эти проблемы »относятся к вашему предыдущему описанию« В конечном итоге вам нужно несколько указателей на несколько виртуальных таблиц в экземпляре D »и« подразумевая некоторые издержки памяти ", то это, очевидно, неверно: MI интерфейсов подразумевает столько vptr и vtables, сколько унаследованных интерфейсов, на C++ или Java, если вы хотите сохранить эффективность во время выполнения виртуальных вызовов C++. – curiousguy
«Я забыл сказать - без данных, нет реальной разницы между виртуальным и не виртуальным наследованием.» «Абсолютно неверно. Я боюсь, вы просто не понимаете наследование в C++. «Однако, с Visual C++, макет, вероятно, отличается, даже если нет элементов данных». Макет, конечно же, не одинаковый для 'struct D: B' и для' struct D: virtual B'.Я не вижу, как они могут иметь подобный макет, не будучи очень неэффективным. – curiousguy