图解shared_ptr共享智能指针原理分析

Posted kongyijin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图解shared_ptr共享智能指针原理分析相关的知识,希望对你有一定的参考价值。

shared_ptr共享智能指针

  shared_ptr 是 C++ 标准库提供的智能指针类型之一,它用于管理动态分配的资源,并在多个指针之间共享所有权。

 

 

  特点

  • 共享所有权的智能指针。多个shared_ptr指向同一对象
  • 可赋值构造(CopyConstructible)
  • 可复制赋值(CopyAssignable)
  • 数据访问非线程安全
  • shared_ptr 的控制块是线程安全

  应用场景

  1. 共享资源:当多个指针需要共享同一个资源时,可以使用 shared_ptr。这种情况下,shared_ptr 会维护一个引用计数,跟踪有多少个指针指向相同的资源。只有当最后一个 shared_ptr 被销毁时,资源才会被释放。

  2. 动态内存管理:shared_ptr 可以用于管理动态分配的内存。它会自动跟踪资源的引用计数,当没有指针引用该内存时,自动释放内存,避免内存泄漏。

  3. 异常安全性:shared_ptr 可以提供异常安全性。在函数中使用 shared_ptr 管理资源时,即使发生异常,也可以确保资源的正确释放,避免资源泄漏。

  4. 循环引用的处理:在存在循环引用的情况下,使用 shared_ptr 可以解决内存泄漏的问题。shared_ptr 使用强引用计数,只有当引用计数为零时,才会释放资源,因此可以处理循环引用并正确释放相关资源。

  5. STL 容器的元素:shared_ptr 可以作为 STL 容器(如 vectorlistmap 等)中的元素类型,方便地管理容器中的动态分配对象。

    需要注意的是,由于 shared_ptr 维护引用计数,它会带来一定的性能开销。当不需要共享所有权或资源生命周期可以明确控制时,可以考虑其他智能指针类型,如 unique_ptr

 

   

智能指针之 shared_ptr

   std::shared_ptr 是通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可占有同一对象大概实现了一下,主要实现原理为,共享指针内部持有堆资源的指针以及引用计数的指针,通过对这两个指针的维护,达到多个共享对象对同一资源的控制

  实现主要分为三个文件。share_ptr.h,smart_ptr_define.h, main.cpp  (编译平台:Linux centos 7.0 编译器:gcc 4.8.5 )

 1 //smart_ptr_define.h
 2 #ifndef __SMART_PTR_DEFINE_H__
 3 #define __SMART_PTR_DEFINE_H__
 4 
 5 #include <assert.h>
 6 
 7 #define PTR_ASSERT(x) assert(x)
 8 
 9 #define _SMART_PTR_BEGIN    namespace smartptr {
10 #define _SMART_PTR_END        }
11 #define _SMART_PTR    ::smartptr::
12 
13 #endif

  主要实现文件share_ptr.h

  1 #ifndef __SHARE_PTR_H__
  2 #define __SHARE_PTR_H__
  3 
  4 #include <iostream>
  5 #include "smart_ptr_define.h"
  6 
  7 _SMART_PTR_BEGIN
  8 
  9 template <class T>
 10 struct default_deleter
 11 {
 12     void operator()(T* ptr)
 13     {
 14         if (ptr != NULL)
 15         {
 16             delete ptr;
 17             ptr = NULL;
 18         }
 19     }
 20 };
 21 
 22 template <class T, class deleter = default_deleter<T> >
 23 class shared_ptr
 24 {
 25 public:
 26     typedef shared_ptr<T, deleter> SHARE_PTR;
 27 
 28     shared_ptr()
 29     {
 30         m_ptr = NULL;
 31         m_iRefCount = NULL;
 32     }
 33 
 34     explicit shared_ptr(T* ptr)
 35     {
 36         if (ptr != NULL)
 37         {
 38             m_ptr = ptr;
 39             RefCountInit();
 40         }
 41     }
 42 
 43     shared_ptr(deleter d, T* ptr)
 44     {
 45         if (ptr != NULL)
 46         {
 47             m_ptr = ptr;
 48             m_deleter = d;
 49             RefCountInit();
 50         }
 51     }
 52 
 53     //拷贝构造
 54     shared_ptr(const SHARE_PTR& sh_ptr) 
 55     {
 56         if (sh_ptr.m_ptr != NULL)
 57         {
 58             m_ptr = sh_ptr.m_ptr;
 59             m_deleter = sh_ptr.m_deleter;
 60             m_iRefCount = sh_ptr.m_iRefCount;
 61 
 62             RefCountIncrease();
 63         }
 64     }
 65 
 66     //赋值运算符
 67     SHARE_PTR& operator = (const SHARE_PTR& sh_ptr)
 68     {
 69         if (this != &sh_ptr)
 70         {
 71             RefCountDecrease();
 72 
 73             if (sh_ptr.m_ptr != NULL)
 74             {
 75                 m_ptr = sh_ptr.m_ptr;
 76                 m_deleter = sh_ptr.m_deleter;
 77                 m_iRefCount = sh_ptr.m_iRefCount;
 78 
 79                 RefCountIncrease();
 80             }
 81         }
 82 
 83         return (*this);
 84     }
 85 
 86     ~shared_ptr()
 87     {
 88         RefCountDecrease();
 89     }
 90 
 91 public:
 92     //提领操作
 93     T& operator*()
 94     {
 95         PTR_ASSERT(m_ptr != NULL);
 96         return *(m_ptr);
 97     }
 98 
 99     //原始指针操作
100     T* operator->()
101     {
102         PTR_ASSERT(m_ptr != NULL);
103         return m_ptr;
104     }
105 
106     operator bool() const
107     {
108         return m_ptr != NULL;
109     }
110 
111     //取得原始指针
112     T* getPointer()
113     {
114         PTR_ASSERT(m_ptr != NULL);
115         return m_ptr;
116     }
117 
118     //获得引用计数
119     int getRefCount()
120     {
121         PTR_ASSERT(m_iRefCount != NULL);
122         return *m_iRefCount;
123     }
124 
125     
126 private:
127     void RefCountInit()
128     {
129         m_iRefCount = new int(1);
130     }
131 
132     void RefCountIncrease()
133     {
134         if (m_iRefCount != NULL)
135         {
136             ++(*m_iRefCount);
137         }
138     }
139 
140     void RefCountDecrease()
141     {
142         if (m_iRefCount != NULL && --(*m_iRefCount) == 0)
143         {
144             m_deleter(m_ptr);
145             delete m_iRefCount;
146             m_ptr = NULL;
147             m_iRefCount = NULL;
148         }
149     }
150 
151 private:
152     int* m_iRefCount; //引用计数
153 
154     T*  m_ptr; //对象指针
155 
156     deleter m_deleter; //删除器
157 };
158 
159 _SMART_PTR_END
160 #endif // !__SHARE_PTR_H__

  main函数测试

 1 #include "share_ptr.h"
 2 #include <memory>
 3 
 4 class Test
 5 {
 6 public:
 7     Test()
 8     {
 9         std::cout << "construct.." << std::endl;
10     }
11 
12     void method()
13     {
14         std::cout << "welcome Test.." << std::endl;
15     }
16 
17     ~Test()
18     {
19         std::cout << "destruct.." << std::endl;
20     }
21 };
22 
23 int main()
24 {
25     Test* t1 = new Test();
26 
27     _SMART_PTR shared_ptr<Test> shptr(t1);
28 
29     _SMART_PTR shared_ptr<Test> shptr1(shptr);
30 
31     _SMART_PTR shared_ptr<Test> shptr2 = shptr1;
32 
33     std::cout << "RefCount: " << shptr2.getRefCount() << std::endl;
34 
35     shptr2->method();
36 
37     (*shptr2).method();
38 
39     if (shptr2)
40     {
41         std::cout << "ptr is exit " << std::endl;
42     }
43 
44 
45 
46     return 0;
47 }

  测试最后打印:

1 [[email protected] cmake-00]$ ./smartptr 
2 construct..
3 RefCount: 3
4 welcome Test..
5 welcome Test..
6 ptr is exit 
7 destruct..
8 [[email protected] cmake-00]$

  shared_ptr主要需实现的功能点如下(以下总结引用自网络,非原创):

  1. 没有参数构造的时候,初始化为空,即对象和引用计数的两个指针都为0

  2. 使用指针为参数构造时,拥有此指针,在没有智能指针指向它时进行析构

  3. 智能指针复制时,两个智能指针共同拥有内部指针,引用计数同时+1

  4. 智能指针可以使用智能指针或普通指针重新赋值。重载=操作符,对于智能指针赋值,需要考虑是否自赋值,以避免将自身析构了后再重新赋值,而普通指针赋值给智能指针,则不需要考虑自赋值,因为两者本身是两个类型

  5. 获得底层指针的访问,定义getPtrPointer()getPtrCounter()来分别返回底层指针和引用计数,定义operator bool()来处理智能指针隐式转换为bool的情况

  6. 重载->×操作符 ,来实现与普通指针相同的指针访问

  7. 需要支持隐式指针类型转换,static_cast不支持而dynamic_cast支持的转换则使用Cast<T2>()成员函数来解决。考虑定义友元类,以防止指向派生类的智能指针有权限访问基类的内部对象;当转型不成功时,返回为空 (未实现)

  8. 如果一个裸指针直接用来创建两个智能指针的话,期望的情况是当两个智能指针析构掉的时候,该指针会被delete两次从而崩溃(这是shared_ptr的特点)

  9. 不处理循环引用(也是shared_ptr的特点),可以通过与weak_ptr协作来打破循环

  10. 实现deleter机制

 

以上是关于图解shared_ptr共享智能指针原理分析的主要内容,如果未能解决你的问题,请参考以下文章

C++ 智能指针 shared_ptr 分析

c++复习笔记——智能指针详细解析(智能指针的使用,原理分析)

[C++11]shared_ptr共享智能指针的初始化与使用

共享智能指针shared_ptr的实现

第21课 shared_ptr共享型智能指针

智能指针原理及实现- shared_ptr