四种智能指针的用法以及实现原理

先来说一下四种常用的智能指针,我按使用度从低到高排一下顺序,分别是auto_ptr, unique_ptr, shared_ptr, weak_ptr,先来列举一下啊,剩下的我在一个一个慢慢说呀
首先来说一下智能指针的实现原理主要是通过对象生命周期来控制程序资源的简单技术,然后了既然是指针就是可以进行和指针类似的行为(解引用和空间内容的访问

  • 先来看一下auto_ptrd的实现原理吧
template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr = nullptr)
		: _ptr(ptr)
	{}

	// 拷贝构造:将ap的资源转移到当前对象上
	AutoPtr(AutoPtr<T>& ap)
		: _ptr(ap._ptr)
	{
		ap._ptr = nullptr;
	}

	AutoPtr<T>& operator=(AutoPtr<T>& ap)
	{
		if (this != &ap)
		{
			if (_ptr)
				delete _ptr;

			_ptr = ap._ptr;
			ap._ptr = nullptr;
		}

		return *this;
	}

	~AutoPtr()
	{
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}

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

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

	T* Get()
	{
		return _ptr;
	}

	void ReSet(T* ptr)
	{
		if (_ptr)
			delete _ptr;

		_ptr = ptr;
	}
protected:
	T* _ptr;
};


struct A
{
	int a;
	int b;
	int c;
};

void TestAutoPtr1()
{
	AutoPtr<int> ap1(new int);
	*ap1 = 10;

	AutoPtr<A> ap2(new A);
	ap2->a = 1;
	ap2->b = 2;
	ap2->c = 3;
}

最显著的特点就是一个对象的空间只能一个对象用,不可以两个对象共用同一块空间,避免了程序崩溃问题,当我们赋值以后我们以前的对象资源就被置空了。
我们使用智能指针的时候我们必须加#include,然后了赋值以后我们以前的对象空间就不能访问了哦。


class Date {
	public:
		Date() { 
			cout << "Date()" << endl;
		}  
		~Date(){ 
			cout << "~Date()" << endl;
		}

			   int _year;  
			   int _month; 
			   int _day;
	};

int main() {
	auto_ptr<Date> ap(new Date);
	auto_ptr<Date> copy(ap);
	return 0;
}
  • unique的实现原理
#include<istream>
using namespace std;

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;
	}

	T* Get()
	{
		return _ptr;
	}

	
	 private:
	 	UniquePtr(const UniquePtr<T>&);
	 	UniquePtr<T>& operator=(const UniquePtr<T>&);


protected:
	T* _ptr;
};

void TestUniquePtr()
{
	UniquePtr<int> up1(new int);
	UniquePtr<int> up2(new int);
}
int main()
{
	TestUniquePtr();
	return 0;

}

unique_ptr主要的特点是我们不能进行赋值,拷贝,而我们实现也和auot_ptr简单的实现原理差不多的,主要是拷贝,赋值函数的私有化,并且在c++98里面我们只声明不定义。c++11里面有新的语法这个应该百度一下就知道了
如果我们试图对unique_ptr进行赋值拷贝时候,就会出现程序崩溃。

unique_ptr的简单的使用

unique_ptr<int> up1(new int);
unique_ptr<int> up2(new int);
  • 在来看一下shared_ptr吧(weak_ptr是差不多自己感觉是填补shared_ptr的某些方面的漏洞而涉及的这个可以放后面说的)

先来简单的说一下shared_ptr吧,上面两个指针也许相比shared_ptr使用度并不是很高,相比之下最简单的话说shared_ptr是更加智能的智能指针,shared_ptr是通过引用计数的方法管理同一块内存的,这样内存什么时候释放,内存指向会不会成为野指针就知道了。

  • shared_ptr的实现原理
template<class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr = nullptr)
		: _ptr(ptr)
		, _pCount(nullptr)
	{
		if (_ptr)
		{
			_pCount = new int(1);
		}
	}

	~SharedPtr()
	{
		if (_ptr && 0 == --(*_pCount))
		{
			delete _ptr;
			delete _pCount;
			_ptr = nullptr;
			_pCount = nullptr;
		}
	}

	SharedPtr(const SharedPtr<T>& sp)
		: _ptr(sp._ptr)
		, _pCount(sp._pCount)
	{
		if (_ptr)
			++(*_pCount);
	}

	
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (this != &sp)
		{
			if (_ptr && 0 == --(*_pCount))
			{
				delete _ptr;
				delete _pCount;
			}

			_ptr = sp._ptr;
			_pCount = sp._pCount;

			if (sp._ptr)
				++(*_pCount);
		}

		return *this;
	}

	int UseCount()
	{
		return *_pCount;
	}

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

	T* operator->()
	{
		return _ptr;
	}
protected:
	T* _ptr;
	int* _pCount;
};

void TestSharedPtr1()
{
	SharedPtr<int> sp1(new int);
	cout << sp1.UseCount() << endl;

	SharedPtr<int> sp2(sp1);
	cout << sp1.UseCount() << endl;
	cout << sp2.UseCount() << endl;
}

void TestSharedPtr2()
{
	SharedPtr<int> sp1(new int);
	SharedPtr<int> sp2(sp1);

	SharedPtr<int> sp3(new int);


	sp3 = sp1;
}

int main()
{
	TestSharedPtr1();
	TestSharedPtr2();
	return 0;
}

shared_ptr中是存在线程安全问题的,简单的来说当,B,C对象同时来引用A对象时候也许引用计数并不会加到3,而是2.所以考虑到线程安全问题时候,我们就要加锁

  • 加锁以后
#include <mutex>

template<class T, class DF = DefDF<T>>
class SharedPtr
{
public:
	SharedPtr(T* ptr = nullptr)
		: _ptr(ptr)
		, _pCount(nullptr)
		, _pMutex(nullptr)
	{
		if (_ptr)
		{
			_pCount = new int(1);
			_pMutex = new mutex;
		}
	}

	~SharedPtr()
	{
		Release();
	}

	SharedPtr(const SharedPtr<T>& sp)
		: _ptr(sp._ptr)
		, _pCount(sp._pCount)
		, _pMutex(sp._pMutex)
	{
		if (_ptr)
			IncreaseRefCount();
			//++(*_pCount);
	}

	// s1、s2
	// s1 = s2;
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (this != &sp)
		{
			Release();

			_ptr = sp._ptr;
			_pCount = sp._pCount;

			if (sp._ptr)
				IncreaseRefCount();
				//++(*_pCount);
		}

		return *this;
	}

	int UseCount()
	{
		return *_pCount;
	}

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

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

	void IncreaseRefCount()
	{
		_pMutex->lock();
		++(*_pCount);
		_pMutex->unlock();
	}

	int DecreaseRefCount()
	{
		_pMutex->lock();
		--(*_pCount);
		_pMutex->unlock();

		return *_pCount;
	}

	void Release()
	{
		if (_ptr && 0 == DecreaseRefCount())
		{
			DF()(_ptr);
			delete _pCount;
			delete _pMutex;

			_ptr = nullptr;
			_pCount = nullptr;
			_pMutex = nullptr;
		}
	}

protected:
	T* _ptr;
	int* _pCount;
	mutex* _pMutex;
};

class Date
{
public:
	int _year = 0;
	int _month = 0;
	int _day = 0;
};

void TestSharedThread(SharedPtr<Date>& sp, int n)
{
	for (size_t i = 0; i < n; ++i)
	{
		SharedPtr<Date> copySp(sp);
		sp->_year++;
		sp->_month++;
		sp->_day++;
	}
}

但是在特定环境当中shared_ptr的引用计数未必也是凑效的,例如双向链表的循环引用,这个时候我们必须在双向节点内部在添加一个引用计数的指针用来通知释放内部节点,就需要一个weak_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); 
	cout << node1.use_count() << endl;    
	cout << node2.use_count() << endl;

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

	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;

	return 0;
}

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值