一、介绍
shared_ptr包装了new操作符在堆上分配的动态对象,但他实现的是计数型的智能指针,shared_ptr早期的名字就是counter_ptr,可以自由拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0时)它才删除被包装的动态分配的对象。shared_ptr也可以安全地放到标准容器中,是在STL容器中存储指针最标准的解法。
template<class T>
class shared_ptr
{
public:
typedef T element_type; //内部类型定义
shared_ptr();
template<class Y> explicit shared_ptr(y*p); //构造函数
template<classY,class D> shared_ptr(Y*p,D d);
~shared_ptr(); //析构函数
shared_ptr(shared_ptr const &r); //拷贝构道
template<class Y> explicit shared_ptr(std::auto_ptr<Y> &r);
shared_ptr & operator=(shared_ptr const &r); //赋值操作
template<class Y> shared_ptr & operator=(shared_ptr<Y> const&r);
template<class Y> shared ptr & operator=(std::autoptr<y> & r);
void reset(); //重置智能指针
template<class Y> void reset(Y*p);
template<class Yclass D> void reset(Y*p, D d);
T & operator*() const; //操作符重载
T * operator->() const; //操作符重载
T * get() const; //获得原始指针
bool unique() const; //是否唯一
long use_count() const; //引用计数
operator bool()const; //bool值转型
void swap(shared_ptr &b); //交换指针
};
shared_ptr与scoped_ptr同样是用于管理new动态分配对象的智能指针,因此功能上有很多相似之处:它们都重载了*和->操作符以模仿原始指针的行为,提供隐式bool类型转换以判断指针的有效性,get可以得到原始指针(return px),并且没有提供指针算数操作。
shared_ptr<int> spi(new int);
assert(spi);
*spi = 253;
shared_ptr<string> sps(new string("smart"));
assert(sps->size() == 5);
shared_ptr有多种形式构造函数,应用于各种可能的情形:
- 无参的shared_ptr()创建一个持有空指针的shared_ptr;
- shared_ptr(Y* p)获得指向类型T的指针p的管理权,同时引用计数置为1.这个构造函数要求Y类型必须能够转换为T类型
- shared_ptr(shared_ptr const & r)从另外一个shared_ptr获得指针的管理权,同时引用计数加1,结果是两个shared_ptr共享一个指针的管理权;
- shared_ptr(std::auto_ptr< Y> &r)从另外一个auto_ptr获得指针的管理权,引用计数置为1,同时auto_ptr自动失去管理权;
- operator=赋值操作符可以从另外一个shared_ptr或auto_ptr获得指针的管理权,其行为同构造函数。
- shared_ptr(Y *p, D d)行为类似shared_ptr(Y *p),但是用参数d指定了析构时定值删除器,而不是简单的delete。
shared_ptr的reset()函数与scoped_ptr也不尽相同。它的作用是将引用计数减1,停止对指针的共享,除非引用计数为0,否则不会发生删除操作。带参数的reset()则是类似相同形式的构造函数,原指针引用计数减1的同时改为管理另一个指针。
shared_ptr有两个专门的函数检查引用计数。unique()检查shared_ptr是指针唯一所有者返回true,use_count()返回当前引用计数。use_count()应该仅用于测试或调试,它不提供高效的操作,而且有可能是不可用的(极少数情形)。
shared_ptr还支持比较运算,可以测试两个shared_ptr的相等或不相等,比较基于内部保存的指针,相当于a.get()==b.get()。shared_ptr还可以使用operator<比较大小。同样基于内部保存的指针,但不提供除operator<之外的比较操作符,这使得shared_ptr可以被用于标准关联容器(set和map)。
shared_ptr的类型转换不能使用static_cast之流,只能使用shared_ptr自己提供的转型函数:static_pointer_cast()、const_pointer_cast()、dynamic_pointer_cast()和reinterpret_cast()函数,它们转型后返回的是shared_ptr类型。
如下使用static_pointer_cast ,dynamic_pointer_cast对bad_exception,std::exception做类型转换赋值。
shared_ptr<std::exception> sp1(new bad_exception("error"));
shared_ptr<bad_exception> sp2 = dynamic_pointer_cast<bad_exception>(sp1);
shared_ptr<std::exception> sp3 = static_pointer_cast<std::exception>(sp2);
此外,shared_ptr还支持流输出操作符operator<<,输出内部指针值,方便调试。
代码实现如下:
template<class T, class U> inline bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b) BOOST_NOEXCEPT
{
return a.owner_before( b );
}
template<class T> inline void swap(shared_ptr<T> & a, shared_ptr<T> & b) BOOST_NOEXCEPT
{
a.swap(b);
}
template<class T, class U> shared_ptr<T> static_pointer_cast( shared_ptr<U> const & r ) BOOST_NOEXCEPT
{
(void) static_cast< T* >( static_cast< U* >( 0 ) );
typedef typename shared_ptr<T>::element_type E;
E * p = static_cast< E* >( r.get() );
return shared_ptr<T>( r, p );
}
template<class T, class U> shared_ptr<T> const_pointer_cast( shared_ptr<U> const & r ) BOOST_NOEXCEPT
{
(void) const_cast< T* >( static_cast< U* >( 0 ) );
typedef typename shared_ptr<T>::element_type E;
E * p = const_cast< E* >( r.get() );
return shared_ptr<T>( r, p );
}
template<class T, class U> shared_ptr<T> dynamic_pointer_cast( shared_ptr<U> const & r ) BOOST_NOEXCEPT
{
(void) dynamic_cast< T* >( static_cast< U* >( 0 ) );
typedef typename shared_ptr<T>::element_type E;
E * p = dynamic_cast< E* >( r.get() );
return p? shared_ptr<T>( r, p ): shared_ptr<T>();
}
template<class T, class U> shared_ptr<T> reinterpret_pointer_cast( shared_ptr<U> const & r ) BOOST_NOEXCEPT
{
(void) reinterpret_cast< T* >( static_cast< U* >( 0 ) );
typedef typename shared_ptr<T>::element_type E;
E * p = reinterpret_cast< E* >( r.get() );
return shared_ptr<T>( r, p );
}
二、用法示例:
shared_ptr<int> sp(new int(10); //一个指向整数的shared ptr
assert(sp.unique)): //现在shared_ptr是指针的唯一持有者
shared_ptr<int> sp2 - sp; //第二个shared_ptr,拷贝构造函数
//两个shared_ptr相等,指向同一个对象,引用计数为 2 assert(sp sp2 .. sp.use count() 2);
*sp2=100; //使用解引用操作符修改被指对象
assert(*sp w*100): //另一个sharedptr也同时被修改
sp.reset(); //停止 shared_ptr 的使用
assert(!sp); //sp不再持有任何指针(空指针)
//第二个例子示范了shared_ptr较复杂的用法:
class shared //一个拥有shared_ptr的类
(
privater:
//shared_ptr 成员变量
shared ptr<int> p;
public:
shared(shared_ptr<int>P_):P(P_){} //构造承数初始化shared_ptr
void print() //输出 shared ptr 的引用计数和指向的值
{
cout <<"count:" << p.use_count()
<<x "v =" <<*p << endl;
}
1;
//使用shared ptr 作为函数参数
void print func(shared ptr<int p)
{
//同样输出 shared_ptr 的引用计数和指向的值
cout << "count;" << p.use_count()
<<" v=" <<*p <<endl;
int main()
{
shared ptr<int>p(new int(100)):
shared s1(p),s2(p); //构造两个自定义类
s1.print();
s2.print();
*p = 20: //修改shared_ptr所指的值
print_func(p);
s1.print();
}
运行结果:
count:3 v=100
count:3 v=100
count:4 v=20
count:3 v=20
这段代码定义了一个类和一个函数,两者都接受share_ptr 对象作为参数,特别注意的是我们没有使用引用的方式传递参数,而是直接拷贝,就像是在使用一个元素指针,,share_ptr支持这样的用法。
在声明了share_ptr和两个shared示例后,指针被他们所共享因此引用计数为3,print_func()函数内部拷贝了一个share_ptr对象,因此引用计数再增加1,当退出函数时拷贝自动析构,引用计数又减为3。
三、工厂函数make_shared
share_ptr提供了一个自由工厂函数make_shared(),来消除显示的new调用,make_shared()函数接受若干个参数,然后把他们传递给类型T的构造函数,创建一个share_ptr的对象并返回。通常make_shared()函数比直接创建share_ptr对象的方式快且高效。因为它内部仅分配一次内存,消除了share_ptr构造时的开销。
int main()
{
shared_ptr<string> sp = make_shared<string>("make_shared");
shared_ptr<vector<int>> spv = make_shared<vector<int> >(10,2);
assert(spv->size() == 10);
}
四、应用于标准容器
有两种方法可以将share_ptr应用于标准容器(或者容器适配器等其他容器),一种方法是将容器作为share_ptr管理的对象,即share_ptr<list>,使容器可以安全地共享。这个比较常用。
另一种是将share_ptr作为容器的元素。如vector<shared_ptr>,因为share_ptr支持拷贝语义和比较操作,符合容器对元素的要求,所以可以在容器中安全地容纳元素的指针而不是拷贝。
#include <boost/make shared.hpp>
int main()
{
typedef vector<shared_ptr<int>> vs; //一个持有shared_ptr 的标准容器类型
vs v(10); //声明一个拥有10个元素的容器。元素被
//初始化为空指针
int i = 0;
for(vs::iterator pos = v.begin():pos != v.end(): ++pos){
(*pos) = make_shared<int>(++i); //使用工厂函数赋值
cout << *(*pos)<<","; //输出值
}
cout << endl;
shared_ptr<int> p = v[9];
*p = 100;
cout <<*v[9] << endl;
}
引用资料:
《Boost程序库完全开发指南–深入C++“准”标准库(第2版)》
作者:罗剑锋