智能指针简单模拟实现

发布时间:2022-10-14 20:30

目录

1 基本概念

2 auto_ptr

3 unique_ptr

   4 shared_ptr

 5 weak_ptr


1 基本概念

        智能指针是针对资源因捕捉异常跳过资源释放(如下图)、或者开辟了空间没有释放等情况所提出的。其解决的办法就是使用RAII(Resource Acquisition Is Initialization)技术利用对象生命周期来控制程序资源。做法就是把普通的指针封装到类中,在析构函数中进行资源释放。这样就不用显示进行资源释放,只要一个智能指针对象生命周期结束,就会自导调用析构函数释放资源。

void func()
{
    int* pi = new int;
    if(pi == nullptr)    
        throw 1;
    delete pi;
}
int main()
{
    try
    {
        func();
    }
    catch
    {
        cout<
	class smart_ptr
	{
	public:
		smart_ptr(T* ptr)
			:_ptr(ptr)
		{}
		~smart_ptr()
		{
			if (_ptr)
				delete _ptr;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

        上述的智能指针中可以自动释放资源了,但是有一个问题是在拷贝构造上。使用拷贝构造会使两个指针指向同一个空间,这样没有问题与原生指针一样。但当析构时同一片空间就会释放两次导致错误。为解决这个问题,又有了以下这些智能指针。

2 auto_ptr

        auto_ptr是C++98的语法,其拷贝构造不是完全的拷贝,而是进行资源转移,将拷贝过来的原指针置空。

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

        这样的做法虽然解决了两次析构,但原指针无法使用与原生指针的情况不同,这并不是我们想看到的。因而auto_ptr没有太大的用途,基本上不使用。后来boost给出了更实用的scoped_ptr和shared_ptr和weak_ptr。C++ 11参考boost中的实现的,引入了unique_ptr和shared_ptr和weak_ptr。unique_ptr对应boost的scoped_ptr。

3 unique_ptr

     unique_ptr也是简单粗暴的将拷贝构造和拷贝赋值直接删除。一个空间只能由一个unique_ptr指向。

	template
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		unique_ptr(const unique_ptr& up) = delete;
		unique_ptr& operator=(const unique_ptr& up) = delete;
		~unique_ptr()
		{
			if (_ptr)
				delete _ptr;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
	};

   4 shared_ptr

        shared_ptr采用引用计数的方式,终于能使用多指针指向一个空间。在引用计数上的增加减少上要使用锁来保证线程安全。

	template
	class shared_ptr
	{
	public:
		shared_ptr(T* ptr)
			:_ptr(ptr)
			,_pRefCount(new int(1))
			,_pmtx(new mutex)
		{}
		shared_ptr(const shared_ptr& sp)
			:_ptr(sp._ptr)
			,_pRefCount(sp._pRefCount)
			,_pmtx(sp._pmtx)
		{
			AddRef();
		}
		shared_ptr& operator=(const shared_ptr& sp)
		{
			if (_ptr != sp._ptr)
			{
				Release();
				_ptr = sp._ptr;
				_pRefCount = sp._pRefCount;
				_pmtx = sp._pmtx;
				AddRef();
			}
			return *this;
		}
		~shared_ptr()
		{
			Release();
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		void Release()
		{
			_pmtx->lock();
			bool flag = false;
			if (--(*(_pRefCount)) == 0 && _ptr)
			{
				delete _ptr;
				delete _pRefCount;
				flag = true;
			}
			_pmtx->unlock();
			if (flag)
				delete _pmtx;
		}
		void AddRef()
		{
			_pmtx->lock();
			++(*_pRefCount);
			_pmtx->unlock();
		}
	private:
		T* _ptr;
		int* _pRefCount;
		mutex* _pmtx;
	};

        shared_ptr已经能解决大部分的场景了,但是在循环引用下就会出问题。例如下图在一个双链表中,A、B、next、prev都使用shared_ptr,A与prev指向一个节点,引用计数为2。B与next也指向一个节点。A、B指针指向节点释放,各自引用计数减1。A中next指向的节点要释放,则B指针指向节点要释放。B指针指向节点要释放,则B中prev指向的节点要先释放,该节点要释放又要释放next指向节点。这样就导致了循环引用问题,两个节点都无法释放。

 5 weak_ptr

        weak_ptr就是为了解决shared_ptr循环引用的。它在拷贝构造和拷贝赋值时的引用计数不增加。注意它不能以原生指针构造,只适用于在循环引用场景将shared_ptr构造为weak_ptr。

	template
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}
		weak_ptr(const shared_ptr& sp)
			:_ptr(sp._ptr)
		{}
		weak_ptr& operator=(const shared_ptr& sp)
		{
			_ptr = sp._ptr;
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _pRefCount;
	};

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号