程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> COM編程—智能指針

COM編程—智能指針

編輯:關於C語言
 

在COM開發中,我們都是在和接口指針打交道。對於接口指針,我們都要小心翼翼的使用AddRef和Release操作來增加對應接口的引用計數。關於引用計數的使用規則可以查看之前總結的 《引用計數(1)》和 《引用技術(2)》。對於接口的引用計數,稍有不慎,就可能出現指針沒有被釋放,或者對已經釋放的指針再次調用Release就會導致程序崩潰。怎麼辦?

程序員都是一群“懶惰”的人,對於這些隨時都會出現問題的地方,我們怎麼可以忍呢?於是,智能指針就出現了。

智能指針

一個智能指針實際上就是一個重載了操作符->的類。智能接口指針類包含指向另外一個對象的指針。當用戶調用智能指針上的->操作符時,智能指針把此調用轉發給它所包含的指針所指的對象。智能接口指針中所包含的指針將是指向一個接口的。這就是設計模式中的代理模式的應用。這也簡化了COM中對引用計數的管理,而真正實現對引用計數管理的是智能接口指針。

我的智能指針

接下來,我要親自實現一個接口指針,通過代碼來學習智能指針是如何實現的。
#include <iostream>
#include <windows.h>
using namespace std;

class RefCount
{
public:
RefCount() : m_nCount(0){}

public:
unsigned AddRef(){ return InterlockedIncrement(&m_nCount); }
unsigned Release(){ return InterlockedDecrement(&m_nCount); }
void Reset(){ m_nCount = 0; }

private:
unsigned long m_nCount;
};

template<class T>
class JTSmartPointer
{
public:
JTSmartPointer(T *pT) : m_pData(pT)
{
m_pData = pT;
m_pRef = new RefCount();
m_pRef->AddRef();
}

JTSmartPointer(const JTSmartPointer<T> &sp)
{
m_pData = sp.m_pData;
m_pRef = sp.m_pRef;
m_pRef->AddRef();
}

~JTSmartPointer()
{
if (m_pRef && m_pRef->Release() == 0)
{
delete m_pRef;
delete m_pData;
}
}

T& operator*()
{
return *m_pData;
}

T* operator->()
{
return m_pData;
}

JTSmartPointer<T>& operator=(const JTSmartPointer<T>& sp)
{
if (this != &sp)
{
if (m_pRef && m_pRef->Release() == 0)
{
delete m_pRef;
delete m_pData;
}
m_pData = sp.m_pData;
m_pRef = sp.m_pRef;
m_pRef->AddRef();
}
return *this;
}

JTSmartPointer<T>& operator=(T *pValue)
{
if (m_pRef && m_pRef->Release() == 0)
{
delete m_pRef;
delete m_pData;
}
m_pData = pValue;
m_pRef = new RefCount();
m_pRef->AddRef();
return *this;
}

private:
T *m_pData;
RefCount *m_pRef;
};

class CA
{
public:
CA() { cout<<"New CA"<<endl; }
~CA() { cout<<"Delete CA"<<endl; }

void Show()
{
cout<<"I am CA."<<endl;
}
};

int main()
{
JTSmartPointer<CA> caObj(new CA);

JTSmartPointer<CA> caObj2 = caObj;
caObj2->Show();

return 0;
}

代碼理解起來很簡單。如果大家有好的想法,期待和大家進行討論。

CComPtr和CComQIPtr

在進行COM開發時,並不需要我們手動的去封裝一個智能指針。在ATL中提供了兩個智能指針:CComPtr和CComQIPtr。這兩個都在<atlbash.h>中聲明。

CComQIPtr包含了CComPtr的所有功能,因此我們完全可以用CComQIPtr來使用智能接口指針;唯一需要注意的一點是CComQIPtr不能定義IUnknown *指針。以下代碼是一些使用CComPtr和CComQIPtr的例子。
CComPtr<IUnknown> spIUnk; // Right
CComPtr<IFun> spFun; // Right
CComQIPtr<IFun> spFun; // Right
CComQIPtr<IFun, &IID_IFun> spFun; // Right

// 給智能指針賦值的方法
CComQIPtr<IFun> spFun; // 調用構造函數,還沒有賦值,被包裝的內部接口指針為NULL
CComQIPtr<IFun> spFun(pOtherIF); //調用構造函數,內部接口指針賦值為通過pOtherIF接口指針調用QueryInterface得到的IFun接口指針

CComQIPtr<IFun> spFun(pUnknown); // 調用構造函數,由IUnknown的QueryInterface得到IFun接口指針

CComQIPtr<IFun> spFun = pOtherInterface; // =運算符重載,含義和上面一樣
spFun = spOtherIF; // 同上
spFun = pUnknown; // 同上

// 得到指針以後,可以通過以下方法進行判斷指針是否有效
if (spFun){}
if (NULL == spFun){}

// 智能指針調用函數的方法
spFun.CoCreateInstance(...) // 與API函數是等價的
spFun.QueryInterface(...)
spFun->Add() // 調用內部接口指針的接口函數

// 智能指針的釋放
spFun.Release(); // 釋放內部的接口指針,同時內部指針賦值為NULL
spFun->Release(); // 這個絕對不能這樣調用,這樣可能造成智能指針內部的接口指針成為懸停的野指針

一般來說,CComQIPtr提供了幾乎所有CComPtr的功能,但是有唯一一個例外,CComQIPtr<IUnknown>是不合法的,template <class T, const IID* piid = &__uuidof(T)>class CComQIPtr 的構造符重復定義。必須寫成CComQIPtr<IUnknown, &IID_IUnknown>。除此之外,其他CComPtr都可以用CComQIPtr代替。

總結

智能指針的種種好處,這裡也進行了總結。希望大家在實際進行開發時,盡量減少和接口指針直接打交道的機會,最好通過智能指針來實現。同時,一定要注意使用智能指針時,對於智能指針的釋放是存在陷阱的,我們絕對不要進行spFun->Release()這樣的調用。最後是我開發時的經驗和教訓,如果不想將周末的休息時間浪費在調試app crash的崩潰上,就使用智能指針吧。

 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved