今天学习了boost 的智能指针中的shared_ptr,感觉收获颇多,现在就写下来,一则作为学习笔记,二则与大家分享我的学习心得
shared_ptr智能指针我想大家如果看过《C++Primer》第四版(16.5 A generic handle class 一个泛型句柄)的话,应该了解作者构造了一个通过引用计数的方法来管理指针的,
没错shared_ptr 同样是这样实现的,首先先看一下书中的实现:
看完上面的代码我想你应该了解智能指针是如何工作的了吧,看过书本的可以当做一次复习;
对于shard_ptr的结构如何使用我想我不用做过多介绍,看一下头文件再结合文档和网上的一些资料应该很快就明白
下面我展示一下shared_ptr的一个用法:
#include <iostream>
#include "boost/shared_ptr.hpp"
using namespace std;
using namespace boost;
class A
{
public:
A(){}
virtual void mem_func() = 0;
protected:
virtual ~A()
{
cout<<"A's dector\n";
}
private:
};
class B:public A
{
public:
B(){}
virtual void mem_func()
{
cout<<"I'm mem_fun\n";
}
};
shared_ptr<A> create()
{
return shared_ptr<A>(new B);
}
int main()
{
shared_ptr<A> sp = create();
sp->mem_func();
return 0;
}
请注意,上面的代码,A的析构函数被定义为protect,而它在程序结束时候被调用了,这个就是我要将的地方了,我们通过源码来分析一下
首先shared_ptr在内部有两个私有成员:
T * px; // contained pointer
boost::detail::shared_count pn; // reference counter
其中 px是原生的指针,也就是 new B 得到的指针,但是他是A * 类型的,shared_ptr通过它来快速访问内存,shared_ptr重载* , ->来让用户像使用原生指针一样来使用,
但是在删除这块内存时,shared_ptr不是直接这样 delete px的,因为~A 是protect,这样释放内存会报错,你可是测试一下的代码就知道了
A * pa = new B;
delete pa;
编译器会报错如:
而你这样定义 A a同样也编译不过,但是前面的代码明明调用了基类A 的析构函数 打印出了 A‘ dector,不急,往下看pn 这个变量
pn 这个东东就大有来头了,要完全弄懂,我想我还没那能力,里面涉及到了一些平台相关的知识,我只讲为何会跳用了~A;
其实跟踪pn 可以发现它是:shared_count 类型,它保存一个计数值,也就是如C++Primer中的那个 user ,但是它还保存有一个指针
就是在构造shared_ptr的时候传就去的那个new B,注意是B *类型的指针,px保存的是A* 基类的指针,当pn 的计数值为0的是后就会
delete这个指针,可以看一下shared_ptr的一个构造函数你就会明白它是如何工作的了:
template<class T>
class shared_ptr{
.............
public:
..........
template<class Y>
explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete
{
boost::detail::sp_enable_shared_from_this( this, p, p );
}
...........
private:
T * px; // contained pointer
boost::detail::shared_count pn; // reference counter
};
请注意 在 shared_ptr<A>(new B)中,模板类型 T *是 一个 A * , shared_ptr调用的是一个模板构造函数shared_ptr( Y * p )中的Y* 是 B*,而px(p)
等价于 A* px = B* p ,而对于pn,下面是我从源代码里面复制出来的一个pn的构造构造函数头
template<class Y> explicit shared_count( Y * p ): pi_( 0 )
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
, id_(shared_count_id)
可以看到pn也是一个模板,传递过来的 Y * p 就是 就是 shared_ptr<A>(new B)中的new B 了,当计数器为0是就会析构掉这个B* 指针,所以可以知道
其实px与pn内保持的指针类型是不一样的,px 的是A* 而 pn的是 A 的子类 B 的指针,这样 B* pb = new B; delete pb;是妹问题的。
对于pn 里面复杂的实现我暂时还没能完全理解,有时间再慢慢研究,感兴趣的可以自己去看源代码
全文完!