Разница в разметке памяти между вложенными классами и множественным наследованием в C++?

Я пытаюсь понять, как COM определяет расположение своих объектов, чтобы клиент, который хочет использовать объект COM, знал, как это сделать.

Я читал, что COM-объект, который реализует несколько интерфейсов, может делать это по-разному, в том числе используя вложенные классы или множественное наследование.

Насколько я понимаю, оба метода должны были бы создавать одну и ту же структуру памяти (в соответствии со спецификацией COM), чтобы клиент, который хочет использовать объект COM (например, в C), знал, как это сделать.

Поэтому мой конкретный вопрос: есть ли разница в разметке памяти для объектов C++, реализованных с использованием множественного наследования по сравнению с вложенными классами.

И кто-нибудь может указать мне, где указана компоновка COM-объекта?

4 ответа

Решение

COM абсолютно не зависит от расположения памяти вашего объекта. Все, что он хочет и нуждается, - это таблица указателей на функции, когда он вызывает IUnknown::QueryInterface(), Как вы реализуете это полностью зависит от вас. MFC использует вложенные классы, почти все, что использует встроенную поддержку множественного наследования в компиляторе C++. То, как компилятор MSVC++ реализует его, полностью совместимо с тем, что нужно COM. Это не случайно. Используйте стандартный код, который вы видите в книгах о COM, который показывает, как правильно реализовать IUnknown.

Единственная "компоновка", указанная в COM, - это vtable (таблица указателей виртуальных функций), связанная с каждым интерфейсом. Каждый интерфейс является производным от IUnknown, поэтому любой интерфейс объекта, на который имеет указатель клиент, может вызвать QueryInterface, чтобы получить другой интерфейс для того же объекта.

Для объектов нет обязательного макета. Действительно, вся идея объекта в COM сильно отличается от экземпляра класса в языке OO: единственный способ узнать, предоставляются ли два интерфейса одним и тем же объектом COM, - это вызвать QueryInterface для интерфейса IUnknown на обоих из них - если и только если они возвращают один и тот же указатель интерфейса, они являются интерфейсами одного и того же объекта.

Это довольно гибкая идея:

  • например, возможно иметь COM-объекты с загруженной в память только частью их внутреннего состояния: другие части их состояния могут быть лениво загружены / распределены, когда запрашиваются дополнительные интерфейсы.
  • Состояние COM-объекта может быть распределено по нескольким несмежным областям памяти.

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

Для одиночного наследования компилятор поместит определения родительского класса впереди, а затем дочерний класс. Это определяется стандартом для элементов данных, но опять-таки это не имеет значения, поскольку интерфейсы не имеют данных. Стандарт ничего не говорит о существовании или расположении vtables, но для того, чтобы полиморфизм работал, он должен быть изложен таким же образом - сначала родитель, потом второй.

Вы обнаружите удивительный факт, если реализуете несколько интерфейсов через множественное наследование. Когда вы приведете указатель на ваш объект класса из одного интерфейса в другой, адрес изменится! Это связано с тем, что разные интерфейсы (vtables) должны соответствовать объявлению интерфейса, поэтому должны быть разные макеты. Все эти макеты содержатся в одном и том же объекте, но компилятор выполняет манипуляции с указателями при приведении к нужному подмножеству.

Если в миксе присутствуют виртуальные функции, в частности, если самый производный класс добавляет какую-либо свою собственную, то расположение памяти в обоих подходах будет отличаться.

Другие вопросы по тегам