话起于《COM技术内幕》第3章的例子,具体代码如下:
#include "stdafx.h" // // IUnknown.cpp // To compile use: cl IUnknown.cpp UUID.lib // #include <iostream> #include <objbase.h> using namespace std; void trace(const char* msg) { cout << msg << endl ;} // Interfaces interface IX : IUnknown { virtual void __stdcall Fx() = 0 ; } ; interface IY : IUnknown { virtual void __stdcall Fy() = 0 ; } ; interface IZ : IUnknown { virtual void __stdcall Fz() = 0 ; } ; // Forward references for GUIDs extern const IID IID_IX ; extern const IID IID_IY ; extern const IID IID_IZ ; // // Component // class CA : public IX, public IY { //IUnknown implementation virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv) ; virtual ULONG __stdcall AddRef() { return 0 ;} virtual ULONG __stdcall Release() { return 0 ;} // Interface IX implementation virtual void __stdcall Fx() { cout << "Fx" << endl ;} // Interface IY implementation virtual void __stdcall Fy() { cout << "Fy" << endl ;} } ; HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv) { if (iid == IID_IUnknown) { trace("QueryInterface: Return pointer to IUnknown.") ; *ppv = static_cast<IX*>(this) ; } else if (iid == IID_IX) { trace("QueryInterface: Return pointer to IX.") ; *ppv = static_cast<IX*>(this) ; } else if (iid == IID_IY) { trace("QueryInterface: Return pointer to IY.") ; *ppv = static_cast<IY*>(this) ; } else { trace("QueryInterface: Interface not supported.") ; *ppv = NULL ; return E_NOINTERFACE ; } reinterpret_cast<IUnknown*>(*ppv)->AddRef() ; // See Chapter 4. return S_OK ; } // // Creation function // IUnknown* CreateInstance() { IUnknown* pI = static_cast<IY*>(new CA) ; pI->AddRef() ; return pI ; } // // IIDs // // {32bb8320-b41b-11cf-a6bb-0080c7b2d682} static const IID IID_IX = {0x32bb8320, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; // {32bb8321-b41b-11cf-a6bb-0080c7b2d682} static const IID IID_IY = {0x32bb8321, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; // {32bb8322-b41b-11cf-a6bb-0080c7b2d682} static const IID IID_IZ = {0x32bb8322, 0xb41b, 0x11cf, {0xa6, 0xbb, 0x0, 0x80, 0xc7, 0xb2, 0xd6, 0x82}} ; // // Client // int _tmain(int argc, _TCHAR* argv[]) { HRESULT hr ; trace("Client: Get an IUnknown pointer.") ; IUnknown* pIUnknown = CreateInstance() ; trace("Client: Get interface IX.") ; IX* pIX = NULL ; hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX) ; if (SUCCEEDED(hr)) { trace("Client: Succeeded getting IX.") ; pIX->Fx() ; // Use interface IX. } trace("Client: Get interface IY.") ; IY* pIY = NULL ; hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY) ; if (SUCCEEDED(hr)) { trace("Client: Succeeded getting IY.") ; pIY->Fy() ; // Use interface IY. } trace("Client: Ask for an unsupported interface.") ; IZ* pIZ = NULL ; hr = pIUnknown->QueryInterface(IID_IZ, (void**)&pIZ) ; if (SUCCEEDED(hr)) { trace("Client: Succeeded in getting interface IZ.") ; pIZ->Fz() ; } else { trace("Client: Could not get interface IZ.") ; } trace("Client: Get interface IY from interface IX.") ; IY* pIYfromIX = NULL ; hr = pIX->QueryInterface(IID_IY, (void**)&pIYfromIX) ; if (SUCCEEDED(hr)) { trace("Client: Succeeded getting IY.") ; pIYfromIX->Fy() ; } trace("Client: Get interface IUnknown from IY.") ; IUnknown* pIUnknownFromIY = NULL ; hr = pIY->QueryInterface(IID_IUnknown, (void**)&pIUnknownFromIY) ; if (SUCCEEDED(hr)) { cout << "Are the IUnknown pointers equal? " ; if (pIUnknownFromIY == pIUnknown) { cout << "Yes, pIUnknownFromIY == pIUnknown." << endl ; } else { cout << "No, pIUnknownFromIY != pIUnknown." << endl ; } } // Delete the component. delete pIUnknown ; //pIUnknown->Release(); return 0 ; }上面的例子运行起来没有任何问题,但有一个疑问,如果我把CreateInstance()修改成
IUnknown* CreateInstance() { IUnknown* pI = static_cast<IY*>(new CA) ; pI->AddRef() ; return pI ; }会有什么影响?
从分析上来看没有任何问题,只是将多继承的对象强转成哪个父类对象,但是在运行到delete pIUnknown时出现ASSERT异常,F10进去发现_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));出错。
因为此时pIUnknown指针并不指向当初分配的整个内存块,在new的时候会给内存写头信息,此时pIUnknown无法找到头信息,导致delete失败。
要想delete成功,必须修改继承顺序,即
class CA : public IY, public IX{...}
当然,在这还有一个疑问,如果是pIUnknown->Release()会是怎样的结果?
本例Release()函数没有进行任何处理,肯定会造成内存泄漏,如果Release()里面也是delete的话,那么强转的类型真的很重要。