1、你可能忘記進行引用計數或者忘記釋放某個元件上接口的指針。
2、即使你在該調用Release的時候調用了它,在程式中卻不一定會真正調用它,因為發生異常後C++的異常處理程式不會調用Release以釋放此COM元件。
3、一對QueryInterface調用極易使函數中有用的代碼黯然失色。
4、其次,QueryInterface不是類型安全的,原因在于(void**)。例如:
IZ* pIZ;
pIX->QueryInterface(IID_IY,(void**)&pIZ);
雖然将一個IY接口指針賦給了一個IZ接口指針,但編譯仍然會通過。
為解決以上問題,可以通過封裝來解決。一種方法是:使用智能指針類來封裝接口指針。另一種方法是通過包裝類對接口本身進行封裝。
一:智能接口指針:
智能接口指針将把引用計數這類細節隐藏起來,并且當程式的執行離開了智能接口指針的作用域後,相應的接口将被釋放掉。一個智能指針實際上就是一個重載了操作符->的類。智能接口指針類包含指向另外一個對象的指針。當使用者調用智能指針上的->操作符時,智能指針把此調用轉發給它所包含的指針所指的對象。包含的指針将是指向一個接口的。
A simple example:
class CFoo{
public:
virtual void Bar();
};
class CFooPointer{
public:
CFooPointer(CFoo* p){m_p = p;}
CFoo* operator ->(){return m_p;}
private:
CFoo *m_p;
};
.......
void Funky(CFoo* pFoo){
CFooPointer spFoo(pFoo);
spFoo->Bar();
}
//實作一個接口指針類IPtr:
template <class T,const IID* piid>
class IPtr{
public:
IPtr(){ m_pI = NULL;}
IPtr(T* lp){
m_pI = lp;
if(m_pI != NULL) m_pI->AddRef();
}
IPtr(IUnknown* pI){
m_pI = NULL;
if(pI != NULL){
pI->QueryInterface(*piid,(void**)&m_pI);
}
}
~IPtr(){Release();}
void Release(){
if(m_pI != NULL){
T* pOld = m_pI;
m_pI = NULL;
pOld->Release();
}
}
operator T*(){ return m_pI; }
T& operator*(){assert(m_pI != NULL); return *m_pI;}
T** operator&(){assert(m_pI != NULL); return &m_pI;}
T* operator->(){assert(m_pIY != NULL); return m_pI};
T* operator=(T* pI){
if(m_pI != pI){
IUnknown* pOld = m_pI;
m_pI = pI;
if(m_pI != NULL){
m_pI -> AddRef();
}
if(pOld != NULL){
pOld->Release();
}
return m_pI;
}
T* operator = (IUnknown* pI){
IUnknown* pOld = m_pI;
m_pI = NULL;
if(pI != NULL){
HRESULT hr = pI->QueryInterface(*piid,(void**)&m_pI);
assert(SUCCEEDED(hr) && (m_pI != NULL));
}
if(pOld != NULL){
pOld->Release();
}
return m_pI;
}
BOOL operator ! (){
return (m_pI == NULL)?TRUE:FALSE;
}
const IID& iid(){
return *piid;
}
private:
T* m_pI;
};
//test
void main(){
IPtr<IX,&IID_IX> spIX;
HRESULT hr = ::CoCreateInstance(CLSID_COMPONENT1, NULL, CLSCTX_ALL,spIX.iid() ,(void**)&spIX);
if(SUCCEEDED(hr)){
spIX->Fx();
}
}
//test
void Fuzzy(IX* pIX1,IX* pIX2){
IPtr<IX,IID_IX> spIX;
spIX = pIX1;
spIX->Fx();
spIX = pIX2;
spIX->Fx();
}
//test.轉換操作符,将一個IPtr對象賦給同類型的另一個IPtr對象。
typedef IPtr<IX,IID_IX> SPIX;
SPIX g_spIX;
void Wuzzy(SPIX spIX){
g_spIX = spIX;
}
//test.将另外一個不同類型的接口指針賦給一個智能指針,指派操作符将自動調用QueryInterface。
void WsaABear(IY* pIY){
IPtr<IX,IID_IX> spIX;
spIX = pIY;
if(spIX) spIX->Fx();
}