com 與 智慧指標,及一些細節問題
阿新 • • 發佈:2018-12-20
CComPtrBase 類 為使用基於COM 的記憶體例程的智慧指標類提供了基礎。
///////////////////////////////////////////////////////////////////////////// // COM Smart pointers template <class T> class _NoAddRefReleaseOnCComPtr : public T { private: STDMETHOD_(ULONG, AddRef)()=0; STDMETHOD_(ULONG, Release)()=0; }; //CComPtrBase provides the basis for all other smart pointers //The other smartpointers add their own constructors and operators template <class T> class CComPtrBase { protected: CComPtrBase() throw() { p = NULL; } CComPtrBase(_Inout_opt_ T* lp) throw() { p = lp; // 普通的引用規則,當介面指標賦值進行賦值操作,應該AddRef // 確保了通過這個指標指標進行的介面操作時介面沒有銷燬 if (p != NULL) p->AddRef(); } void Swap(CComPtrBase& other) { T* pTemp = p; p = other.p; other.p = pTemp; } public: typedef T _PtrClass; ~CComPtrBase() throw() { // 同樣的,當智慧指標銷燬,應該減少引用計數 // 值觀的一個用途就是,區域性變數的智慧指標,通過構造和解構函式來管理引用計數 if (p) p->Release(); } operator T*() const throw() { return p; } T& operator*() const { ATLENSURE(p!=NULL); return *p; } //The assert on operator& usually indicates a bug. If this is really //what is needed, however, take the address of the p member explicitly. T** operator&() throw() { ATLASSERT(p==NULL); return &p; } _NoAddRefReleaseOnCComPtr<T>* operator->() const throw() { ATLASSERT(p!=NULL); return (_NoAddRefReleaseOnCComPtr<T>*)p; } bool operator!() const throw() { return (p == NULL); } bool operator<(_In_opt_ T* pT) const throw() { return p < pT; } bool operator!=(_In_opt_ T* pT) const { return !operator==(pT); } bool operator==(_In_opt_ T* pT) const throw() { return p == pT; } // Release the interface and set to NULL // 顯式的釋放這個指標 void Release() throw() { T* pTemp = p; if (pTemp) { p = NULL; pTemp->Release(); } } // Compare two objects for equivalence inline bool IsEqualObject(_Inout_opt_ IUnknown* pOther) throw(); // Attach to an existing interface (does not AddRef) // 這裡需要注意,Attach 到一個已經存在的介面,不需要呼叫AddRef // 但是,會將之前繫結的介面,進行Release 操作 // 這就要求我們,當重複、連續Attach 到同一個介面,將導致,該介面的引用計數異常 // 這個問題本身就是一個,引用計數的問題,因為,Attach 和 Detach 要對應才保證程式的正常執行 void Attach(_In_opt_ T* p2) throw() { if (p) { ULONG ref = p->Release(); (ref); // Attaching to the same object only works if duplicate references are being coalesced. Otherwise // re-attaching will cause the pointer to be released and may cause a crash on a subsequent dereference. ATLASSERT(ref != 0 || p2 != p); } p = p2; } // Detach the interface (does not Release) // 和上面的Attach 對應 T* Detach() throw() { T* pt = p; p = NULL; return pt; } // 將當前的介面copy 到另外的指標,理所應當的會需要我們增加當前介面指標的引用計數 _Check_return_ HRESULT CopyTo(_COM_Outptr_result_maybenull_ T** ppT) throw() { ATLASSERT(ppT != NULL); if (ppT == NULL) return E_POINTER; *ppT = p; if (p) p->AddRef(); return S_OK; } _Check_return_ HRESULT SetSite(_Inout_opt_ IUnknown* punkParent) throw() { return AtlSetChildSite(p, punkParent); } _Check_return_ HRESULT Advise( _Inout_ IUnknown* pUnk, _In_ const IID& iid, _Out_ LPDWORD pdw) throw() { return AtlAdvise(p, pUnk, iid, pdw); } _Check_return_ HRESULT CoCreateInstance( _In_ REFCLSID rclsid, _Inout_opt_ LPUNKNOWN pUnkOuter = NULL, _In_ DWORD dwClsContext = CLSCTX_ALL) throw() { ATLASSERT(p == NULL); return ::CoCreateInstance(rclsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&p); } #ifdef _ATL_USE_WINAPI_FAMILY_DESKTOP_APP _Check_return_ HRESULT CoCreateInstance( _In_z_ LPCOLESTR szProgID, _Inout_opt_ LPUNKNOWN pUnkOuter = NULL, _In_ DWORD dwClsContext = CLSCTX_ALL) throw() { CLSID clsid; HRESULT hr = CLSIDFromProgID(szProgID, &clsid); ATLASSERT(p == NULL); if (SUCCEEDED(hr)) hr = ::CoCreateInstance(clsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&p); return hr; } #endif // _ATL_USE_WINAPI_FAMILY_DESKTOP_APP template <class Q> _Check_return_ HRESULT QueryInterface(_Outptr_ Q** pp) const throw() { ATLASSERT(pp != NULL); return p->QueryInterface(__uuidof(Q), (void**)pp); } T* p; };
需要注意的一點:智慧指標已經幫助我們進行了引用計數的操作,此時,我們不應該顯式的呼叫Addref 和 Release 函式。