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

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

{

if (riid == IID_IBoat)

{

// allocate resource the first time through

// размещаем ресурсы при первом проходе

if (m_pTonsOfMemory == 0) m_pTonsOfMemory = new char[4096 * 4096];

*ppv = static_cast<IBoat*>(this);

}

else if

}

не существует способа определить момент, когда больше нет внешних указателей интерфейса IBoat, так как вызов Release, который клиент делает через интерфейс IBoat, неотличим от вызова Release, сделанного через любой другой интерфейс объекта. В обычной ситуации именно это и нужно, но в данном случае вызовы AddRef и Release через интерфейсы IBoat необходимо рассматривать иначе. Если бы интерфейс IBoat был реализован с использованием композиции, то он имел бы свои собственные уникальные реализации AddRef и Release, в которых он мог бы поддерживать свой собственный счетчик ссылок, отличный от счетчика главного объекта:

class CarBoatPlane : public ICar, public IPlane

{

LONG m_cRef;

char *m_pTonsOfMemory;

CarBoatPlane (void) : m_cRef(0),

m_pTonsOfMemory (0) {}

public:

// IUnknown methods – методы IUnknown

STDMETHODIMP QueryInterface(REFIID, void**);

STDMETHODIMP_(ULONG) AddRef(void);

STDMETHODIMP_(ULONG) Release(void);

// IVehicle methods – методы IVehicle

STDMETHODIMP GetMaxSpeed(long *pMax);

// ICar methods – методы ICar

STDMETHODIMP Brake(void);

// IPlane methods – методы IPlane

STDMETHODIMP TakeOff(void);

// define nested class that implements IBoat

// определяем вложенный класс, реализующий IBoat

struct XBoat : public IBoat {

// get back pointer to main object

// получаем обратный указатель на главный объект

inline CarBoatPlane* This();

LONG m_cBoatRef;

// per-interface ref count

// счетчик ссылок на каждый интерфейс

XBoat(void) : m_cBoatRef(0) {}

STDMETHODIMP QueryInterface(REFIID, void**);

STDMETHODIMP_(ULONG) AddRef(void);

STDMETHODIMP_(ULONG) Release(void);

STDMETHODIMP GetMaxSpeed(long *pval);

STDMETHODIMP Sink(void);

};

XBoat m_xBoat; };

Реализация AddRef и Release из IBoat могут теперь следить за числом ссылок типа IBoat и высвободить ресурсы, когда они больше не нужны:

STDMETHODIMP_(ULONG) CarBoatPlane::XBoat::AddRef()

{

ULONG res = InterlockedIncrement(&m_cBoatRef);

if (res == 1)

{

// first AddRef – первый AddRef

// allocate resource and forward AddRef to object

// размещаем ресурсы и пересылаем AddRef на объект

This()->m_pTonsOfMemory = new char[4096*4096];

This()->AddRef(); }

return res; }

STDMETHODIMP_(ULONG) CarBoatPlane::XBoat::Release()

{

ULONG res = InterlockedDecrement(&m_cBoatRef);

if (res == 0) {

// last Release – последний Release

// free resource and forward Release to object

// освобождаем ресурсы и пересылаем Release на объект

delete [] This()->m_pTonsOfMemory;

This()->Release();

} return res; }

Чтобы эта методика работала, все пользующиеся интерфейсными указателями должны придерживаться требований спецификации СОМ: функция Release должна вызываться через указатель, посредством которого вызывается соответствующая функция AddRef. Поэтому правильной концовкой QueryInterface будет следующая:

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

// use exact ptr

// используем точный указатель return S_OK;

вместо такого:

AddRef();

// just call this->AddRef

// только вызов

this->AddRef return S_OK;

Первый вариант гарантирует, что если клиент пишет следующий правильный код

IBoat *pBoat = 0;

HRESULT hr = pUnk->QueryInterface(IID_IBoat, (void**)&pBoat);

if (SUCCEEDED(hr))

{ hr = pBoat->Sink(); pBoat->Release(); }

то для AddRef и для Release обязательно будет использовано одно и то же значение указателя.


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