c++智能指针(raii)

目录

1.智能指针的作用

2.智能指针带来的问题与挑战

 3.三种不同的智能指针

4.auto_ptr

5.unique_ptr

6.shared_ptr

7.weak_ptr;相互引用

8.总结


1.智能指针的作用

以c++的异常处理为例看看throw catch用法。有时,一个用new开出的空间用完还没delete呢。突然throw一个错误,程序就跑没了,造成了内存泄漏。为了解决这个问题,引入了智能指针的概念,作用是为了让这块空间可以自动释放。智能指针实际就是用一个类来管理这块空间,利用析构函数来释放空间。同时利用重载-> = *来实现一般指针相同的功能。 


2.智能指针带来的问题与挑战

因为是用一个类来进行管理,那么拷贝怎么办?我们都知道普通指针是采用的浅拷贝的方式,智能指针也得有普通指针的功能啊。但是,若是简单的完成浅拷贝就必然会导致同一块空间多次释放的问题。看图:

ap1和ap2都指向了同一块资源,俩再一析构,就全完了。


 3.三种不同的智能指针

a.auto_ptr:直接转移管理权(摆烂做法,不推荐)。

b.unique_ptr:直接不让你拷贝,拷贝就报错。(太粗暴,没有解决问题,也不推荐)。

c.shared_ptr:用引用计数的方式解决了这个问题。(推荐,完美解决问题)。


4.auto_ptr

上菜:

#include<iostream>
#include<string>
#include<map>

using namespace std;
class A
{
public:
	~A()
	{
		cout << "~A true" << endl; //验证析构次数
	}
	int _a=0; //为了下面验证才搞成public的
};
namespace cpp
{
	template<class T>
	class auto_ptr
	{
	private:
		T* _ptr;
	public:
		auto_ptr(T* ptr=nullptr)
			:_ptr(ptr)
		{}
		auto_ptr(auto_ptr<T>& ap) //拷贝构造
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr; 
			//auto_ptr用了非常搞的方式来拷贝,直接将资源管理权给拷贝对象,我摆烂辣!
			//被拷贝的对象直接置空,防止2次析构。
		}

		~auto_ptr()
		{
			if (_ptr) //非空删除空间
			{
				delete _ptr;
				_ptr = nullptr;
			}
		}
		auto_ptr<T>& operator=(auto_ptr<T>& ap)
		{
			if (&ap != this) //要考虑自身赋值的问题
			{
				//与拷贝构造不同的是,先得把自己释放了,才可以拿别人的,防止内存泄漏。
				if (_ptr)
				{
					delete _ptr;
				}
				_ptr = ap._ptr;
				ap._ptr = nullptr; 
			}
			return *this;
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator*()
		{
			return *_ptr;
		}

	};
}

再来看看测试:

int main()
{
	//看看能不能和普通指针一样用
	cpp::auto_ptr<A> ap1 = new(A);
	ap1->_a++;
	cout << ap1->_a << endl;

	//看看拷贝多次析构解决了没
	cpp::auto_ptr<A> ap2(ap1);
	ap2->_a++;
	cout << ap2->_a << endl;
	
	
}

 要是再调用ap1就崩溃,这auto_ptr不太行。


5.unique_ptr

上菜:

template<class T>
	class unique_ptr
	{
	private:
		T* _ptr;
	public:
		unique_ptr(T* ptr)
			:_prt = ptr;
		{}
		unique_ptr(unique_ptr<T>& up)=delete; //用delete关键字不让你用
		unique_ptr<T>& operator=(unique_ptr<T>& up) = delete;
		~unique_ptr()
		{
			if (_ptr) //非空删除空间
			{
				delete _ptr;
				_ptr = nullptr;
			}
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator*()
		{
			return *_ptr;
		}	
	};

这都没啥好试的,直接拷贝用不了。


6.shared_ptr

这么巧妙的shared_ptr得好好说说,我们想到计数引用,估计都会先想到用一个静态(static)的count来计数。但这是搞不定的,每个类都搞一个,无法共享count。所以,我们采用一个共享的空间来存储count。

template<class T>
	class shared_ptr
	{
	private:
		T* _ptr;
		int* _pCount; //用一块共享空间计数
	public:
		shared_ptr(T* ptr=nullptr)
			:_ptr(ptr)
			,_pCount(new int(1)) //默认构造给1
		{}
		shared_ptr(shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pCount(sp._pCount)
		{
			++(*_pCount); //拷贝构造完成记得++count
		}
		shared_ptr<T>& operator=(shared_ptr<T>& sp)
		{
			if (&sp != this) //首先保证不是自己拷贝自己。
			{
				if (--(*sp._pCount) == 0) //当*pCount == 1时,要释放空间。
				{
					delete sp._ptr;
					delete sp._pCount;
				}
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				++(*_pCount);
			}
			return *this;
		}
		~shared_ptr()
		{
			if ((--*_pCount) == 0)
			{
				delete _ptr;
				delete _pCount;
			}
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator*()
		{
			return *_ptr;
		}
	};
}

看看测试:

int main()
{	
	cpp::shared_ptr<A> ap1 = new(A);
	ap1->_a++;
	cpp::shared_ptr<A> ap2(ap1);
	ap2->_a++;
	cpp::shared_ptr<A> ap3 = ap2;
	ap3->_a++;
	ap3 = ap3;

	cout << ap2->_a << endl; //和auto_ptr不同的是完全解决了拷贝问题。
	cout << ap3->_a << endl;

	
}

 完美,以后再也不用担心漏掉delete啦,用完自动就释放。


7.weak_ptr;相互引用

class Node
{
public:
	~Node()
	{
		cout << "~Node true" << endl; //验证析构次数
	}
	int _val = 0;
	shared_ptr<Node> _next = nullptr; //必须用智能指针才能对应上类型
	shared_ptr<Node> _prev = nullptr;
};

int main()
{	
	shared_ptr<Node>sp1 (new(Node));
	shared_ptr<Node>sp2 (new(Node));

	//形成了相互引用
	sp1->_next = sp2; 
	sp2->_prev = sp1;

    cout << sp1.use_count() << endl; //use_count表示引用计数的大小
	cout << sp2.use_count() << endl;
}

 我注释掉一行,结果没问题。

 我去掉注释

就析构不了。

这就是引用计数的缺点,如图:

 两边count都变成了1,就僵住了。左边整个空间想要释放需要右边空间的_prev释放;右边空间的_prev释放需要右边整个空间释放;右边整个空间想要释放需要左边空间的_next释放;左边空间的_next释放需要左边整个空间释放。走边整个空间的释放......(串上了,没完没了了,谁也释放不了)。

这时,我们需要用弱化指针weak_ptr,直接让_next和_prev无法改变空间的引用计数。

shared_ptr是可以接收weak_ptr的。

看图查看差别:

ok,问题解决,直接让他俩count都是1。 


8.总结

不得不说,这c++为了填上没垃圾回收器的坑,搞了一套这么复杂的智能指针出来。有价值的就是shared_ptr啦。智能指针也会带来问题(相互引用)又得用weak_ptr来解决。不过,学会这套机制对于我们理解内存管理有很大的帮助。

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会敲代码的运气选手^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值