C++. , vptr vtbl. , vtbl , . , QueryInterface, AddRef Release, , ( ). , . .
. ICar ( ) , GetMaxSpeed ( ). IBoat ( ) IPlane ( ) , GetMaxSpeed . , GetMaxSpeed , vtbl, ICar, IBoat IPlane , .
, . , , ? , . C++, :
struct IXCar : public ICar { // add new non-clashing method as pure virtual // virtual HRESULT STDMETHODCALLTYPE GetMaxCarSpeed(long *pval) = 0; // implement clashing method by upcalling // non-clashing implementation in derived class // // STDMETHODIMP GetMaxSpeed(long *pval) { return GetMaxCarSpeed(pval); } };
, IBoat IPlane , GetMaxSpeed GetMaxSpeed:
class CarBoatPlane : public IXCar, public IXBoat, public IXPlane { public: // Unknown methods - IUnknown STDMETHODIMP QueryInterface(REFIID, void**); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IVehicle methods - IVehicle // do not override GetMaxSpeed! // GetMaxSpeed! // ICar methods - ICar STDMETHODIMP Brake(void); // IBoat methods - IBoat STDMETHODIMP Sink(void); // IXPlane methods - IXPlane STDMETHODIMP TakeOff(void); // upcalled from IXCar::GetMaxSpeed // IXCar::GetMaxSpeed STDMETHODIMP GetMaxCarSpeed(long *pval); // upcalled from IXBoat::GetMaxSpeed // IXBoat::GetMaxSpeed STDMETHODIMP GetMaxBoatSpeed(long *pval); // called from IXPlane::GetMaxSpeed // IXPlane::GetMaxSpeed STDMETHODIMP GetMaxPlaneSpeed(long *pval); }
4.6 vtbl. , GetMaxSpeed . CarBoatPlane , CarBoatPlane . , CarBoatPlane GetMaxSpeed, , , IXCar, IXBoat IXPlane. , , ( ) .
, IUnknown. , C++. C++, , . , vtbl QueryInterface. C++ C++ . , , QueryInterface, . :
class CarPlane { LONG m_cRef; CarPlane(void) : m_cRef(0) {} public: // Main IUnknown methods // IUnknown STDMETHODIMP QueryInterface(REFIID, void**); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); private: // define nested class that implements ICar // , ICar struct XCar : public ICar { // get back pointer to main object // inline CarPlane* This(); STDMETHODIMP QueryInterface(REFIID, void**); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); STDMETHODIMP GetMaxSpeed(long *pval); STDMETHODIMP Brake(void); }; // define nested class that implements IPlane // , IPlane struct XPlane : public IPlane { // Get back pointer to main object // inline CarPlane* This(); STDMETHODIMP QueryInterface(REFIID, void**); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); STDMETHODIMP GetMaxSpeed(long *pval); STDMETHODIMP TakeOff(void); }; // declare instances of nested classes // XCar m_xCar; XPlane m_xPlane; };
, , CarPlane. 4.7 vtbl.
, , . GetMaxSpeed:
STDMETHODIMP CarPlane::XCar::GetMaxSpeed(long *pn) { // set *pn to max speed for cars // *pn } STDMETHODIMP CarPlane::XPlane::GetMaxSpeed(long *pn) { // set *pn to max speed for planes // *pn }
, GetMaxSpeed , , vtbl, ICar IPlane, GetMaxSpeed.
, CarPlane, , IUnknown, IUnknown . CarPlane , . , , static_cast vptr, QueryInterface CarPlane , :
STDMETHODIMP CarPlane::QueryInterface(REFIID riid, void **ppv) { if (riid == IID_IUnknown) *ppv = static_cast<IUnknown*>(&m_xCar); else if (riid == IID_IVehicle) *ppv = static_cast<IVehicle*> (&m_xCar); else if (riid == IID_ICar) *ppv = static_cast<ICar*>(&m_xCar); else if (riid == IID_IPlane) *ppv = static_cast<IPlane*>(&m_xPlane); else return (*ppv = 0), E_NOINTERFACE; ((IUnknown*)(*ppv))->AddRef(); return S_OK; }
CarPlane QueryInterface, QueryInterface CarPlane. , - . CarPlane::XCar , this this .
inline CarPlane CarPlane::XCar::This(void) { return (CarPlane*)((char*)this // ptr to composite - - offsetof (CarPlane, m_xCar)); } inline CarPlane CarPlane::XPlane::This(void) { return (CarPlane*)((char*)this // ptr to composite - - offsetof(CarPlane, m_xPlane)); }
(back-pointer) , . QueryInterface :
STDMETHODIMP CarPlane::XCar::QueryInterface(REFIID r, void**p) { return This()->QueryInterface(r, p); } STDMETHODIMP CarPlane::XPlane::QueryInterface(REFIID r, void**p) { return This()->QueryInterface(r, p); }
this AddRef Release ( ) .
, , , . , , , ( , ), . , CarPlane , , . , MFC (Microsoft Foundation Classes Microsoft) . , . , , , , , , . , , GetMaxSpeed, , , , . , . , .
. , vtbl AddRef Release. :
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { 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 .
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; // 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() };
QueryInterf , AddRef Release. , , :
// inttable.h // (book-specific header file) // ( , ) #define COMPOSITE_OFFSET(ClassName, BaseName, \ MemberType, MemberName) \ (DWORD(static_cast<BaseName*>(\ reinterpret_cast<MemberType*>(0x10000000 + \ offsetof(ClassName, MemberName)))) - 0 10000000) #define IMPLEMENTS_INTERFACE_WITH_COMPOSITE(Req,\ MemberType, MemberName) \ { &IID_##Req,ENTRY_IS_OFFSET, COMPOSITE_OFFSET(_IT,\ Req, MemberType, MemberName) }, // impunk.h // (book-specific header file) // ( , ) #def1ne IMPLEMENT_COMPOSITE_UNKNOWN(OuterClassName,\ InnerClassName, DataMemberName) \ OuterClassName *This() \ { return (OuterClassName*)((char*)this - \ offsetof(OuterClassName, DataMemberName)); }\ STDMETHODIMP QueryInterface(REFIID riid, void **ppv)\ { return This()->QueryInterface(riid, ppv); }\ STDMETHODIMP_(ULONG) AddRef(void) \ { return This()->AddRef(); }\ STDMETHODIMP_(ULONG) Release(void) \ { return This()->Release(); }
QueryInterface, AddRef Release, .