关于智能指针

个人主页:Lei宝啊 

愿所有美好如期而遇


智能指针是什么?

智能指针(smart pointer)是一种用于自动管理动态分配内存的指针(其实也就是个类,在类中去使用一个指针管理资源)。

智能指针有多种实现方式,如:auto_ptr(狗都不用),unique_ptr,shared_ptr,weak_ptr

剩下的不再列出,使用他们需要包含头文件memory。

为什么需要智能指针?

为了解决内存泄漏的问题,你可能会说,小心一些,像C语言那样不就好了吗?C++因为引入了异常,所以在下面这种情况下没有智能指针就会出现内存泄漏:

int* a = new int;
int* b = new int;

//如果这个函数抛异常,被其他地方捕获,那么资源泄漏
//又或者在b处new失败,抛异常在其他地方异常被捕获,那么资源也会泄漏
func();

delete a;
delete b;

 所以我们需要智能指针来替我们管理资源,这样,一但出了这个作用域,就会析构释放资源。

智能指针是怎么实现的?

智能指针需要满足两个条件:

  1. RAIIResource Acquisition Is Initialization),也可以叫做资源获得立即初始化,利用对象生命周期来控制程序资源。
  2. 像指针一样

我们按照上面给出的几个智能指针挨个实现:

auto_ptr

这种智能指针比较粗暴,拷贝构造和赋值运算符重载就干一件事:一个资源只能被一个智能指针管着,如果没有智能指针管,那么释放资源。

拷贝构造:新智能指针去管,旧智能指针置空。

赋值重载:被赋值的智能指针释放掉他原来管着的资源,去管理赋值的智能指针管理的资源,并且这个智能指针置空。

析构:释放管理的资源。

template<class T>
class auto_ptr
{
public:

	//RAII 资源获得立即初始化
	auto_ptr(T* ptr)
		:_ptr(ptr)
	{}

	auto_ptr(auto_ptr& ptr)
	{
		_ptr = ptr._ptr;
		ptr._ptr = nullptr;
	}

	auto_ptr& operator=(auto_ptr& ptr)
	{
		if (this != &ptr)
		{
			if (_ptr)
				delete _ptr;

			_ptr = ptr._ptr;
			ptr._ptr = nullptr;
		}		

		return *this;
	}

	~auto_ptr()
	{
		if (_ptr)
		{
			cout << "~auto_ptr" << endl;
			delete _ptr;
		}		
	}

	//像指针一样
	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

unique_ptr

这个也比较粗暴,直接禁止拷贝与赋值,删除了他们的成员函数。

template<class T>
class unique_ptr
{
public:

	//RAII 资源获得立即初始化
	unique_ptr(T* ptr)
		:_ptr(ptr)
	{}

	unique_ptr(unique_ptr& ptr) = delete;
	unique_ptr& operator=(unique_ptr& ptr) = delete;
	
	~unique_ptr()
	{
		if (_ptr)
		{
			cout << "~unique_ptr" << endl;
			delete _ptr;
		}
	}

	//像指针一样
	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

shared_ptr

但是如果要使用智能指针,而且一定要进行拷贝,那么前两种智能指针都不是好选择,这时我们就可以使用这个智能指针。

这个智能指针增加了一个引用计数,但是我们需要注意的是,同一个资源使用一个引用计数,不同资源使用的是不同的引用计数,也就是说,我们不能简单的就使用静态成员变量,这样是行不通的。

所以我们使用一个int*的指针,在构造时将他指向的值初始化为1,在拷贝构造时,新对象的int*指针同步被拷贝对象,并将值++;赋值操作时,如果被赋值对象引用计数为1,那么不仅*(int*)的值需要--,并且这个对象指向的资源还需要析构,之后的操作同拷贝构造。

并且,我们一定要注意的是,不能自己给自己赋值,因为当被赋值对象引用计数为1时,析构之后资源被释放,然后再进行对成员变量的操作时,那就坑了,因为这些资源已经被释放了。

template<class T>
class shared_ptr
{
public:


	//RAII 资源获得立即初始化
	shared_ptr(T* ptr = nullptr)
		:_ptr(ptr)
		,_count(new int(1))
	{}

	shared_ptr(shared_ptr& ptr)
	{		
		_ptr = ptr._ptr;
		_count = ptr._count;

		(*_count)++;
	}

	shared_ptr& operator=(shared_ptr& ptr)
	{
		if (_ptr != ptr._ptr)
		{
			release();

			_ptr = ptr._ptr;
			_count = ptr._count;

			(*_count)++;
		}

		return *this;
	}

	void release()
	{
		if (--(*_count) == 0)
		{
			cout << "~shared_ptr" << endl;
			delete _ptr;
			delete _count;
		}
	}

	~shared_ptr()
	{
		release();
	}

	//像指针一样
	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	int count()
	{
		return *_count;
	}

	T* get() const
	{
		return _ptr;
	}

private:
	T* _ptr;
	int* _count;
};

weak_ptr 

我们这里需要提到,shared_ptr有循环引用的问题:

struct ListNode
{
     int _data;
     shared_ptr<ListNode> _prev;
     shared_ptr<ListNode> _next;
     ~ListNode(){ cout << "~ListNode()" << endl; }
};

int main()
{
     shared_ptr<ListNode> node1(new ListNode);
     shared_ptr<ListNode> node2(new ListNode);

     node1->_next = node2;
     node2->_prev = node1;

     return 0;
}

 


	void release()
	{
		if (--(*_count) == 0)
		{
			cout << "~shared_ptr" << endl;
			delete _ptr;
			delete _count;
		}
	}

	~shared_ptr()
	{
		release();
	}

node2先进行析构,引用计数--,_count == 1, 什么也没发生:

接着就是node1进行析构,引用计数--,_count == 1, 同样什么也没有发生,也就导致,他们的资源都没有释放,并且他们的成员变量也没有调用析构函数,造成了资源泄漏。

所以这里我们引入了weak_ptr,这个智能指针只是单纯的指向资源,不对资源做释放。

template<class T>
class weak_ptr
{
public:

	//RAII 资源获得立即初始化
	weak_ptr()
		:_ptr(nullptr)
	{}

	weak_ptr(const shared_ptr<T>& ptr)
	{
		_ptr = ptr.get();
	}

	weak_ptr& operator=(const shared_ptr<T>& ptr)
	{
		_ptr = ptr.get();
		
		return *this;
	}

	//error
	/*~weak_ptr()
	{
		cout << "~weak_ptr" << endl;

		if (_ptr)						
			delete _ptr;					
	}*/

	//像指针一样
	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

所以我们可以修改成这样:

struct ListNode
{
     int _data;
     weak_ptr<ListNode> _prev;
     weak_ptr<ListNode> _next;
     ~ListNode(){ cout << "~ListNode()" << endl; }
};

int main()
{
     shared_ptr<ListNode> node1(new ListNode);
     shared_ptr<ListNode> node2(new ListNode);

     node1->_next = node2;
     node2->_prev = node1;

     return 0;
}

这样就不会有循环引用的问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lei宝啊

觉得博主写的有用就鼓励一下吧

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

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

打赏作者

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

抵扣说明:

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

余额充值