第4章
本章实现了组件IUnknown接口中的AddRef()和Release()函数。这两个函数实现很简单,本章主要是介绍了客户端程序在什么情况下调用组件的AddRef和Release函数。
客户程序通过这两个函数实现了对组件生命期(创建,释放)的控制。通过控制接口的生命期来控制组件生命期,因为客户能够知道何时开始使用一个接口(AddRef(),引用计数加一),何时使用完这个接口(Release(),引用计数减一),这样当客户使用完组件的所有接口后,组件会完成对自己的释放。
事实上在后面的章节,会介绍智能指针,不要客户去AddRef和Release(实际上,智能指针的内部实现也是使用引用计数的方法)
COM组件中(实现COM的C++类中)有一个称作是引用计数的值。当客户从组件中获得一个接口时,该引用计数增1,当客户使用完成某个接口后,组件的引用计数减1,当引用计数为0时,组件便将自己从内存中删除。正确的使用引用计数,有三条规则
1.返回接口之前调用AddRef。目前包括QueryInterface和CreateInstance,这样在客户端只需用完接口进行Release就好了。
2.客户使用接口完成后,调用该接口的Release。
3.在将一个接口指针赋值给另外一个接口时,调用被赋值接口的AddRef。
对于第3条,用之前的IX接口举例如下:
IX *pIx2 = pIx; pIx2->AddRef();
实际上为每一个接口维护一个引用计数,可以方便程序的调试和资源的释放(可以快速定位到某个接口的计数,进而只查找该接口的问题,或者释放在该接口上的资源。感觉这种计数方式合理很多,书上暂时没用这种方式,以后研究下,书上使用的方法是对一个COM组件维护一个使用计数)。
在客户看来,为每一个接口维护一个引用计数和整个组件维护单个引用计数没有差别,都是调用AddRef和Release。作者为了组件实现起来简单,为整个组件维护了单个的引用计数。
AddRef和Release的实现
ULONG __stdcall AddRef() { return InterlockedIncrement(&m_cRef); }
ULONG __stdcall Release() { if(InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } return m_cRef }
关于引用计数还有几条规则需要注意
1.输出参数规则。输出接口在返回值前必须调用AddRef
2.输入参数规则。输入接口参数的生命期永远在调用函数的生命期内,所以不需要AddRef,更不需要Release。
3.输入输出参数规则。接口在被赋值之前,先Release,然后再AddRef。
4.局部变量。等同于输入参数规则。
5.全局变量和不能确定时,都需要调用AddRef和Release。
本章的例子:
//IUnkown.cpp //use:cl IUnkown.cpp UUID.lib // #include <iostream> #include <string> #include <objbase.h> using namespace std; void trace(string 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; }; extern const IID IID_IX ; extern const IID IID_IY ; extern const IID IID_IZ ; //Component class CA:public IX, public IY { virtual HRESULT __stdcall QueryInterface(const IID &iid, void **ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); virtual void __stdcall Fx() { cout<<"Fx"<<endl; } virtual void __stdcall Fy() { cout<<"Fy"<<endl; } public: CA():m_cRef(0) {} ~CA() {trace("CA:Destroy itselt");} private: long m_cRef; }; HRESULT __stdcall CA::QueryInterface(const IID &iid, void **ppv) { if(iid == IID_IUnknown) { trace("Return pointer to IUnkown"); *ppv = static_cast<IX*>(this); } else if(iid == IID_IX) { trace("pointer to IX"); *ppv = static_cast<IX*>(this); } else if(iid == IID_IY) { trace("return pointer to IY"); *ppv = static_cast<IY*>(this); } else { trace("Interface not supported"); *ppv = NULL; return E_NOINTERFACE; } reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK; } ULONG __stdcall CA::AddRef() { cout<<"CA:AddRef "<<m_cRef + 1<<endl; return InterlockedIncrement(&m_cRef); } ULONG __stdcall CA::Release() { cout<<"CA:Release "<<m_cRef - 1<<endl; if(InterlockedDecrement(&m_cRef) == 0) { delete this; return 0; } return m_cRef; } //Create function IUnknown *CreateInstance() { IUnknown *pI = static_cast<IX*>(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 main(void) { HRESULT hr; trace("Client: Get Iunkown 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 get IX"); pIx->Fx(); pIx->Release(); } trace("Client: Get interface IY"); IY *pIy = NULL; hr = pIunknown->QueryInterface(IID_IY, (void**)&pIy); if(SUCCEEDED(hr)) { trace("Client: Succeeded get IY"); pIy->Fy(); pIy->Release(); } trace("Client: Ask for an unsupported interface"); IZ *pIz = NULL; hr = pIunknown->QueryInterface(IID_IZ, (void**)&pIz); if(SUCCEEDED(hr)) { trace("Client: Succeeded get IZ"); pIz->Fz(); pIz->Release(); } else { trace("Client: Could not get interface IZ"); } pIunknown->Release(); return 0; }