A Review of Smart Pointers
使用smart pointers的好处:
对象析构时,自动release interface
发生异常时,创建在栈上的对象自动release interface
assignment操作时,旧的interface自动release,新的interface自动AddRef
提供不同的构造函数
可以在大部分用raw pointer的地方使用
ATL提供了2种smart pointer:
CComPtr<>
CComQIPtr<> //可以对不同类型的赋值对象做QueryInterface操作
The CComPtr and CComQIPtr Classes
template <class T> class CComPtrBase { ... T* p; }; template <class T> class CComPtr : public CComPtrBase<T> { ... }; template <class T, const IID* piid = &__uuidof(T)> class CComQIPtr : public CComPtr<T> { ... };
T* p是其唯一的state
Constructors and Destructor
CComPtrBase() { p = NULL; } CComPtrBase(T* p) { if ((p = lp) != NULL) p->AddRef(); } ~CComPtrBase() { if (p) p->Release(); } CComPtr(const CComPtr<T>& lp) : CComPtrBase<T>(lp.p) { }
CComQIPtr(T* lp) : CComPtr<T>(lp) {} CComQIPtr(const CComQIPtr<T,piid>& lp) : CComPtr<T>(lp.p) {}
CComQIPtr(IUnknown* lp) { if (lp != NULL) lp->QueryInterface(*piid, (void **)&p); }
当QueryInterface不成功时,应该注意到指针==null
void func (IUnknown* punk) { CComQIPtr<INamedObject> pno (punk); if (pno) { // Can call SomeMethod because the QI worked pno->SomeMethod (); } }
Initialization
// CComPtr assignment operators T* operator=(T* lp); template <typename Q> T* operator=(const CComPtr<Q>& lp); T* operator=(const CComPtr<T>& lp);
// CComQIPtr assignment operators T* operator=(T* lp); T* operator=(const CComQIPtr<T>& lp); T* operator=(IUnknown* lp);
注意CComQIPTR可以直接对非空interface pointer查询
Object Instantiation Methods
HRESULT CoCreateInstance (REFCLSID rclsid, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) { ATLASSERT(p == NULL); return ::CoCreateInstance(rclsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&p); } HRESULT CoCreateInstance (LPCOLESTR szProgID, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL);
例子:
ISpeaker* pSpeaker; HRESULT hr = ::CoCreateInstance (__uuidof (Demagogue), NULL, CLSCTX_ALL, __uuidof (ISpeaker_, (void**) &pSpeaker); ... Use the interface pSpeaker->Release () ; CComPtr<ISpeaker> pSpeaker; HRESULT hr = pSpeaker.CoCreateInstance (__uuidof (Demogogue)); ... Use the interface. It releases when pSpeaker leaves scope
CComPtr and CComQIPtr Operations
T& operator*() const { ATLENSURE(p!=NULL); return *p; }
T** operator&() { ATLASSERT(p==NULL); return &p; }
例子:
STDMETHODIMP SomeClass::UpdateObject ( /* [in, out] */ IExpected** ppExpected); CComPtr<IExpected> pE = /* Initialize to some value */ ; pobj->UpdateObject (&pE); // Asserts in debug build because // pE is non-NULL
也可以:
pobj->UpdateObject (&pE.p);
CComPtr and CComQIPtr Resource-Management Operations
注意如果smart pointer夹在CoInitilize()和CoUninitialize()之间,析构函数可能来不及运行
int main( ) { HRESULT hr = CoInitialize( NULL ); If (FAILED(hr)) return 1; // Something is seriously wrong CComPtr<IUnknown> punk = /* Initialize to some object */ ; ... punk.Release( ); // Must Release before CoUninitialize! CoUninitialize( ); }
上面这个例子使用点操作Release,所以操作之后内部指针=null;在ATL3之前如果使用->Release内部pointer将会释放2次
void Release() { T* pTemp = p; if (pTemp) { p = NULL; pTemp->Release(); } }
ATL3之后,->操作AddRef和Release将会出现编译错误:
_NoAddRefReleaseOnCComPtr<T>* operator->() const { ATLASSERT(p!=NULL); return (_NoAddRefReleaseOnCComPtr<T>*)p; }
template <class T>
class _NoAddRefReleaseOnCComPtr : public T {
private:
STDMETHOD_(ULONG, AddRef)()=0;
STDMETHOD_(ULONG, Release)()=0;
};
The CopyTo Method
HRESULT CopyTo(T** ppT) { ATLASSERT(ppT != NULL); if (ppT == NULL) return E_POINTER; *ppT = p; if (p) p->AddRef(); return S_OK; }
通常,使用CopyTo来制造[out]:
STDMETHODIMP SomeClass::get_Object( /* [out] */ IExpected** ppExpected) { // Interface saved in member m_object // of type CComPtr<IExpected> // Correctly AddRefs pointer return m_object.CopyTo (ppExpected) ; }
注意下面的错误代码:
STDMETHODIMP SomeClass::get_Object (
/* [out] */ IExpected** ppExpected) {
// Interface saved in member m_object
// of type CComPtr<IExpected>
*ppExpected = m_object ; // Wrong! Does not AddRef pointer!
}
The Type-Cast Operator
operator T*() const { return (T*) p; }
例子:
STDMETHODIMP SomeClass::put_Object (
/* [in] */ IExpected* pExpected);
// Interface saved in member m_object of type CComPtr<IExpected>
// Correctly does not AddRef pointer!
pObj->put_Object (m_object) ;
The Detach and Attach Methods
T* Detach() { T* pt = p; p = NULL; return pt; }
例子:
STDMETHODIMP SomeClass::get_Object ( /* [out] */ IExpected** ppExpected) { CComPtr<IExpected> pobj = /* Initialize the smart pointer */ ; *ppExpected = pobj->Detach(); // Destructor no longer Releases return S_OK; }
上述可以避免不必要的AddRef/Release操作
void Attach(T* p2) { if (p) p->Release(); p = p2; }
例子:
STDMETHODIMP SomeClass::get_Object (
/* [out] */ IExpected** ppObject);
void VerboseGetOption () {
IExpected* p;
pObj->get_Object (&p) ;
CComPtr<IExpected> pE;
pE.Attach (p); // Destructor now releases the interface pointer
// Let the exceptions fall where they may now!!!
CallSomeFunctionWhichThrowsExceptions();
}
Miscellaneous Smart Pointer Methods
template <class Q> HRESULT QueryInterface(Q** pp) const { ATLASSERT(pp != NULL && *pp == NULL); return p->QueryInterface(__uuidof(Q), (void**)pp); }
例子:
CComPtr<IFoo> pfoo = /* Initialize to some IFoo */ IBar* pbar; // We specify an IBar variable so the method queries for IID_IBar HRESULT hr = pfoo.QueryInterface(&pBar);
bool IsEqualObject(IUnknown* pOther);
例子:
bool SameObjects(IUnknown* punk1, IUnknown* punk2) { CComPtr<IUnknown> p (punk1); return p.IsEqualObject (punk2); } IUnknown* punk1 = NULL; IUnknown* punk2 = NULL; ATLASSERT (SameObjects(punk1, punk2); // true
HRESULT SetSite(IUnknown* punkParent);
HRESULT Advise(IUnknown* pUnk, const IID& iid, LPDWORD pdw); CComPtr<ISource> ps /* Initialized via some mechanism */ ; ISomeSink* psink = /* Initialized via some mechanism */ ; DWORD dwCookie; ps->Advise (psink, __uuidof(ISomeSink), &dwCookie);
CComPtr Comparison Operators
bool operator!() const { return (p == NULL); } bool operator< (T* pT) const { return p < pT; } bool operator==(T* pT) const { return p == pT; } bool operator!=(T* pT) const { return !operator==(pT); }
The CComPtr Specialization for IDispatch
//specialization for IDispatch template <> class CComPtr<IDispatch> : public CComPtrBase<IDispatch> { public: CComPtr() {} CComPtr(IDispatch* lp) : CComPtrBase<IDispatch>(lp) {} CComPtr(const CComPtr<IDispatch>& lp) : CComPtrBase<IDispatch>(lp.p) {} };
Property Accessor and Mutator Methods
HRESULT GetIDOfName(LPCOLESTR lpsz, DISPID* pdispid);
HRESULT GetProperty(DISPID dwDispID, VARIANT* pVar); HRESULT PutProperty(DISPID dwDispID, VARIANT* pVar);
上述两步可以合并:
HRESULT GetPropertyByName(LPCOLESTR lpsz, VARIANT* pVar); HRESULT PutPropertyByName(LPCOLESTR lpsz, VARIANT* pVar);
Method Invocation Helper Functions
HRESULT Invoke0(DISPID dispid, VARIANT* pvarRet = NULL); HRESULT Invoke0(LPCOLESTR lpszName, VARIANT* pvarRet = NULL); HRESULT Invoke1(DISPID dispid, VARIANT* pvarParam1, VARIANT* pvarRet = NULL); HRESULT Invoke1(LPCOLESTR lpszName, VARIANT* pvarParam1, VARIANT* pvarRet = NULL); HRESULT Invoke2(DISPID dispid, VARIANT* pvarParam1, VARIANT* pvarParam2, VARIANT* pvarRet = NULL); HRESULT Invoke2(LPCOLESTR lpszName, VARIANT* pvarParam1, VARIANT* pvarParam2, VARIANT* pvarRet = NULL); HRESULT InvokeN(DISPID dispid, VARIANT* pvarParams, int nParams, VARIANT* pvarRet = NULL); HRESULT InvokeN(LPCOLESTR lpszName, VARIANT* pvarParams, int nParams, VARIANT* pvarRet = NULL);
注意使用数组传递参数时,应该倒叙
例子:
HRESULT TheEasyWay( IDispatch *spCalcDisp ) { CComPtr< IDispatch > spCalcDisp( pCalcDisp ); CComVariant varOp1( 6.0 ); CComVariant varOp2( 7.0 ); CComVariant varResult; HRESULT hr = spCalcDisp.Invoke2( OLESTR( "Add" ), &varOp1, &varOp2, &varResult ); // varResult now holds sum of 6 and 7 }
static HRESULT GetProperty(IDispatch* pDisp, DISPID dwDispID, VARIANT* pVar); static HRESULT PutProperty(IDispatch* pDisp, DISPID dwDispID, VARIANT* pVar);
例子:
HRESULT GetCount(IDispatch* pdisp, long* pCount) { *pCount = 0; const int DISPID_COUNT = 1; CComVariant v; CComPtr<IDispatch>::GetProperty (pdisp, DISPID_COUNT, &v); HRESULT hr = v.ChangeType (VT_I4); If (SUCCEEDED (hr)) *pCount = V_I4(&v) ; return hr; }