{
delete m_pHolder;
}
}
CRefIPtr& operator = (const CRefIPtr& ref)
{
if(this != &ref
&& m_pHolder != ref.m_pHolder)
{
if(m_pHolder != NULL
&& m_pHolder->SubRefCount() == 0)
{
delete m_pHolder;
}
m_pHolder = ref.m_pHolder;
if(m_pHolder != NULL)
{
m_pHolder->AddRefCount();
}
}
return *this;
}
bool operator == (const CRefIPtr& ref) const
{
return m_pHolder == ref.m_pHolder;
}
protected:
T& GetObject() const
{
assert(m_pHolder != NULL
&& m_pHolder->m_pRawObj != NULL);
return *(m_pHolder->m_pRawObj);
}
T* GetObjectPtr() const
{
if(m_pHolder != NULL)
{
return m_pHolder->m_pRawObj;
}
else
{
return NULL;
}
}
class CRefHolder: public CRefObject
{
public:
CRefHolder()
{
m_pRawObj = NULL;
}
~CRefHolder()
{
delete m_pRawObj;
}
T* m_pRawObj;
};
private:
CRefHolder* m_pHolder;
};
可以看到在外置的方法中我们内部封装了一个具有计数功能的CRefHolder, 通过它实现我们的计数功能, 具体用法和上面CRefPtr类似,只不过CRefIPtr不再强制要求对象从CRefObject继承。
下面我们来讨论这2种方法的优缺点:
(1)从性能上来说,肯定内置的高,因为它不用通过新建内部Holder对象。
(2) 从易用性上来说, 外置的更方便,因为它不强制要求对象从CRefObject继承。
(3) 从使用范围上说, 外置的更广阔, 因为外置的方法支持C++ 内置类型也很方便, 比如CRefIPtr
但是外置的比内置在使用不当的情况下,有时更容易出错,比如下面的代码:
int main(int argc, char* argv[])
{
{
CRefPtr
CTest* pRaw = p1.GetPtr();
CRefPtr
}
system("pause");
return 0;
}
用CRefPtr运行正常,但是改成CRefIPtr时,却会Crash。
究其原因是在内置情况下我们可以知道原始对象内部的计数值,但是外置情况下就无能为力了。
当然上面的用法本身就是不规范的,就像你这样用:
int main(int argc, char* argv[])
{
{
CTest t(1);
CRefPtr
}
system("pause");
return 0;
}
上面代码,无论用内置还是外置,都会Crash。
当然,我们上面的2种引用计数智能指针在实现上都没有考虑多线程的情况,多线程情况只要给CRefObject加锁就可以了。
基于引用计数智能指针还有一个致命的缺点就是循环引用,会造成对象没法自动释放,这种情况下需要我们在需要释放对象时手动将指针值设成NULL。
总之,如果我们要在正式项目中使用这种方式的智能指针,使用者要对它的内部机制有深入的理解,同时建议不要同时混用智能指针和原始指针,另外建议只在模块内部使用,而不要跨模块传递智能指针。
上面是我对引用计数智能指针的一些理解和看法,如果有不正确的地方,欢迎指正。
摘自 厚积薄发