C++ , (overhead) vptr ( , sizeof (void*) == 4). , , , , , . , , , vptr , , , . . , , , vptr .
, , QueryInterface IUnknown . . QueryInterface , IUnknown. , vptr , , - . (transient) " " Microsoft (Microsoft white paper The Programmer's Cookbook), (Crispin Goswell) (http://www.microsoft.com/oledev). " " (tearoff).
. , , . , QueryInterface QueryInterface . , : 1) , , 2) , , , , . IBoat :
class CarBoat : public ICar { LONG m_cRef; CarBoat (void): m_cRef(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); // define nested class that implements IBoat // , IBoat struct XBoat : public IBoat { LONG m_cBoatRef; // back pointer to main object is explicit member // - CarBoat *m_pThis; inline CarBoat* This() { return m_pThis; } XBoat(CarBoat *pThis); ~XBoat(void); STDMETHODIMP QueryInterface(REFIID, void**); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); STDMETHODIMP GetMaxSpeed(long *pval); STDMETHODIMP Sink(void); }; // note: no data member of type Xboat // : Xboat };
QueryInterface , IBoat:
STDMETHODIMP CarBoat::QueryInterface(REFIID riid, void **ppv) { if (riid == IID_IBoat) *ppv = static_cast<IBoat*>(new XBoat(this)); else if (riid == IID_IUnknown) *ppv = static_cast<IUnknown*>(this); : : :
IBoat . QueryInterface AddRef :
((IUnknown*)*ppv)->AddRef();
AddRef QueryInterface . , , . . :
CarBoat::XBoat::XBoat(CarBoat *pThis) : m_cBoatRef(0), m_pThis(pThis) { m_pThis->AddRef(); } CarBoat::XBoat::~XBoat(void) { m_pThis->Release(); }
, QueryInterface , . ( ), , , AddRef, :
STDMETHODIMP CarBoat::XBoat::QueryInterface(REFIID riid, void**ppv) { if (riid != IID_IBoat) return This()->QueryInterface(riid, ppv); *ppv = static_cast<IBoat*>(this); reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK; }
, , , , . , , :
STDMETHODIMP_(ULONG) CarBoat::XBoat::AddRef (void) { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CarBoat::X8oat::Release(void) { ULONG res = InterlockedDecrement(&m_cBoatRef); if (res == 0) delete this; // dtor releases main object // return res; }
, This() , . , , , .
, . , . , 4 . . - , 4 vptr. 1. - , (custom memory allocator), 4 / , - malloc/operator new. , 4 , . , 12 , , 16 , , , new. , , . , .
, . , 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) // tearoff exists, let tearoff QI // , QI return m_pTearOff->QueryInterface(riid, ppv); else // memory allocation failure // return (*ppv = 0), E_NOINTERFACE; } else ... : : : }
QueryInterface . , 12 (vptr IUnknown + + ). , 24 28 ( 12 + Vehicle vptr + + + ( ) malloc (memory allocation - )).
, :
class GenericVehicle : public ITruck, public IHelicopter, public IBoat, public ICar, public IMonsterTruck, public IBicycle, public IMotorcycle, public ICar, public IPlane, public ISkateboard { LONG m_cRef; // IUnknown methods - IUnknown : : : };
, 44 ( vptr + ). , , , . , ( - ). , . , , . , , , .
1 , AddRef. , , ( ) AddRef/Release.