Как работает реализация нескольких COM-интерфейсов в C++?
Я пытаюсь понять этот пример кода относительно объектов Browser Helper.
Внутри автор реализует один класс, который предоставляет несколько интерфейсов (IObjectWithSite, IDispatch).
Его функция QueryInterface выполняет следующее:
if(riid == IID_IUnknown) *ppv = static_cast<BHO*>(this);
else if(riid == IID_IObjectWithSite) *ppv = static_cast<IObjectWithSite*>(this);
else if (riid == IID_IDispatch) *ppv = static_cast<IDispatch*>(this);
Я узнал, что с точки зрения C указатели интерфейса - это всего лишь указатели на VTables. Поэтому я понимаю, что C++ способен возвращать VTable любого реализованного интерфейса, используя static_cast.
Означает ли это, что класс, созданный таким образом, имеет в памяти несколько VTables (IObjectWithSite, IDispatch и т. Д.)? Что делает C++ с конфликтами имен на разных интерфейсах (каждый из них имеет функции QueryInterface, AddRef и Release), могу ли я реализовать разные методы для каждого из них?
2 ответа
Да, существует несколько v-таблиц, по одной для каждого унаследованного интерфейса. Static_cast<> возвращает его. Компилятор обеспечивает совместное использование общих методов в унаследованных интерфейсах, заполняя каждый слот v-таблицы указателем на одну и ту же функцию. Таким образом, вам нужна только одна реализация AddRef, Release, QueryInterface. Как раз то, что вы хотите. Ничто из этого не является случайностью.
Это всегда проблема, когда в Coclass реализовано несколько интерфейсов с одним и тем же методом, который вы не хотите использовать в одной и той же реализации. Метод IConnectionPoint::Advise() является пресловутым примером. Или это был DAdvise()? К сожалению, я не помню, с чем это столкнулось и как это было решено, это было покрыто ATL Internals. Очень хорошая книга, кстати.
При множественном наследовании несколько таблиц VT располагаются в последовательности, подобной следующему формату, если дано this
указатель (который указывает на первый байт, 01)
[01] [02] [03] [04] [05] [06] [07] [08] [09] [10] [11] [12]
[Ptr VTableA][Ptr VTableB][Ptr VTableC]
В C++ только одна реализация будет сгенерирована для прототипа функции в сценарии с несколькими интерфейсами. Однако для обычного сценария наследования суперкласс может иметь предопределенную реализацию, и дочерние элементы, которые переопределяют функцию, будут иметь свои VTables, указывающие на содержимое, отличное от родительского.