C++:浅析智能指针

智能指针是为了解决C++里防止程序员因为忘记释放资源而造成内存泄漏的问题

RAII

        RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
        在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此, 我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。

智能指针

一、原理:

  • RAII特性
  • 重载operator*和opertaor->,具有像指针一样的行为

理解:利用一个行为很像指针的对象帮你保管资源,同时当出了对象作用域时,会自动帮你释放,智能指针不是一个真正的指针:而是一个对象,可以使用操控指针的语句来操控这个对象。并且因为智能指针是栈空间上类的对象。所以当程序运行结束后,会自动调用其析构函数自动释放。这样就不会担心在申请资源和释放资源中程序抛异常了。

二、分类:

指针名优点缺点
auto_ptr实现简单不安全,进行复制后会有指针悬空现象
unique_ptr简单粗暴且安全无法拷贝,仅能进行所有权转移
shared_ptr安全,并且能拷贝为了解决循环引用问题,必须借助weak_ptr实现完整功能

      注:C++库中的智能指针都定义在#include <memory>这个头文件中

三、分析:

  • auto_ptr

1.实现原理:将资源管理权进行转移,即将一个指针所指向的空间交给另一个指针。

2.缺陷:管理权一直在变,但只允许有一个指针指向该资源,两个指针就无法指向同一块资源。这样的话当调用拷贝构造和赋值运算符重载的时候,将原来所管理的资源转移给当前对象后,就断开了原ptr与其所管理资源的联系,导致再通过原对象访问资源 时就会出现问题。  

   

3.代码实现:

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr = nullptr)
		:_ptr(ptr)
	{}
	~AutoPtr()
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}

	AutoPtr(AutoPtr<T>& ap)  //拷贝构造
		:_ptr(ap._ptr) //将ap中资源转移到当前对象中
	{
		ap._ptr = nullptr; //令ap与其所管理资源断开联系
	}

	AutoPtr<T>& operator=(AutoPtr<T>& ap) //赋值运算符重载
	{
		if (&this != &ap) //检测是否给自己赋值
		{
			if (_ptr)  //释放当前对象中的资源
				delete _ptr;

			_ptr = ap._ptr;  
			ap._ptr = nullptr;   
		}
		return *this;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};

 

  • unique_ptr (C++11中提供)

1.实现原理:简单粗暴的防拷贝,即不让拷贝和赋值

2.特点:让自己的东西无法被别人获取

3.代码实现:

template<class T>
class UniquePtr
{
public:
	UniquePtr(T* ptr = nullptr)
		:_ptr(ptr)
	{}
	~UniquePtr()
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	//C++98中防拷贝的方式:声明成私有 并且 不进行实现
	//UniquePtr(UniquePtr<T> const &);
	//UniquePtr& operator=(UniquePtr<T>const &);


	//C++11中防拷贝的方式:采用delete来禁止默认创建的函数
	UniquePtr(UniquePtr<T>const &) = delete;
	UniquePtr& operator=(UniquePtr<T>const &) = delete;
private:
	T* _ptr;
};

 

  • shared_ptr(C++11中提供)

1.实现原理:通过引用计数的方式来实现多个shared_ptr对象之间 共享资源

  1.  在shared_ptr内部,给每个资源都维护着一份计数,用来记录该份资源被几个对象共享。
  2.  在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。 
  3. 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
  4. 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

     注:count的类型是int*,且它是由多个对象共有的,在对count++或--时需要加上互斥锁,这样才能保证线程安全。

shared_ptr的线程安全问题:

  • 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或--,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2,这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以在智能指针中引用计数++、--是需要加锁的,也就是说引用计数的操作是 线程安全的。
  • 智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。

2.缺点:双向链表中可能会出现循环引用问题,导致资源空间不能完全释放掉。

   解决方案:在引用计数的场景下,把节点中的_prev和_next改成weak_ptr就可以了。

3.代码实现:

#include <thread>
#include <mutex>
template<class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr = nullptr)
		:_ptr(ptr)
		,_pCount(new int(1))
		,_pMutex(new mutex)
	{
		// 如果是一个空指针对象,则引用计数给0
		if (_ptr == nullptr)
			*_pCount = 0;
	}
	SharedPtr()
	{
		Release();
	}
	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)
		, _pCount(sp._pCount)
		, _pMutex(sp._pMutex)
	{
		if (_ptr) //指针对象不为空才可进行加引用计数
			AddCount();
	}
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)   //sp1 = sp2;
	{
		if (_ptr != sp._ptr)  //if(this != &sp)
		{
			Release(); //释放旧资源

			//共享管理新对象的资源
			_ptr = sp._ptr;
			_pCount = sp._pCount;
			_pMutex = sp._pMutex;

			//并进行加引用计数
			if (_ptr)  
				AddCount();
		}
		return *this;
	}

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

	int UseCount()
	{
		return *_pCount;
	}
	T* Get()
	{
		return _ptr;
	}
	int AddCount()  //引用计数加1
	{
		_pMutex->lock();
		++(*_pCount);
		_pMutex->unlock();
		return *_pCount;
	}
	int SubCount()   //引用计数减1
	{
		_pMutex->lock();  
		--(*_pCount);
		_pMutex->unlock();
		return *_pCount;
	}
private:
	void Release()
	{
		if (_ptr && SubCount() == 0)   //判断引用计数减1后是否为0,为0则释放资源
		{
			delete _ptr;
			delete _pCount;
		}
	}
private:
	T* _ptr;    //指向管理资源的指针
	int* _pCount; //引用计数
	mutex* _pMutex;   //互斥锁
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值