【转载】智能指针的原理及实现

智能指针的原理及实现

智能指针

1 智能指针的作用

智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源

2 智能指针的种类

shared_ptr、unique_ptr、weak_ptr、auto_ptr

(1) shared_ptr

实现原理:采用引用计数器的方法,允许多个智能指针指向同一个对象,每当多一个指针指向该对象时,指向该对象的所有智能指针内部的引用计数加1,每当减少一个智能指针指向对象时,引用计数会减1,当计数为0的时候会自动的释放动态分配的资源。

  1. 智能指针将一个计数器与类指向的对象相关联,引用计数器跟踪共有多少个类对象共享同一指针;

  2. 每次创建类的新对象时,初始化指针并将引用计数置为1;

  3. 当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;

  4. 对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;

  5. 调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。

(2)unique_ptr

   unique\_ptr采用的是独享所有权语义,一个非空的unique\_ptr总是拥有它所指向的资源。转移一个unique\_ptr将会把所有权全部从源指针转移给目标指针,源指针被置空;所以unique\_ptr不支持普通的拷贝和赋值操作,不能用在STL标准容器中;局部变量的返回值除外(因为编译器知道要返回的对象将要被销毁);如果你拷贝一个unique\_ptr,那么拷贝结束后,这两个unique\_ptr都会指向相同的资源,造成在结束时对同一内存指针多次释放而导致程序崩溃。

(3)weak_ptr

  weak\_ptr:弱引用。 引用计数有一个问题就是互相引用形成环(环形引用),这样两个指针指向的内存都无法释放。需要使用weak\_ptr打破环形引用。**weak\_ptr**是一个弱引用,它是为了配合shared\_pt而引入的一种智能指针,它指向一个由shared\_ptr管理的对象而不影响所指对象的生命周期,也就是说,它只引用,不计数。如果一块内存被shared\_ptr和weak\_ptr同时引用,当所有shared\_ptr析构了之后,不管还有没有weak\_ptr引用该内存,内存也会被释放。所以weak\_ptr不保证它指向的内存一定是有效的,在使用之前使用函数lock()检查weak\_ptr是否为空指针。

(4)auto_ptr

auto_ptr不支持拷贝和赋值操作,不能用在STL标准容器中。STL容器中的元素经常要支持拷贝、赋值操作,在这过程中auto_ptr会传递所有权,auto_ptr采用的是独享所有权语义,一个非空的unique_ptr总是拥有它所指向的资源。转移一个auto_ptr将会把所有权全部从源指针转移给目标指针,源指针被置空。

智能指针代码实现:用两个类来实现智能指针的功能,一个是引用计数类,另一个则是指针类。

(1) 引用计数类

`

1.  //  引用计数器类  用于存储指向同一对象的指针数
    
2.  template<typename T>
    
3.  class Counter
    
4.  {
    
5.  private:
    
6.  	//  数据成员
    
7.  	T *ptr;    //  对象指针
    
8.  	int cnt;   //  引用计数器
    

10.  	//  友元类声明
    
11.  	template<typename T>
    
12.  	friend class SmartPtr;
    

14.  	//  成员函数
    
15.  	//  构造函数
    
16.  	Counter(T *p)   //  p为指向动态分配对象的指针
    
17.  	{
    
18.  		ptr = p;
    
19.  		cnt = 1;
    
20.  	}
    
21.  	//  析构函数
    
22.  	~Counter()
    
23.  	{
    
24.  		delete ptr;
    
25.  	}
    
26.  };
    

`


(2) 指针类

`

1.  //  智能指针类 
    
2.  template<typename T>
    
3.  class SmartPtr
    
4.  {
    
5.  private:
    
6.  	//  数据成员
    
7.  	Counter<T> *ptr_cnt;  // 
    

9.  public:
    

11.  	//  成员函数
    
12.  	//  普通构造函数  初始化计数类
    
13.  	SmartPtr(T *p)
    
14.  	{
    
15.  		ptr_cnt = new Counter<T>(p);
    
16.  	}
    
17.  	//  拷贝构造函数
    
18.  	SmartPtr(const SmartPtr &other)
    
19.  	{
    
20.  		ptr_cnt = other.ptr_cnt;
    
21.  		ptr_cnt->cnt++;
    
22.  	}
    
23.  	//  赋值运算符重载函数
    
24.  	SmartPtr &operator=(const SmartPtr &rhs)
    
25.  	{
    
26.  		ptr_cnt = rhs->ptr_cnt;
    
27.  		rhs.ptr_cnt->cnt++;
    
28.  		ptr_cnt->cnt--;
    
29.  		if (ptr_cnt->cnt == 0)
    
30.  			delete ptr_cnt;
    
31.  		return *this;
    
32.  	}
    
33.  	//  解引用运算符重载函数
    
34.  	T &operator*()
    
35.  	{
    
36.  		return *(ptr_cnt->cnt);
    
37.  	}
    

39.  	//  析构函数
    
40.  	~SmartPtr()
    
41.  	{
    
42.  		ptr_cnt->cnt--;
    
43.  		if (ptr_cnt->cnt == 0)
    
44.  			delete ptr_cnt;
    
45.  		else
    
46.  			cout << "还有" << ptr_cnt->cnt << "个指针指向基础对象" << endl;
    
47.  	}
    
48.  };
    

`

![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)


(3) 使用与测试

`

1.  #include<iostream>
    

3.  using namespace std;
    

6.  //  测试函数
    
7.  void test()
    
8.  {
    
9.  	int *p = new int(42);
    
10.  	{
    
11.  		SmartPtr<int> sptr1(p);  //  出了作用域,计数器减1
    
12.  		{
    
13.  			SmartPtr<int> sptr2(sptr1);  //  出了作用域,计数器减1
    
14.  			{
    
15.  				SmartPtr<int> sptr3(sptr1);  //  出了作用域,计数器减1
    
16.  			}
    
17.  		}
    
18.  	}
    
19.  	cout << *p << endl;  //  动态分配的对象已被释放,故输出垃圾值
    
20.  }
    

22.  //  主函数
    
23.  int main()
    
24.  {
    
25.  	test();
    
26.  	return 0;
    
27.  }

运行结果如下:
image

  如图所示,在离开大括号后,共享基础对象的指针计数从3->2->1->0变换,最后计数为0时,指针p指向的动态分配对象被delete,此时使用\*p已经获取不到原来的值,故而产生垃圾值。

原文智能指针的原理及实现_智能指针实现原理-CSDN博客