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

ОглавлениеДобавить в закладки К обложке

К сожалению, дело с отделяемым интерфейсом обстоит еще хуже. Как видно из показанной ранее реализации, если объект получает два запроса QueryInterface на тот же самый отделяемый интерфейс, то будут созданы две копии этого отделяемого интерфейса, так как указатель на первый из них полностью забывается главным объектом, поскольку он был возвращен вызывающему объекту. Это означает, что в этом случае отделяемый интерфейс занимает по крайней мере от 24 до 32 байт, так как в памяти находятся оба vptr отделяемого интерфейса, по одному на каждый запрос QueryInterface. Эта память не будет восстановлена, пока клиент не освободит каждый отделяемый интерфейс. Ситуация, когда два запроса QueryInterface удерживают указатель в течение всего времени жизни объекта, особенно важна, так как именно это и происходит при удаленном обращении к объекту. СОМ-слой, реализующий удаленные вызовы, будет дважды запрашивать объект (с помощью QueryInterface) на предмет одного и того же интерфейса и будет удерживать оба результата в течение всего времени жизни объекта. Это обстоятельство делает отделяемые интерфейсы особенно рискованными для объектов, к которым может осуществляться удаленный доступ.

Узнав обо всех подводных камнях отделяемых интерфейсов, задаешь себе логичный вопрос: "В каких же случаях отделяемые интерфейсы являются подходящими?" Не существует безусловного ответа; в то же время отделяемые интерфейсы очень хороши для поддержки большого числа взаимно исключающих интерфейсов. Рассмотрим случай, в котором в дополнение к трем транспортным интерфейсам, показанным ранее, имеются интерфейсы ITruck (грузовик), IMonsterТruck (грузовик-монстр), IMotorcycle (мотоцикл), IBicycle (велосипед), IUnicycle (уницикл), ISkateboard (скейтборд) и IHelicopter (вертолет), причем все они наследуют IVehicle. Если бы производящий транспортный класс хотел поддерживать любой из этих интерфейсов, но только по одному из них для каждого заданного экземпляра, то для осуществления этого отделяемые интерфейсы были бы прекрасным способом при условии, что главный объект кэшировал бы указатель на первый отделяемый интерфейс. Определение класса главного объекта выглядело бы примерно так:

class GenericVehicle : public IUnknown

{

LONG m_cRef;

IVehicle *m_pTearOff;

// cached ptr to tearoff

// кэшированный указатель на отделяемый интерфейс

GenericVehicle(void) : m_cRef(0), m_pTearOff(0) {}

// IUnknown methods

// методы IUnknown

STDMETHODIMP QueryInterface(REFIID, void **);

STDMETHODIMP_(ULONG) AddRef(void);

STDMETHODIMP_(ULONG) Release (void);

// define tearoff classes

// определяем классы отделяемых интерфейсов

class XTruck : public ITruck { … };

class XMonsterTruck : public IMonsterTruck { … };

class XBicycle : public IBicycle { … };

:

:

:

};

В этом классе в случае, когда не используется ни один из интерфейсов, объект платит за пустой кэшированный указатель только четырьмя дополнительными байтами. Когда приходит запрос QueryInterfасе на один из десяти транспортных интерфейсов, то память выделена для нового отделяемого интерфейса один раз и кэширована для более позднего использования:

STDMETHODIMP GenericVehicle::QueryInterface(REFIID riid ,void **ppv)

{ if (riid == IID_IUnknown) *ppv = static_cast<IUnknown*>(this);

else if (riid == IID_ITruck) { if (m_pTearOff == 0)

// no tearoff yet, make one

// отделяемого интерфейса еще нет, создаем один

m_pTearOff = new XTruck(this);

if (m_pTearOff)

// tearoff exists, let tearoff QI

// отделяемый интерфейс существует, пусть это QI

return m_pTearOff->QueryInterface(riid, ppv);

else

// memory allocation failure

// ошибка выделения памяти

return (*ppv = 0), E_NOINTERFACE;

}

else if (riid == IID_IMonsterTruck)

{

if (in_pTearOff == 0)

// no tearoff yet, make one

// отделяемого интерфейса еще нет, создаем один

m_pTearOff = new XMonsterTruck(this);

if (m_pTearOff)


Логин
Пароль
Запомнить меня