如果在设计之初没有考虑使用智能指针,有可能在程序所有的地方都是直接使用类的裸指针,在后来再使用智能指针就有可能引入别的问题,大库和函数语义的改变,这样会导致不可知的错误,所以这里尝试提供一个不改变原始类结构的智能指针。
假定现在有类CTestClass, 原来的使用方法是CTestClass * pTClass = new CTestClass(xxxx);
在函数调用中使用CTestClass的指针, 如: HRESULT Process(CTestClass * pClass);
这时我们要在整个设计中使用智能指针,在短平快的指导思想下,不如自己设计一个,一方面可以学习智能指针的思想理论,令一方面还可以直接结合实际,让理论为自己产生价值。
当然,学习首先是拿来,
在使用了ComPtr之后,发现,虽然ComPtr虽然好用,但是它需要操作的类是继承自IUnknown的,而且每一个这样的类都需要实现IUnknown的3个方法,即使在你不需要QueryInterface的时候也是如此。
转而使用Chrome开源代码中的 scoped_refptr,这个自然也是需要操作的类继承自RefCounted, 而且还要把Chrome代码中的base拿来用。
Boost库就不能考虑了。
那么,什么才是合适的设计呢, 如下定义:
1. 短小精干
2. 不改变或尽量少改变已有的接口, 实现中也只能做相对少的修改
在原来的设计中, 大量的使用了如下操作
1. CTestClass * pClass = new CTestClass(....);
2. returntype Procedure(..., pClass, ...)
3. CTestClass * ReturnProcedure(...)
4. pClass2 = pClass
5. pClass->xxxxx()
统计一下比较多的操作是
1. CTestClass * ReturnProcedure(...)
2. pClass2 = pClass;
3. pClass->xxxxx();
所以先设计满足条件的测试程序
首先是测试的类
class CTestClass
{
public:
CTestClass(){ATLTRACE(_T(__FUNCTION__) _T("\n"));}
virtual ~CTestClass(){ATLTRACE(_T(__FUNCTION__) _T("\n"));}
void Do(LPCTSTR lpszText){ATLTRACE(_T(__FUNCTION__) _T(" %s\n"), lpszText);}
};
class CTestClass2
{
int m_nSize;
public:
CTestClass2(int nSize):m_nSize(nSize){ATLTRACE(_T(__FUNCTION__) _T("\n"));}
virtual ~CTestClass2(){ATLTRACE(_T(__FUNCTION__) _T(" %d\n"), m_nSize);}
void Do(LPCTSTR lpszText){ATLTRACE(_T(__FUNCTION__) _T(" %s\n"), lpszText);}
};
然后是测试程序
CAutoSmartPtr<CTestClass> aspTest;
CTestClass * pClass = aspTest;
pClass->Do(_T("class test"));
CAutoSmartPtr<CTestClass> aspTest2 = aspTest;
aspTest2->Do(_T("aspTest2->Do"));
struct foo
{
static void run(CTestClass * ptc, void ** pp)
{
CAutoSmartPtr<CTestClass> ssptc(ptc);
ssptc->Do(_T("run"));
CAutoSmartPtr<CTestClass2> ss2(1000);
ss2.CopyTo(pp);
}
};
CAutoSmartPtr<CTestClass2> asp2;
foo::run(aspTest2, &asp2);
CAutoSmartPtr<CTestClass2> aspTest3(300);
aspTest3->Do(_T("aspTest3->Do"));
智能指针设计如下
template<typename T>
class CAutoSmartPtr
{
class TFollowClass:public T
{
private:
long m_ref;
public:
void addref(){m_ref++;}
void releaseref()
{
if(!((--m_ref) > 0)) delete this;
}
TFollowClass():m_ref(0){}
virtual ~TFollowClass(){}
template<typename A1>
TFollowClass(A1 a1):m_ref(0),T((A1)a1){}
template<typename A1,typename A2>
TFollowClass(A1 a1, A2 a2):m_ref(0),T((A1)a1,(A2)a2){}
template<typename A1,typename A2, typename A3>
TFollowClass(A1 a1, A2 a2, A3 a3):m_ref(0),T((A1)a1,(A2)a2,(A3)a3){}
template<typename A1,typename A2, typename A3, typename A4>
TFollowClass(A1 a1, A2 a2, A3 a3):m_ref(0),T((A1)a1,(A2)a2,(A3)a3, (A4)a4){}
template<typename A1,typename A2, typename A3, typename A4, typename A5>
TFollowClass(A1 a1, A2 a2, A3 a3):m_ref(0),T((A1)a1,(A2)a2,(A3)a3, a4, a5){}
};
protected:
TFollowClass * pshareptr;
void ptrrelease()
{
if(pshareptr) pshareptr->releaseref();
}
void ptraddref()
{
if(pshareptr) pshareptr->addref();
}
public:
CAutoSmartPtr():pshareptr(NULL){}
~CAutoSmartPtr(){ptrrelease();}
CAutoSmartPtr(CAutoSmartPtr & src):pshareptr(src.pshareptr){ptraddref();}
template<typename A1>
CAutoSmartPtr(A1 a1)
{
pshareptr = new TFollowClass(a1);
ptraddref();
}
template<typename A1, typename A2>
CAutoSmartPtr(A1 a1, A2 a2):
pshareptr(NULL)
{
pshareptr = new TFollowClass(a1, a2);
ptraddref();
}
template<typename A1, typename A2, typename A3>
CAutoSmartPtr(A1 a1, A2 a2, A3 a3):
pshareptr(NULL)
{
pshareptr = new TFollowClass(a1, a2, a3);
ptraddref();
}
template<typename A1, typename A2, typename A3, typename A4>
CAutoSmartPtr(A1 a1, A2 a2, A3 a3, A4 a4 ):
pshareptr(NULL)
{
pshareptr = new TFollowClass(a1, a2, a3, a4);
ptraddref();
}
template<typename A1, typename A2, typename A3, typename A4, typename A5>
CAutoSmartPtr(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5):
pshareptr(NULL)
{
pshareptr = new TFollowClass(a1, a2, a3, a4, a5);
ptraddref();
}
CAutoSmartPtr & operator = (CAutoSmartPtr & src)
{
ptrrelease();
pshareptr = src.pshareptr;
ptraddref();
return *this;
}
CAutoSmartPtr & operator = (void *pSrc)
{
ptrrelease();
pshareptr = static_cast<TFollowClass *>(pSrc);
if(pshareptr)
pshareptr->addref();
return *this;
}
CAutoSmartPtr(T * pSrc):pshareptr(static_cast<TFollowClass *>(pSrc))
{
if(pshareptr)
pshareptr->addref();
}
void CopyTo(void ** pp)
{
*pp = (void*)pshareptr;
if(pshareptr)
pshareptr->addref();
}
operator bool ()
{
return !!pshareptr->p;
}
void ** operator &()
{
ptrrelease();
return (void**)&pshareptr;
}
T& operator * (){return *(static_cast<T*>(pshareptr));}
operator T*(){return static_cast<T*>(pshareptr);}
T* operator ->(){return static_cast<T*>(pshareptr);}
bool operator !(){return (pshareptr == NULL);}
bool operator <(T*pT){return static_cast<T*>(pshareptr) < pT;}
bool operator >(T*pT){return static_cast<T*>(pshareptr) > pT;}
bool operator != (T*pT) {return static_cast<T*>(pshareptr) != pT;}
bool operator == (T*pT) {return static_cast<T*>(pshareptr) == pT;}
};
注意,因为这个智能指针在初始化的时候生成的是一个继承自原始类的指针,其实方法和让操作类继承自引用计数是一样的,但是这里的表现形式不同,不需要去修改原始类的代码
- 在所有使用new CTestClass的地方替换成 CAutoSmartPtr<CTestClass>(....),它有和CTestClass一样的初始化列表
- 在所有给指针赋值的地方都换成CopyTo
原来的是
CTestClass * pClass = new CTestClass; CTestClass * pClass2 = GetPointer(); CTestClass * GetPointer() { ... return pClass; }
现在是 CAutoSmartPtr<CTestClass> pClass; CAutoSmartPtr<CTestClass> pClass2; GetPointer(&pClass2); CTestClass * GetPointer(void ** ppClass) { CAutoSmartPtr<CTestClass> spClass; spClass.CopyTo(ppClass); // 这里的引用计数已经被加1了 return spClass; }
让CAutoSmartPtr接近类的语义的代价就是pClass一开始就是一个CTestClass的实例, 而不像其他的智能指针那样一开始是NULL,必须显示的给他赋值,如:
xxx::scope_ptr<CTestClass> spClass = new CTestClass,
所以CAutoSmartPtr的 & 操作会先释放一次原来类的实例
可以修改一个和其他智能指针一样的语义的实现
去掉几个的实现
template<typename A1> CAutoSmartPtr(A1 a1) { pshareptr = new TFollowClass(a1); ptraddref(); }
// 加上 static CAutoSmartPtr CreateInstance() // 无参数的初始化 { pshareptr = new TFollowClass(a1); ptraddref(); } // 加上几个 template<typename A1> static CAutoSmartPtr CreateInstance(A1 a1) // 参数初始化 { pshareptr = new TFollowClass(a1); ptraddref(); }
初始化的时候改成
CAutoSmartPtr<CTestClass> spClass = CAutoSmartPtr<CTestClass>::CreateInstance(); // 没有参数的情况 CAutoSmartPtr<CTestClass> spClass = CAutoSmartPtr<CTestClass>::CreateInstance(3); // 1参数的情况 CTestClass(int x)
完成的程序
template<typename T> class CAutoSmartPtr { class TFollowClass:public T { private: long m_ref; public: void addref(){m_ref++;} void releaseref() { if(!((--m_ref) > 0)) delete this; } TFollowClass():m_ref(0){} virtual ~TFollowClass(){} template<typename A1> TFollowClass(A1 a1):m_ref(0),T((A1)a1){} template<typename A1,typename A2> TFollowClass(A1 a1, A2 a2):m_ref(0),T((A1)a1,(A2)a2){} template<typename A1,typename A2, typename A3> TFollowClass(A1 a1, A2 a2, A3 a3):m_ref(0),T((A1)a1,(A2)a2,(A3)a3){} template<typename A1,typename A2, typename A3, typename A4> TFollowClass(A1 a1, A2 a2, A3 a3):m_ref(0),T((A1)a1,(A2)a2,(A3)a3, (A4)a4){} template<typename A1,typename A2, typename A3, typename A4, typename A5> TFollowClass(A1 a1, A2 a2, A3 a3):m_ref(0),T((A1)a1,(A2)a2,(A3)a3, a4, a5){} }; protected: TFollowClass * pshareptr; void ptrrelease() { if(pshareptr) pshareptr->releaseref(); } void ptraddref() { if(pshareptr) pshareptr->addref(); } public: CAutoSmartPtr():pshareptr(NULL){} ~CAutoSmartPtr(){ptrrelease();} CAutoSmartPtr(CAutoSmartPtr & src):pshareptr(src.pshareptr){ptraddref();} // 加上 static CAutoSmartPtr CreateInstance() // 无参数的初始化 { return CAutoSmartPtr(new TFollowClass); } // 加上几个 template<typename A1> static CAutoSmartPtr CreateInstance(A1 a1) // 参数初始化 { return CAutoSmartPtr(new TFollowClass(a1)); } template<typename A1, typename A2> static CAutoSmartPtr CreateInstance(A1 a1, A2 a2) { return CAutoSmartPtr(new TFollowClass(a1, a2)); } template<typename A1, typename A2, typename A3> static CAutoSmartPtr CreateInstance(A1 a1, A2 a2, A3 a3) { return CAutoSmartPtr(new TFollowClass(a1, a2, a3)); } template<typename A1, typename A2, typename A3, typename A4> static CAutoSmartPtr CreateInstance(A1 a1, A2 a2, A3 a3, A4 a4 ) { return CAutoSmartPtr(new TFollowClass(a1, a2, a3, a4)); } template<typename A1, typename A2, typename A3, typename A4, typename A5> static CAutoSmartPtr CreateInstance(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { return CAutoSmartPtr(new TFollowClass(a1, a2, a3, a4, a5)); } CAutoSmartPtr & operator = (CAutoSmartPtr & src) { ptrrelease(); pshareptr = src.pshareptr; ptraddref(); return *this; } CAutoSmartPtr & operator = (void *pSrc) { ptrrelease(); pshareptr = static_cast<TFollowClass *>(pSrc); if(pshareptr) pshareptr->addref(); return *this; } CAutoSmartPtr(T * pSrc):pshareptr(static_cast<TFollowClass *>(pSrc)) { if(pshareptr) pshareptr->addref(); } void CopyTo(void ** pp) { *pp = (void*)pshareptr; if(pshareptr) pshareptr->addref(); } operator bool () { return !!pshareptr->p; } void ** operator &() { return (void**)&pshareptr; } T& operator * (){return *(static_cast<T*>(pshareptr));} operator T*(){return static_cast<T*>(pshareptr);} T* operator ->(){return static_cast<T*>(pshareptr);} bool operator !(){return (pshareptr == NULL);} bool operator <(T*pT){return static_cast<T*>(pshareptr) < pT;} bool operator >(T*pT){return static_cast<T*>(pshareptr) > pT;} bool operator != (T*pT) {return static_cast<T*>(pshareptr) != pT;} bool operator == (T*pT) {return static_cast<T*>(pshareptr) == pT;} };
测试
CAutoSmartPtr<CTestClass> aspTest = CAutoSmartPtr<CTestClass>::CreateInstance(); CTestClass * pClass = aspTest; // pClass->Do(_T("class test")); CAutoSmartPtr<CTestClass> aspTest2 = aspTest; // aspTest2->Do(_T("aspTest2->Do")); struct foo { static void run(CTestClass * ptc, void ** pp) { CAutoSmartPtr<CTestClass> ssptc(ptc); ssptc->Do(_T("run")); CAutoSmartPtr<CTestClass2> ss2 = CAutoSmartPtr<CTestClass2>::CreateInstance(1000); ss2.CopyTo(pp); } }; CAutoSmartPtr<CTestClass2> asp2; foo::run(aspTest2, &asp2); // // // CAutoSmartPtr<CTestClass2> aspTest3 = CAutoSmartPtr<CTestClass2>::CreateInstance(300); aspTest3->Do(_T("aspTest3->Do")); CAutoSmartPtr<CTestClass2> aspTest4 = CAutoSmartPtr<CTestClass2>::CreateInstance(40);
输出
CTestClass::CTestClass CTestClass::Do class test CTestClass::Do aspTest2->Do CTestClass::Do run CTestClass2::CTestClass2 CTestClass2::CTestClass2 CTestClass2::Do aspTest3->Do CTestClass2::CTestClass2 CTestClass2::~CTestClass2 40 CTestClass2::~CTestClass2 300 CTestClass2::~CTestClass2 1000 CTestClass::~CTestClass