Poco::SharedPtr 实现了针对类的引用计数功能,而这些类不需要自己实现引用计数(AutoPtr的duplicate()和release()函数)。
Poco::SharedPtr有着和Poco::AutoPtr相同的解引用以及相关操作。
警告:赋值指向普通对象的指针到不同的Poco::SharedPtr将导致多个拥有者的产生,进而引起未定义的行为,换句话说,就是crash。
一旦你对一个对象使用Poco::SharedPtr,就不要再使用是想那个对象的指针了。
#include "Poco/SharedPtr.h"
#include <string>
#include <iostream>
using Poco::SharedPtr;
int main(int argc, char** argv)
{
std::string* pString = new std::string("hello, world!");
Poco::SharedPtr<std::string> p1(pString); // rc == 1
Poco::SharedPtr<std::string> p2(p1); // rc == 2
p2 = 0; // rc == 1,放弃共享拥有权
// p2 = pString; // BAD BAD BAD: multiple owners -> multiple delete
p2 = p1; // rc == 2
std::string::size_type len = p1->length(); // dereferencing with ->
std::cout << *p1 << std::endl; // dereferencing with *
return 0;
}
// rc == 0 -> deleted
先来看一个容易理解的例子:
有一份对象资源,分别被两个无任何关系的Poco::SharedPtr持有,所以各自的引用计数都是一。而在Poco::SharedPtr内部的析构函数中,会进行这个引用计数值的判断(先减1,再判断是否为0,为0就 delete 持有的资源)。好了,分别析构后,都得到引用计数为0,就产生了两次delete 资源,进而crash。
解决方法就是让其中一个Poco::SharedPtr的引用计数以另一个Poco::SharedPtr引用计数为基础,在其上加1.这样,最终得到0的情况只会发生在一个Poco::SharedPtr身上。
如何建立者两个Poco::SharedPtr的关系呢,那就是拷贝构造函数和=操作符。
SharedPtr(const SharedPtr& ptr): _pCounter(ptr._pCounter), _ptr(ptr._ptr)
{
_pCounter->duplicate();//引用计数加1
}
可以看到指向资源的_ptr都指向同一份资源,且_pCounter的值来源于另一个_pCounter。
SharedPtr& operator = (const SharedPtr& ptr)
{
return assign(ptr);
}
SharedPtr& assign(const SharedPtr& ptr)
{
if (&ptr != this)
{
SharedPtr tmp(ptr);//执行拷贝构造函数,创建临时对象
swap(tmp);
}
return *this;
}
可以看到=操作符的内部产生了一个临时的tmp,通过swap把tmp的_ptr,_pCounter的值交换到this中相应的值中去。
除了管理单个对象,Poco::SharedPtr还可以管理对象数组。
char* arr = new char[100];
SharedPtr<char,Poco::ReferenceCounter,Poco::ReleaseArrayPolicy<char>> ptr(arr);
其实Poco::SharedPtr模板定义是:
template <class C, class RC = ReferenceCounter, class RP = ReleasePolicy<C> >
class SharedPtr
因为第二个和第三个模板参数已经有默认的了,且都有确实实现,所以默认是管理一个对象。当把第三个参数换成内部已经存在的
Poco::ReleaseArrayPolicy<C>
后,从名字也可以看出,它可以管理数组了。
总结一下三个模板参数的意义:
class C:要管理的对象类型
class RC:执行引用计数的实现方法
class RP:实现最后析构被管理的对象的方法
谢谢观赏!