Важный вынос
Это не имеет ничего общего с наследством или удобством. Сигнатуры функций являются результатом основ COM, позволяющих ему работать. Они требуются , чтобы быть набранными, как они есть. Если вам не хочется читать этот ответ, вот важные выводы: моральный эквивалент приведения в C ++ - это вызов QueryInterface
в COM. Единственный раз, когда вам разрешено использовать приведение C ++ в COM, - это реализация QueryInterface
.
деталей вашего COM-объекта * Подробности
Сигнатуры функций в COM, которые ожидают адрес void*
(в отличие от IUnknown*
) в качестве выходного параметра может возвращаться любой интерфейсный тип. Если бы это было изменено на IUnknown**
в гипотетической реализации, это сделало бы использование COM либо непрактичным, либо невозможным.
Давайте выполним 2 мысленных эксперимента, начиная с того, который сделает COM непрактичным дляиспользуйте:
Предположим, что CoCreateInstance должен был вернуть IUnknown*
вместо реального интерфейса, запрошенного через void*
. В этом случае клиент должен был бы немедленно вызвать QueryInterface
на возвращенном IUnknown*
, чтобы получить указатель интерфейса, который он запросил 1 . Это не практично 2 .
Это сразу приводит к невозможности решения эксперимента. Давайте предположим, что QueryInterface
вернул IUnknown*
. Чтобы добраться до реального указателя интерфейса, клиенту нужно будет вызвать QueryInterface
. Но это только возвращает IUnknown*
! В этот момент поверхность интерфейса расходуемого COM сворачивается в единый интерфейс IUnknown
.
Всякий раз, когда COM возвращает указатель на интерфейс, он должен возвращать указатель на конечный тип. Единственный тип языка программирования, который соответствует всем указателям на интерфейс, - это, действительно, void*
. 3 . Это должно объяснить, почему выходные параметры нужно вводить void**
, а не IUnknown**
.
. Для IObjectWithSite :: SetSite , с другой стороны, IUnknown*
является входом . Интерфейс по-прежнему принимает любой COM-интерфейс, но его необходимо передавать в качестве указателя на (сопоставимый по идентичности) интерфейс IUnknown
.
1 COM не требует конкретной реализации объекта для реализаций. Вместо этого он делегирует запросы указателей на интерфейсы соответствующим реализациям QueryInterface
.
2 Даже при игнорировании немедленной необходимости вызывать Release()
на IUnknown
для учета увеличенного количества ссылок как части вызова QI
.
3 С Модель объектов компонентов : «Единственное требование языка для COM - это то, что код генерируется на языке, который может создавать структуры указателей и, явно или неявно, вызывать функции через указатели». Умышленно не требуется, чтобы наследование интерфейса осуществлялось с использованием конструкций языкового уровня. Даже когда для реализации IUnknown
требуется IFoo
, нет никакой связи между IFoo*
и IUnknown*
в языке программирования, таком как C.