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;
}
本文详细介绍了智能指针的使用方式及其带来的好处,包括对象析构时自动释放资源、异常处理时自动释放资源、赋值操作时资源的转移等。同时,展示了ATL提供的两种智能指针类型及其实例应用。

被折叠的 条评论
为什么被折叠?



