Chinaunix首页 | 论坛 | 博客
  • 博客访问: 64465
  • 博文数量: 32
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 255
  • 用 户 组: 普通用户
  • 注册时间: 2016-12-02 09:11
文章分类

全部博文(32)

文章存档

2017年(21)

2016年(11)

我的朋友

分类: C/C++

2017-01-13 14:54:09

转自:http://blog.csdn.net/u013696062/article/details/39665247 
      Share_ptr也是一种智能指针。类比于auto_ptr学习。所以推荐先学习auto_ptr,再来学习shared_ptr。本博客的前两个就是auto_ptr的总结。希望感兴趣的朋友可以看看。
       Shared_ptr和auto_ptr最大的区别就是,shared_ptr解决了指针间共享对象所有权的问题,也就是auto_ptr中的赋值的奇怪问题。所以满足了容器的要求,可以用于容器中。而auto_ptr显然禁止共享对象所有权,不可以用于容器中。
  1. int * a=new int(2);
  2. shared_ptr<int> sp1(a);
  3. shared_ptr<int> sp2(sp1); // OK
        当然shared_ptr作为一种智能指针,也拥有和shared_ptr一些相似的性质。它们本质上都是类,但是使用起来像指针。它们都是为了解决防止内存泄漏的解决方案。都是运用了RAII技术来实现的。
注意:使用shared_ptr也要引用头文件#include
由于shared_ptr的源码过于复杂,就不给出源码。类比于auto_ptr学习.

1. 首先类shared_ptr有两个成员变量。T * px和unsign long * pn;
    T * px;显然和auto_ptr一样,用于储存对象的指针。
    pn用于记录有多少个shared_ptr拥有同一个对象。pn是shared_ptr对象间共享的,类似于static成员变量。
  1. template<class T>
  2. class shared_ptr{
  3. private:
  4.        T *px; // contained pointer
  5.     unsignedlong* pn; // reference counter
  6. }
        总结:其实shared_ptr的原理,就是使用px来记录指针,使用*pn来记录px指向的对象的拥有者share_ptr的个数,当一个shared_ptr对象达到作用域时,不会释放资源,只有当*pn变为0的时候,才会释放指针指向的资源

2.一个简单实现的源码
  1. #pragma once
  2. //shared_ptr的简单实现版本
  3. //基于引用记数的智能指针
  4. //它可以和stl容器完美的配合
  5. namespace boost
  6. {
  7. template<class T>
  8. class shared_ptr
  9. {
  10. typedef unsigned longsize_type;
  11. private:
  12.        T *px; // contained pointer
  13.    size_type* pn; // reference counter
  14. public:
  15. //构造函数---------------------------------------------------2
  16. /*
  17. int* a=new int(2);
  18. shared_ptr<int> sp;
  19. shared_ptr<int> sp(a);
  20. */
  21. explicitshared_ptr(T* p=0) : px(p)
  22. {
  23.    pn = new size_type(1);
  24. }
  25.    
  26. /*
  27. Derived d;
  28. shared_ptr<Base> ap(d);
  29. */
  30. template<typename Y>
  31. shared_ptr(Y* py)
  32. {
  33. pn = newsize_type(1);
  34. px=py;
  35. }
  36. //copy构造函数------------------------------------------------
  37. /*
  38. int * a=new int;
  39. shared_ptr<int> sp(a);
  40. shared_ptr<int> sp1(sp);
  41. */
  42. shared_ptr(constshared_ptr& r) throw(): px(r.px)
  43. {
  44. ++*r.pn;
  45. pn = r.pn;
  46. }
  47.    
  48. /*
  49. shared_ptr<Derived>sp1(derived);
  50. shared_ptr<Base> sp2(sp1);
  51. */
  52. template<typename Y>
  53. shared_ptr(constshared_ptr<Y>& r)//用于多态
  54. {
  55. px = r.px;
  56. ++*r.pn;
  57. pn = r.pn; //shared_count::op= doesn't throw
  58. }
  59. //重载赋值operator=--------------------------------------------
  60. shared_ptr& operator=(const shared_ptr& r) throw()
  61. {
  62. if(this== &r) return *this;
  63. dispose();
  64. px = r.px;
  65. ++*r.pn;
  66. pn = r.pn;
  67. return *this;
  68. }
  69. template<typename Y>
  70. shared_ptr& operator=(const shared_ptr<Y>& r)//用于多态
  71. {
  72. dispose();
  73. px = r.px;
  74. ++*r.pn;
  75. pn = r.pn; //shared_count::op= doesn't throw
  76. return *this;
  77. }
  78.    
  79. ~shared_ptr() { dispose(); }
  80. void reset(T* p=0)
  81. {
  82. if ( px == p ) return;
  83. if (--*pn == 0)
  84. { delete(px); }
  85. else
  86. { // allocate newreference
  87. // counter
  88. // fix: prevent leak if new throws
  89. try { pn = new size_type; }
  90. catch (...) {
  91. // undo effect of —*pn above to
  92. // meet effects guarantee
  93. ++*pn;
  94. delete(p);
  95. throw;
  96. } // catch
  97. } // allocate newreference counter
  98. *pn = 1;
  99. px = p;
  100. } // reset
  101. reference operator*()const throw(){ return *px; }
  102. pointer operator->()const throw(){ return px; }
  103. pointer get() constthrow(){ returnpx; }
  104. size_type use_count() constthrow()//
  105. { return *pn; }
  106. bool unique() const throw()//
  107. { return *pn ==1; }
  108. private:
  109. void dispose() throw()
  110. {
  111. if (--*pn == 0)
  112. { delete px; delete pn; }
  113. }
  114. }; // shared_ptr
  115. template<typename A,typenameB>
  116. inline bool operator==(shared_ptr<A>const & l, shared_ptr<B> const & r)
  117. {
  118. return l.get() == r.get();
  119. }
  120. template<typename A,typenameB>
  121. inline bool operator!=(shared_ptr<A>const & l, shared_ptr<B> const & r)
  122. {
  123. return l.get() != r.get();
  124. }
  125. }//namespace boost

要注意的地方
3. Shared_ptr和auto_ptr都有类似的规定:
看看它们的copy构造和重载赋值都可以看出:
不允许使用一个纯指针给一个智能指针赋值或copy构造。
  1. int* a=new int(2);
  2. shared_ptr<int>sp=a;// error
  3. sp=a;// error
只能使用智能指针给另一个智能指针赋值或copy构造。
  1. int* a=new int(2);
  2. hared_ptr<int> sp(a);//构造函数
  3. shared_ptr<int> sp1(sp);//copy构造
  4.    sp1=sp;//赋值
在auto_ptr中也是相同的。

4. 注意shared_ptr的几个函数:
①   Reset()函数:重置函数
标准中的是:
  1. int* a=new int(2);
  2. int* b=new int(3);
  3. shared_ptr<int> sp2(a);
  4. shared_ptr<int> sp1(a);
  5. shared_ptr<int> sp(a);
  6. sp.reset(b);
  7. sp.reset();
  8. sp.reset(sp2); -----!!!也是可以的。
使得sp获得b的拥有权。失去a的拥有权。注意这会使得a的拥有者少1.当a的拥有者变为0时,就会释放a的资源。
②   Swap()函数:交换函数
  1. int* a=new int(2);
  2. shared_ptr<int> sp(a);
  3. shared_ptr<int> sp1(a);
  4. sp.swap(sp1);
就是两个shared_ptr中的px和pn都互换一下。
③     Get()函数:返回px
④     Use_count函数:返回*pn,就是对象的拥有者的数量。
⑤    Unique函数:令*pn=1;让对象的拥有者的数量变为1。返回bool
⑥   同时share_ptr也重载了*和->

5. tr1中重载了几个有关shared_ptr的符号:
  1. template<classT, class U>
  2. booloperator==(shared_ptr<T> const& a, shared_ptr<U> const& b);
判断拥有的对象是否是一样的
  1. template<classT, class U>
  2. bool operator!=(shared_ptr<T> const&a, shared_ptr<U> const& b);
判断拥有的对象是否是不一样的
  1. template<classT, class U>
  2. bool operator<(shared_ptr<T>const& a, shared_ptr<U> const& b);
重载了小于号,在STL中的LIST中非常有用。
  1. int* a=new int(2);
  2. int* b=new int(3);
  3. shared_ptr<int> sp(a);
  4. shared_ptr<int> sp1(b);
  5. if(sp<sp1)
  6.        cout<<"2222"<<endl;

6. 注意真实中shared_ptr中没有public dispose这个函数,这里只是为了避免代码重复。

7. 注意shared_ptr中的析构函数中不是直接释放资源,而是调用了dispose函数,如果*pn==0了,才会释放资源。

8.shared_ptr的多线程的安全性
        shared_ptr 本身不是 100%线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为shared_ptr有两个数据成员,读写操作不能原子化。根据文档,shared_ptr的线程安全级别和内建类型、标准库容器、string一样,即:
一个 shared_ptr 实体可被多个线程同时读取;
两个的 shared_ptr 实体可以被两个线程同时写入,“析构”算写操作;
如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。

两个非常有意思的东西:
1. 源码中发现两个这样的东西:
template shared_ptr(Y * p, D d);
template void reset(Y * p, D d);
        其中的D d是个什么东西?源码的解释是d是一个deleter(删除器)。至此我们突然发现我们可以给shared_ptr指定一个删除器,当*pn==0的时候,不去释放资源,而去调用我们自己给它的删除器。
        当shared_ptr的引用次数为0的时候,share_ptr就会调用释放函数来释放资源。
        当我们希望引用次数为0的时候,shared_ptr不释放资源,而是调用我们指定的操作的时候,就会用到D d;
  1. void foo(int * d)
  2. {
  3.        cout<<"1234"<<endl;
  4. }
  5.    
  6. int _tmain(int argc, _TCHAR* argv[])
  7. {
  8.        int* a=new int(2);
  9.        shared_ptr<int> sp(a,foo);
  10.        shared_ptr<int> sp1(sp);
  11.        sp.reset();
  12.        sp1.reset();
  13.        //_CrtDumpMemoryLeaks();
  14.        system("pause");
  15.        return 0;
  16. }
注意!:
①指定的删除器的参数必须是int*;和shared_ptr中的int对应。不能是其他的,或者为空也是错的。因为系统会把shared_ptr的对象px赋给删除器的参数,我们也可以在删除器中释放资源。
②只有a的引用次数为0才会调用,所以如果没有sp1.reset()。也不会调用foo函数。

2. 使用shared_ptr的时候,要小心,想一想操作的内在含义才去做。
1>
  1. int* a=new int(2);
  2. shared_ptr<int> sp(a);
  3. shared_ptr<int> sp1(sp);
  4. sp.reset();//--------(1)
  5. sp.reset();//--------(2)
这里(1)是重置了sp,注意(2)是没有任何作用的,不能使得a的引用次数变为0.想一想reset的函数内部,(2)的时候,sp中的对象pn已经为空了,则不能改变*pn的值了。
2>
  1. int* a=new int(2);
  2.   shared_ptr<int> sp(a);//----------(1)
  3.   shared_ptr<int> sp1(a);//---------(2)
注意:这里的(2)也是不对的。想一想shared_ptr的构造函数,(1)的时候,sp的px指向a,且*pn为1.而(2)的时候,px指向a,且*pn也是1.这显然就问题了。a被引用了2次,但是*pn为1.在最后作用域达到的时候,就会释放2次内存,这就会引发异常。
总结:shared_ptr和auto_ptr的区别。
Shared_ptr有两个变量,一个记录对象地址,一个记录引用次数
Auto_ptr只有一个变量,用来记录对象地址
Shared_ptr可用多个shared_ptr拥有一个资源。
Auto_ptr只能一个auto_ptr拥有一个资源
Shared_ptr可以实现赋值的正常操作,使得两个地址指向同一资源
Auto_ptr的赋值很奇怪,源失去资源拥有权,目标获取资源拥有权
Shared_ptr到达作用域时,不一定会释放资源。
Auto_ptr到达作用域时,一定会释放资源。
Shared_ptr存在多线程的安全性问题,而auto_ptr没有。
Shared_ptr可用于容器中,而auto_ptr一般不可以用于容器中。
Shared_ptr可以在构造函数、reset函数的时候允许指定删除器。而auto_ptr不能。
        还有这里说一句:使用智能指针(不管shared_ptr还是auto_ptr),都要清楚源码内部的实现原理,使用起来才不会错。而且使用的时候,一定要想一想函数内部的实现原理再去使用。切记小心。


阅读(801) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~