天天看點

解決C++shared_ptr循環連結清單無法釋放記憶體的指針full_ptr

         今天廢話不多說,直接進入主題。

full_ptr制作背景:C++auto_ptr指針不做解釋,shared_ptr是引用計數智能指針,它可以幫助你托管你的記憶體,但在有循環引用的地方比如循環連結清單它就會失效,需要利用week_ptr進行配合使用,相當麻煩和不智能,是以作者興趣之作衍生出了full_ptr來解決shader_ptr的問題。

使用并介紹功能:

這個是測試類,用來給full_ptr測試使用。

class test {
public:
	test(char c):c(c) {}
	void show()
	{
		std::cout << "調用了列印函數:" << c << std::endl;
	}
	~test()
	{
		std::cout << "删除:" << c << std::endl;
	}
private:
	char c;
};
           

建立full指針a并儲存test("a"),然後b也儲存a指向的空間,c儲存test("c"),然後開始。

c也儲存a指向的空間,這時test("c")沒有被其他full引用會被釋放,現在3個full都指向了test("a")。

a和b指針full都列印一下是一樣的,然後把a指派為空沒有回收test("a"),把b指派為空也沒有回收test("a"),最後函數執行完後回收了c後也回收了test("a")。

解決C++shared_ptr循環連結清單無法釋放記憶體的指針full_ptr

循環連結清單示例:

這段代碼是一個簡單的連結清單結構

template<typename T>
class p1
{
public:
	p1(full_ptr<T> & data) :_data(&data)
	{
	}
	p1<T> * next;
	full_ptr<T> * _data;
};
           

建立一個full并指派test('a'),然後建立兩個結點f1和f2都儲存full指針,并将f1和f2互相引用,然後列印一下内容,最後函數執行結束後test('a')被回收了。這裡才是本次文章的重點,如果這裡換成shared_ptr是不能回收test('a')的。

解決C++shared_ptr循環連結清單無法釋放記憶體的指針full_ptr

實作原理:

這段是核心代碼,這個類用來儲存需要托管的堆空間對象,它有一個count引用計數,用來做被引用計數,當計數為0虛構所儲存的堆空間,同時也虛構自己。

template<typename T>
	class _ptr
	{
	public:
		_ptr(T & t) :_data(&t){ count = 0; }
		
		T & GetData()
		{
			return *_data;
		}
		void operator++(int){ count++; }
		void operator--(int)
		{
			if (--count <= 0)
			{
				delete _data;
				delete(this);
			}
			
		}
	private:
		int count;
		T * _data;
	};
           

father_ptr其實沒什麼用,用途就是繼承它的類才可以使用_ptr。

class father_ptr
{
public:
	template<typename T>
	class _ptr
	{
	    。。。
	};
};
           

任何解決shared的問題光靠_ptr肯定不行的,是以就多了一個維護類full_ptr。

在給full指派的時候都會new一個_ptr并讓它來儲存傳進來的空間,然後full來維護_ptr

當出現另外一個full引用自己(full1 = full2)操作時,full2會将_ptr引用的空間也給full1的_ptr引用

不管哪個full自己被回收了都會在虛構函數裡面給_ptr進行計數減1,就這樣,就算外面結構是網狀的,隻要有一個full被删除都會使_ptr減1,直到_ptr為0時代表沒有full維護它了自己回收了自己。

最後後續作者可能會寫個垃圾回收系統。。。。。

template<typename T>
class full_ptr : public father_ptr
{
public:
    /*4個構造函數,這些都是指派使用的
      每個構造函數裡都會new_ptr進行托管*/
	full_ptr(T * t)
	{
		m_ptr = new _ptr<T>(*t);
		(*m_ptr)++;
	}
	full_ptr(T && t)
	{
		m_ptr = new _ptr<T>(*(new T(t)));
		(*m_ptr)++;
	}
	full_ptr(T & t)
	{
		m_ptr = new _ptr<T>(*(new T(t)));
		(*m_ptr)++;
	}
	full_ptr(const full_ptr & ptr)
	{
		if (m_ptr != nullptr)
		{
			(*m_ptr)--;
		}
		m_ptr = ptr.m_ptr;
		(*m_ptr)++;
	}
    /*重載->用來可以直接使用full_ptr使用被儲存的對象*/
	T * operator->()
	{
		return &m_ptr->GetData();
	}
    /*引用另外一個full所引用的值*/
	void operator=(full_ptr & ptr)
	{
		if (m_ptr != nullptr)
		{
			(*m_ptr)--;
		}
		m_ptr = ptr.m_ptr;
		(*m_ptr)++;
	}
    /*如果指派為nullptr就将_ptr計數減1*/
	void operator=(void * ptr)
	{
		if (ptr == nullptr)
		{
			(*m_ptr)--;
			m_ptr = nullptr;
		}
	}
	_ptr<T> * GetData()
	{
		return  m_ptr;
	}
    /*删除*/自己也将_ptr計數減1*/
	~full_ptr()
	{ 
		if (m_ptr != nullptr)
		{
			(*m_ptr)--;
		}
	}
private:
	_ptr<T> * m_ptr;
};
           

完整源碼:

class father_ptr
{
public:
	template<typename T>
	class _ptr
	{
	public:
		_ptr(T & t) :_data(&t){ count = 0; }
		
		T & GetData()
		{
			return *_data;
		}
		void operator++(int){ count++; }
		void operator--(int)
		{
			if (--count <= 0)
			{
				delete _data;
				delete(this);
			}
			
		}
	private:
		int count;
		T * _data;
	};
};
template<typename T>
class full_ptr : public father_ptr
{
public:
	full_ptr(T * t)
	{
		m_ptr = new _ptr<T>(*t);
		(*m_ptr)++;
	}
	full_ptr(T && t)
	{
		m_ptr = new _ptr<T>(*(new T(t)));
		(*m_ptr)++;
	}
	full_ptr(T & t)
	{
		m_ptr = new _ptr<T>(*(new T(t)));
		(*m_ptr)++;
	}
	full_ptr(const full_ptr & ptr)
	{
		if (m_ptr != nullptr)
		{
			(*m_ptr)--;
		}
		m_ptr = ptr.m_ptr;
		(*m_ptr)++;
	}

	T * operator->()
	{
		return &m_ptr->GetData();
	}
	void operator=(full_ptr & ptr)
	{
		if (m_ptr != nullptr)
		{
			(*m_ptr)--;
		}
		m_ptr = ptr.m_ptr;
		(*m_ptr)++;
	}
	void operator=(void * ptr)
	{
		if (ptr == nullptr)
		{
			(*m_ptr)--;
			m_ptr = nullptr;
		}
	}
	_ptr<T> * GetData()
	{
		return  m_ptr;
	}
	~full_ptr()
	{ 
		if (m_ptr != nullptr)
		{
			(*m_ptr)--;
		}
	}
private:
	_ptr<T> * m_ptr;
};
           

工程檔案下載下傳

繼續閱讀