智能指针的原理及实现

智能指针

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_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是说,它只引用,不计数。如果一块内存被shared_ptrweak_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) 引用计数类

//  引用计数器类  用于存储指向同一对象的指针数
template
class Counter
{
private:
	//  数据成员
	T *ptr;    //  对象指针
	int cnt;   //  引用计数器

	//  友元类声明
	template
	friend class SmartPtr;

	//  成员函数
	//  构造函数
	Counter(T *p)   //  p为指向动态分配对象的指针
	{
		ptr = p;
		cnt = 1;
	}
	//  析构函数
	~Counter()
	{
		delete ptr;
	}
};

(2) 指针类

//  智能指针类  
template
class SmartPtr
{
private:
	//  数据成员
	Counter *ptr_cnt;  //  

public:

	//  成员函数
	//  普通构造函数  初始化计数类
	SmartPtr(T *p)
	{
		ptr_cnt = new Counter(p);
	}
	//  拷贝构造函数
	SmartPtr(const SmartPtr &other)
	{
		ptr_cnt = other.ptr_cnt;
		ptr_cnt->cnt++;
	}
	//  赋值运算符重载函数
	SmartPtr &operator=(const SmartPtr &rhs)
	{
		ptr_cnt = rhs->ptr_cnt;
		rhs.ptr_cnt->cnt++;
		ptr_cnt->cnt--;
		if (ptr_cnt->cnt == 0)
			delete ptr_cnt;
		return *this;
	}
	//  解引用运算符重载函数
	T &operator*()
	{
		return *(ptr_cnt->cnt);
	}

	//  析构函数
	~SmartPtr()
	{
		ptr_cnt->cnt--;
		if (ptr_cnt->cnt == 0)
			delete ptr_cnt;
		else
			cout << "还有" << ptr_cnt->cnt << "个指针指向基础对象" << endl;
	}
};

(3) 使用与测试

#include

using namespace std;


//  测试函数
void test()
{
	int *p = new int(42);
	{
		SmartPtr sptr1(p);  //  出了作用域,计数器减1
		{
			SmartPtr sptr2(sptr1);  //  出了作用域,计数器减1
			{
				SmartPtr sptr3(sptr1);  //  出了作用域,计数器减1
			}
		}
	}
	cout << *p << endl;  //  动态分配的对象已被释放,故输出垃圾值
}

//  主函数
int main()
{
	test();
	return 0;
}

运行结果如下:

        智能指针的原理及实现_第1张图片

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

你可能感兴趣的:(C++基础知识)