не существует способа определить момент, когда больше нет внешних указателей интерфейса
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; };
Реализация
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; }
Чтобы эта методика работала, все пользующиеся интерфейсными указателями должны придерживаться требований спецификации СОМ: функция
((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(); }
то для
Можно осуществлять композицию в контексте управляемой таблицами реализации
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;
// IVehicle methods
// методы IVehicle
STDMETHODIMP GetMaxSpeed(long *pMax);
// ICar methods
// методы ICar
STDMETHODIMP Brake(void);
// IPlane methods
// методы IPlane
STDMETHODIMP TakeOff(void);
// standard heap-based QI/AddRef/Release
// стандартные расположенные в «куче» QI/AddRef/Release
IMPLEMENT_UNKNOWN(CarBoatPlane)
BEGIN_INTERFACE_TABLE(CarBoatPlane)
IMPLEMENTS_INTERFACE_AS(IVehicle, ICar)
IMPLEMENTS_INTERFACE(ICar)
IMPLEMENTS_INTERFACE(IPlane)
// macro that calculates offset of data member
// макрос, вычисляющий смещение элемента данных
IMPLEMENTS_INTERFACE_WITH_COMPOSITE(IBoat, XBoat, m_xBoat)
END_INTERFACE_TABLE() };
В приведенном выше определении класса опущены только определения методов объекта вне
// inttable.h
// (book-specific header file)
// (заголовочный файл, специфический для данной книги)
#define COMPOSITE_OFFSET(ClassName, BaseName, \
MemberType, MemberName) \
(DWORD(static_cast