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

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

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

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

	AutoPtr& operator=(AutoPtr& 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 ap1(new int);
	*ap1 = 10;

	AutoPtr 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 ap(new Date);
	auto_ptr copy(ap);
	return 0;
}
  • unique的实现原理
#include
using namespace std;

template
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&);
	 	UniquePtr& operator=(const UniquePtr&);


protected:
	T* _ptr;
};

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

}

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

unique_ptr的简单的使用

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

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

  • shared_ptr的实现原理
template
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& sp)
		: _ptr(sp._ptr)
		, _pCount(sp._pCount)
	{
		if (_ptr)
			++(*_pCount);
	}

	
	SharedPtr& operator=(const SharedPtr& 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 sp1(new int);
	cout << sp1.UseCount() << endl;

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

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

	SharedPtr sp3(new int);


	sp3 = sp1;
}

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

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

  • 加锁以后
#include 

template>
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& sp)
		: _ptr(sp._ptr)
		, _pCount(sp._pCount)
		, _pMutex(sp._pMutex)
	{
		if (_ptr)
			IncreaseRefCount();
			//++(*_pCount);
	}

	// s1、s2
	// s1 = s2;
	SharedPtr& operator=(const SharedPtr& 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& sp, int n)
{
	for (size_t i = 0; i < n; ++i)
	{
		SharedPtr copySp(sp);
		sp->_year++;
		sp->_month++;
		sp->_day++;
	}
}

但是在特定环境当中shared_ptr的引用计数未必也是凑效的,例如双向链表的循环引用,这个时候我们必须在双向节点内部在添加一个引用计数的指针用来通知释放内部节点,就需要一个weak_ptr
四种智能指针的用法以及实现原理_第1张图片

  • 修改以后的双向链表的引用计数
struct ListNode {
	int _data; 
	weak_ptr _prev; 
	weak_ptr _next;

	~ListNode()
	{
		cout << "~ListNode()" << endl; 
	}
};

int main() {
	shared_ptr node1(new ListNode); 
	shared_ptr 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;
}

你可能感兴趣的:(c++)