程序员成长之旅——智能指针

智能指针的发展旅程

我所了解的智能指针其实是为了解决一些异常安全的问题的,它是运用RAII思想封装的类,从而可以达到自动析构的形式。

auto_ptr
C++98提出来的第一个智能指针,它是一个类似管理权转让的形式,拷贝构造或者赋值的时候,将其原本的管理权转交给新的对象。
unique_ptr
C++11提出来的,它的特性非常的明确,就是防拷贝和赋值,将其私有化,或者delete。
shared_ptr
C++11提出来的,这是为了可以支持拷贝和赋值出现的,它的底层其实是用引用计数来实现的。
weak_ptr
这边这个C++11提出来的主要是为了弥补shared_ptr的缺点,有循环引用的可能,因此是用它来解决的。

智能指针的模拟实现

auto_ptr

template<class T>
	class auto_ptr 
	{
	public:
		auto_ptr(T* ptr = nullptr) 
			:_ptr(ptr)
		{}
		auto_ptr(const auto_ptr<T>& tmp)
		{
			_ptr = tmp._ptr;
			tmp._ptr = nullptr;
		}
		auto_ptr<T>& operator=(const auto_ptr<T>& tmp)
		{
			if (_ptr != tmp._ptr)
			{
				if(_ptr)
					delete(_ptr);
					
				_ptr = tmp._ptr;
				tmp._ptr = nullptr;
			}
			return *this;
		}
		~auto_ptr()
		{
			if (_ptr)
				delete(_ptr);
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

从上面我们可以看到,运用auto_ptr的话,在将管理权转让出去之后,如果我们在使用原对象,就会出现错误,为了避免这种错误,C++11提出了更优的智能指针。
unique_ptr

template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}
		~unique_ptr()
		{
			if (_ptr)
				delete(_ptr);
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		//C++11的用法
		unique_ptr(const unique_ptr<T>&) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>&) = delete;
	private:
		T* _ptr;
	};

shared_ptr

		template<class T>
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_pCount(new int(1))
			,_mtu(new mutex)
		{}
		shared_ptr(const shared_ptr<T>& tmp)
			:_ptr(tmp._ptr)
			,_pCount(tmp._pCount)
			,_mtu(tmp._mtu)
		{
			AddRef();
		}
		shared_ptr<T>& operator=(const shared_ptr<T>& tmp)
		{
			if (_ptr != tmp._ptr)
			{
				Release();

				_ptr = tmp._ptr;
				_pCount = tmp._pCount;
				AddRef();
			}
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		~shared_ptr()
		{
			Release();
		}
		
	private:
		void AddRef()
		{
			_mtu->lock();
			++(*_pCount);
			_mtu->unlock();
		}
		void Release()
		{
			bool flag = false;
			_mtu->lock();
			if (--(*_pCount) == 0)
			{
				delete(_pCount);
				delete(_ptr);
				flag = true;
			}
			_mtu->unlock();

			if (flag == true)
				delete(_mtu);
		}
		T* _ptr;
		int* _pCount;
		mutex* _mtu;
	};

以上就是shared_ptr的简单模拟实现,它所存在的最大问题就是,它会有循环引用的问题。举个例子:

typedef struct ListNode
	{
		shared_ptr<ListNode> _next;
		shared_ptr<ListNode> _prev;
		int _val;
	}ListNode;

	void Test()
	{
		shared_ptr<ListNode> t1(new ListNode);
		shared_ptr<ListNode> t2(new ListNode);

		t1->_next = t2;
		t2->_prev = t1;
	}

画个图就理解为何出现循环引用了
在这里插入图片描述
而为了解决这个情况,我们就引入了弱指针,来解决这个问题。
weak_ptr

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}
		weak_ptr(const shared_ptr<T>& tmp)
		{
			_ptr = tmp.GetPtr();
		}
		weak_ptr<T>& operator=(const shared_ptr<T>& tmp)
		{
			_ptr = tmp.GetPtr();
			return *this;
		}
	private:
		T* _ptr;
	};
	struct ListNode
	{
	//我们将会发生循环引用的指针用弱指针来完成它
		weak_ptr<ListNode> _next;
		weak_ptr<ListNode> _prev;
		int _val;
	};

	void Test()
	{
		shared_ptr<ListNode> t1(new ListNode);
		shared_ptr<ListNode> t2(new ListNode);
		t1->_next = t2;
		t2->_prev = t1;
	}

简单的来说,weak_ptr就是只要能构成循环引用的,我们都不将它进行计数加加,这样的话就能有效解除掉循环引用的问题了。

智能指针常见的问题个人理解

1.模拟实现shared_ptr的时候,引用计数是放在栈上还是堆上,说其中原因?
是在堆上
我认为不放在栈上的理由很简单,就是为了手控这个释放内存的时刻,不会让编译器自动控制,这样子的话,就可能会造成,多个对象指向同一个控制块,当某一个对象作用域 结束的时候,栈上面的这个引用计数就会释放,其它的在使用就会出错。

2.模拟shared_ptr的时候为何在计数++和–的时候要加锁?
会有线程安全的问题
因为++和–是非原子操作的,因此会出现多个线程争抢访问,从而出现线程安全的问题,因此要将其加锁,保证其原子性操作。

补充一个小知识点:+ +为何不是线程安全的
拿++来说的话,它的内部执行至少有三个指令
从内核读取数据
将数据读取到寄存器+1
将寄存器数据写入到内核

并且这三个指令是独立运行的,它们单方面都是原子操作的,但是在多线程并发的情况下,如果两个操作及两个操作以上是一体的话,就是非原子操作,也就是引发线程安全问题的真正缘由。
以上有任何问题,欢迎指正

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

从零出发——

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

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

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

打赏作者

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

抵扣说明:

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

余额充值