c++ 智能指针_聊聊C++ 智能指针

什么是智能指针

管理内存主要两种方式

  1. 智能指针
  2. 垃圾回收

各有优缺点,这里主要谈谈智能指针

我们为什么要使用智能指针

在多个指针指向同一块内存时候,如果A指针释放了内存,导致B指针指向的内存无效,那么再使用B指针时候就会出现未知的错误

C++提供的智能指针

C++98的方案中的 auto_ptr (已经被弃用)

auto_ptr<string> p1 (new string ("p1")); 
auto_ptr<string> p2; 
p2 = p1;

auto_ptr 是独占式的, 拥有权已经移交给了p2,所以在使用p1的时候回报错, 这种设计是不合理的。

C++11里的智能指针

  1. std::unique_ptr (C++11 独占式和auto_ptr作用相同, 比auto_ptr更安全)
  2. std::shared_ptr (C++11)
  3. std::weak_ptr (C++11)
  4. std::make_shared (C++11)
  5. std::make_unique (C++14)

这些都是线程安全的

std::shared_ptr

552031e44a2c3cabe501eb30f6a0d9a4.png

P1 P2 P3 引用对象的时候,会增加引用计数, P1释放的时候会减少引用计数。如果引用计数等于0,则删除实际的内存对象。

class D;	//声明
class C
{
public:
	shared_ptr<D> pd_;
	~C()
	{
		std::cout << "C deleten";
	}
};

class D
{
public:
	shared_ptr<C> pc_;
	D()
	{
		std::cout << "D::D()n";
	}
	~D()
	{
		std::cout << "D deleten";
	}
};

void Test()
{
    
		std::shared_ptr<int> sp0(new int(1));
		std::shared_ptr<int> sp1(new int(2));
		cout << " sp0.use_count " << sp0.use_count() <<endl;
		std::shared_ptr<int> sp2 = sp1;
		sp2 = sp0;
		cout << " sp1.use_count " << sp1.use_count() << endl;
		cout << " sp2.use_count " << sp2.use_count() << endl;

		{
			std::shared_ptr<C> pc(new C());
			std::shared_ptr<D> pd(new D());
			cout << " pc.use_count " << pc.use_count() << endl;
			cout << " pd.use_count " << pd.use_count() << endl;
			pc->pd_ = pd;
			pd->pc_ = pc;
			cout << " pc.use_count " << pc.use_count() << endl;
			cout << " pd.use_count " << pd.use_count() << endl;
		}
    
}

此代码pc pd在离开作用域不会删除,因为shared_ptr 有循环引用的缺陷。 std::weak_ptr 这里就会派上用途。 改写为:

class D
{
public:
	weak_ptr<C> pc_;
	D()
	{
		std::cout << "D::D()n";
	}
	~D()
	{
		std::cout << "D deleten";
	}
};

weak_ptr是弱智能指针对象,它不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的智能指针。

  1. weak_ptr 使用 lock 获得一个可用的 shared_ptr 对象, weak_ptr 在使用前需要检查合法性.
  2. expired 用于检查对象是否已经释放, 如果已经释放, 返回 true; 否则返回 false.
  3. lock 用于获取对象的强引用(shared_ptr). 如果 expired 为 true, 返回一个空的 shared_ptr; 否则返回一个 shared_ptr。
  4. use_count 返回与 shared_ptr 共享引用计数.
  5. weak_ptr 支持拷贝或赋值, 但不会影响对应的 shared_ptr 内部对象的计数.
shared_ptr<string> p = make_shared<string>("c++");
weak_ptr<string> wp1(p);
if (shared_ptr<string>  smptr = wp1.lock())
{
    cout << "the shared_ptr is validn";
}
wp1.reset();
if (!shared_ptr<string> smptr = wp1.lock())
{
    cout << "the shared_ptr is Invalidn";
}

std::unique_ptr 对象始终是关联的原始指针的唯一所有者。我们无法复制unique_ptr对象,它只能移动。

std::unique_ptr<string> uniquePtr(new string("c++"));
std::unique_ptr<string> uniquePtr1 = uniquePtr;  //error
std::unique_ptr<string> uniquePtr1 = std::move(uniquePtr);//所有权只能转移
uniquePtr = std::make_unique<string>("c");

std::make_shared是一个模板函数,用于创建智能指针对象。

我们在写一代码时候

void TestMakeShare(const std::shared_ptr<D>& c, const std::shared_ptr<E> & e){}

C++ 不能保值参数的求值顺序。如果循序为

  1. D()
  2. E() //异常跳出
  3. shared_ptr<D>
  4. shared_ptr<E>

在2的位置异常了, 那么D()内存就泄漏了。

std::make_shared 可以解决内存泄漏问题。

TestMakeShare(std::make_shared<D>(new D()), std::make_shared<E>(new E()));

优点

  1. 提高性能 (一次内存申请)
  2. 异常安全 (防止内存泄漏)

缺点

  1. 构造函数必须是public
  2. 对象的内存可能无法及时回收(控制块和对象分配在一起)

实现shared_ptr weak_ptr unique_ptr仅供学习

/*********************************************************************************
  *Copyright(C),Your Company
  *FileName:  SharePtr.h
  *Author:  BillMarshall
**********************************************************************************/

#pragma once
#include <iostream>

using  namespace std;
namespace XCommon
{
	//智能指针配置块
	class ControlBlock
	{
	public:
		ControlBlock() : _uses(0), _weaks(0) {};
		int _uses;	//强引用计数
		int _weaks;	//弱引用计数
	};

	template <class T>
	class WeakPtr; 

	template <class T>
	class SharePtr
	{
	public:
		SharePtr(T* p = 0) : _ptr(p)
		{
			//这里最好使用小对象分配器
			cnt = new ControlBlock();
			if (p)
				cnt->_uses = 1;
		}
		~SharePtr()
		{
			release();
		}

		int use_count()
		{
			return cnt->_uses;
		}

		SharePtr(SharePtr<T> const& s)
		{
			_ptr = s._ptr;
			(s.cnt)->_uses++;
			cnt = s.cnt;
		}


		//用过WeakPtr构造SharePtr
		friend class WeakPtr<T>; 
		SharePtr(WeakPtr<T> const& w) 
		{
			_ptr = w._ptr;
			(w.cnt)->_uses++;
			cnt = w.cnt;
		}

		//赋值构造函数
		SharePtr<T>& operator=(SharePtr<T>& s)
		{
			if (this != &s)
			{
				//先释放自己
				release();
				(s.cnt)->_uses++;
				cnt = s.cnt;
				_ptr = s._ptr;
			}
			return *this;
		}


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


		void Debug()
		{
			cout << "SP:" << cnt->_uses << "  WP:" << cnt->_weaks << endl;
		}

	protected:
		void release()
		{
			cnt->_uses--;
			if (cnt->_uses < 1)
			{
				//强引用为0则删除
				delete _ptr;
				if (cnt->_weaks < 1)
				{
					//弱引用为0则删除配置块
					delete cnt;
					cnt = NULL;
				}
			}
		}

	private:
		T* _ptr;	  //对象指针
		ControlBlock* cnt; //配置块
	};

	//用来解决ShaderPtr引用循环问题
	template <class T>
	class WeakPtr
	{
	public: 
		WeakPtr()
		{
			_ptr = 0;
			cnt = 0;
		}
		//WeakPtr只能通过SharePtr构造
		WeakPtr(SharePtr<T>& s) : _ptr(s._ptr), cnt(s.cnt)
		{
			cnt->_weaks++;
		}
		WeakPtr(WeakPtr<T>& w) : _ptr(w._ptr), cnt(w.cnt)
		{
			cnt->_weaks++;
		}
		~WeakPtr()
		{
			release();
		}
		WeakPtr<T>& operator=(WeakPtr<T>& w)
		{
			if (this != &w)
			{
				release();
				cnt = w.cnt;
				cnt->_weaks++;
				_ptr = w._ptr;
			}
			return *this;
		}
		WeakPtr<T>& operator=(SharePtr<T>& s)
		{
			release();
			cnt = s.cnt;
			cnt->_weaks++;
			_ptr = s._ptr;
			return *this;
		}
		//在使用WeakPtr时候转化为SharePtr对象
		SharePtr<T> lock()
		{
			if (!expired())
			{
				return SharePtr<T>(*this);
			}
			return nullptr;		
		}
		//判断SharePtr对象是否已经别释放掉
		bool expired()
		{
			if (cnt)
			{
				if (cnt->_uses > 0)
				{
					return false;
				}
			}
			return true;
		}
		friend class SharePtr<T>; 

		void Debug()
		{
			cout << "SP:" << cnt->_uses << "  WP:" << cnt->_weaks << endl;
		}

	protected:
		void release()
		{
			if (cnt)
			{
				//只删除弱引用控制块
				cnt->_weaks--;
				if (cnt->_weaks < 1 && cnt->_uses < 1)
				{
					delete _ptr;
					cnt = NULL;
				}
			}
		}
	private:
		T* _ptr;
		ControlBlock* cnt;
	};


	template<typename T>
	class UniquePtr
	{
	public:
		UniquePtr(T* pResource = NULL)
			: _ptr(pResource)
		{

		}

		~UniquePtr()
		{
			del();
		}

		void reset(T* newPtr) 
		{
			del();
			_ptr = newPtr;
		}

		T* release() 
		{
			T* pTemp = _ptr;
			_ptr = nullptr;
			return pTemp;
		}

		T* get() 
		{
			return _ptr;
		}

		void Own(UniquePtr<T>& uPtr)
		{
			_ptr = uPtr._ptr;
			uPtr._ptr = nullptr;
		}

		operator bool() const
		{
			return _ptr != nullptr;
		}

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

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

	private:
		void del()
		{
			if (nullptr == _ptr) return;
			delete _ptr;
			_ptr = nullptr;
		}
		UniquePtr(const UniquePtr&) = delete; // 禁用拷贝构造
		UniquePtr& operator = (const UniquePtr&) = delete; // 禁用拷贝赋值
	private:
		//对象是唯一的不需要引用配置块
		T* _ptr;
	};

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值