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 обязательно будет использовано одно и то же значение указателя.

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

class CarBoatPlane : public ICar, public IPlane

{ public: struct XBoat : public IBoat

{

// composite QI/AddRef/Release/This()

// композит из QI/AddRef/Release/This()

IMPLEMENT_COMPOSITE_UNKNOWN(CarBoatPlane, XBoat, m_xBoat) STDMETHODIMP GetMaxSpeed (long *pval);

STDMETHODIMP Sink(void);

};

XBoat m_xBoat;

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату