interface IUnknown {
// runtime type discovery
HRESULT QueryInterface([in] REFIID riid,
[out, iid_is(riid)] void **ppv);
// lifetime management
ULONG AddRef();
ULONG Release();
}
COM allows every object to implement these
methods as it chooses (within certain restrictions, as described in
Chapter 5, "COM
Servers"). The canonical implementation is as follows:
// Server lifetime management
extern void ServerLock();
extern void ServerUnlock();
class CPenguin : public IBird, public ISnappyDresser {
public:
CPenguin() : m_cRef(0) { ServerLock(); }
virtual ~CPenguin() { ServerUnlock(); }
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {
if( riid == IID_IBird || riid == IID_IUnknown )
*ppv = static_cast<IBird*>(this);
else if( riid == IID_ISnappyDresser )
*ppv = static_cast<ISnappyDresser*>(this);
else *ppv = 0;
if( *ppv ) {
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
ULONG AddRef()
{ return InterlockedIncrement(&m_cRef); }
ULONG Release() {
ULONG l = InterlockedDecrement(&m_cRef);
if( l == 0 ) delete this;
return l;
}
// IBird and ISnappyDresser methods...
private:
ULONG m_cRef;
};
These common assumptions are not the only
possibilities. Common variations include the following: