天天看點

shared_ptr實作機制

一、概述

shared_ptr實作了引用計數的功能,在引用計數為0時,自動delete掉管理的對象。

二、實作要點

  • shared_ptr需要實作構造對象以及指派時的計數。
  • shared_ptr需要實作自身銷毀時的計數減少。
  • 需要實作計數為0時自動銷毀管理的對象。
  • 需要實作std::move語義。
  • 要處理多線程下的計數問題。

三、shared_ptr的簡單實作源碼

#pragma once
#include <memory>
#include <atomic>
using namespace std;
template<class T> class MyRefCount
{
private:
	atomic<long> _uses; //atomic使用CPU級的控制,保證多個線程,一個在修改時,其他線程檢視和修改回退。
	T* _ptr;
	//long weaks; 這個用來處理weak_ptr,本代碼不做實作
	void destory()
	{
		delete _ptr; //計數為0後,調用實際對象的析構函數
	}
public:
	MyRefCount(T* ptr)
	{
		_ptr = ptr;
		_uses = 1;
	}
	void incRef()
	{
		_uses++;
		
	}

	void decRef()
	{
		_uses--;
		if (_uses == 0)
		{
			destory();
		}
	}
	long use_count()
	{
		return _uses;
	}
};
template<class T> class MySharedPtr
{
private:
	T* _ptr;
	MyRefCount<T>* _refCount;
public:
	MySharedPtr()
	{
		_ptr = nullptr;
		_refCount = nullptr;
	}
	MySharedPtr(T* ptr)
	{
		this->_ptr = ptr;
		_refCount = new MyRefCount<T>(ptr);
	}
	~MySharedPtr()
	{
		if (_refCount != nullptr)
		{
			_refCount->decRef();
			_refCount = nullptr;
		}
		_ptr = nullptr;
	}
	MySharedPtr(const MySharedPtr<T>& right) //拷貝構造函數
	{
		if (right._refCount != nullptr)
		{
			right._refCount->incRef(); //右值被引用次數加1
		}
		_ptr = right._ptr; //左值用右值的ptr
		_refCount = right._refCount; //左值用右值的refcount
	}
	MySharedPtr(MySharedPtr<T>&& right) //移動構造函數
	{
		//把右值的歸屬權轉移給左值
		_ptr = right._ptr;
		_refCount = right._refCount;

		right._ptr = nullptr;
		right._refCount = nullptr;

	}
	MySharedPtr<T>& operator=(MySharedPtr<T>& right)
	{
		right._refCount->incRef();
				
		if (_refCount != nullptr)
		{
			_refCount->decRef();
		}
		_ptr = right._ptr;
		_refCount = right._refCount;
		return (*this);
	}
	MySharedPtr<T>& operator=(MySharedPtr<T>&& right)
	{
		MySharedPtr<T> t = std::move(right); //= std::move 會調用移動構造函數,t獲得right的資料所有權,right失去資料所有權
		t.swap(*this); //交換t和this,然後t在函數退出時清理掉,會讓原本this指向的對象計數減1
		return (*this);
	}
	void swap(MySharedPtr<T>& t)
	{
		std::swap(this->_refCount, t._refCount);
		std::swap(this->_ptr, t._ptr);
	}

	T* get() //擷取原生指針
	{
		return _ptr;
	}
	T* operator->() //重載指針操作符,實作用MySharedPtr通路T的成員
	{
		return get();
	}
	
	long use_count() //傳回引用的數量
	{
		return _refCount != nullptr?_refCount->use_count():0;
	}

};
class MySharedPtrClass
{
public:
	int v;
	~MySharedPtrClass()
	{
		printf("MySharedPtrClass destory\n");
	}
};
class MySharedPtrTest
{
private:
	void func(MySharedPtr<MySharedPtrClass> mySharedPtr, shared_ptr<MySharedPtrClass> sharedPtr)
	{
		printf("mySharedPtr use count=%ld, sharedPtr use count=%ld\n", mySharedPtr.use_count(), sharedPtr.use_count());
		mySharedPtr->v = 1;
	}
public:
	
	void doTest()
	{
		MySharedPtr<MySharedPtrClass> mySharedPtr(new MySharedPtrClass());
		shared_ptr<MySharedPtrClass> sharedPtr(new MySharedPtrClass());

		printf("mySharedPtr use count=%ld, sharedPtr use count=%ld\n", mySharedPtr.use_count(),sharedPtr.use_count());
		MySharedPtr<MySharedPtrClass> myT = mySharedPtr;
		shared_ptr<MySharedPtrClass> t = sharedPtr;
		printf("mySharedPtr use count=%ld, sharedPtr use count=%ld\n", mySharedPtr.use_count(), sharedPtr.use_count());

		func(mySharedPtr, sharedPtr);
		printf("mySharedPtr use count=%ld, sharedPtr use count=%ld\n", mySharedPtr.use_count(), sharedPtr.use_count());

		myT = std::move(mySharedPtr);
		t = std::move(sharedPtr);
		printf("mySharedPtr use count=%ld, sharedPtr use count=%ld\n", mySharedPtr.use_count(), sharedPtr.use_count());

	}
	
};