Сущность технологии СОМ. Библиотека программиста | страница 129



// IBoat methods – методы

IBoat STDMETHODIMP Sink(void);

// IPlahe methods – методы

IPlane STDMETHODIMP TakeOff(void); };


Ниже приведена стандартная реализация QueryInterface в CarBoatPlane:


STDMETHODIMP QueryInterface(REFIID riid, void **ppv)

{

if (riid == IID_IUnknown) *ppv = static_cast(this);

else if (riid == IID_IVehicle) *ppv = static_cast(this);

else if (riid == IID_ICar) *ppv = static_cast(this);

else if (riid == IID_IBoat) *ppv = static_cast(this);

else if (riid == IID_IPlane) *ppv = static_cast(this);

else return (*ppv = 0), E_NOINTERFACE;

((IUnknown*)*ppv)->AddRef();

return S_OK;

}


Для того чтобы быть объектом СОМ, реализация CarBoatPlane QueryInterface должна полностью придерживаться правил IUnknown , приведенных в данной главе.

Класс CarBoatPlane выставляет интерфейсы только типа ICarIPlane, IBoat, IVehicle и IUnknown . Каждая таблица vtbl CarBoatPlane будет ссылаться на единственную реализацию QueryInterface, показанную выше. К каждому поддерживаемому интерфейсу можно обращаться через эту реализацию QueryInterface, так что невозможно найти два несимметричных интерфейса, то есть не существует двух интерфейсов A и B, для которых неверно следующее:

If QI(A)->B Then QI(QI(A)->B)->A

Если следовать той же логике, то поскольку все пять интерфейсов принадлежат к одной и той же реализации QueryInterface, не существует трех интерфейсов А, В и С , для которых неверно следующее:

If QI(QI(A)->B)->C Then QI(A)->C

Наконец, поскольку реализация QueryInterface всегда удовлетворяет запросы на пять возможных интерфейсных указателей, которые могут поддерживаться клиентом, то следующее утверждение должно быть верным для каждого из пяти поддерживаемых интерфейсов:

QI(A)->A

Поскольку из множественного наследования вытекает единственная реализация QueryInterface для всех интерфейсов объекта, в действительности очень трудно нарушить требования симметричности, транзитивности и рефлективности.

Реализация также корректно выполняет правило СОМ об идентификации, возвращая только одно значение указателя при запросе IUnknown:


if (riid == IID_IUnknown) *ppv = static_cast(this);


Если бы реализация QueryInterface возвращала различные указатели vptr для каждого запроса:


if (riid == IID_IUnknown)

{

int n = rand() % 3;

if (n == 0) *ppv = static_cast(this);

else if (n == 1) *ppv = static_cast(this);

else if (n == 2) *ppv = static_cast(this);

}


то реализация была бы корректной только в терминах чисто С++-отношений типа (то есть все три интерфейса были бы совместимы по типу с запрошенным типом