1. 程式人生 > >智慧指標二

智慧指標二

一、智慧指標:

1、智慧指標給出的原因

 

2、智慧指標原理

RAII:在建構函式中分配資源,在解構函式中釋放資源,可以進行解引用或者通過->訪問空間中的結構體變數。需要過載這幾個符號。(迭代器也是把指標進行了封裝、也進行了解引用、也進行了過載,所以迭代器也可以成為一種型別的智慧指標)

3、瞭解哪些智慧指標

迭代器也是把指標進行了封裝、也進行了解引用、也進行了過載,所以迭代器也可以成為一種型別的智慧指標

二、shared_ptr

1、ScopedPtr和sharedPtr在C++11中被加到了標準庫中,原理相同。ScopedPtr是boost庫裡面的,boost庫是c++中非常重要的一個庫,和標準庫一樣提供了一些非常重要的功能供大家呼叫,用到時候也是免費的。(當你在不是很瞭解boost庫裡面詳細的內容的時候,不要輕易說你瞭解ScopedPtr,因為你不瞭解其詳細內容)在標準庫中unique_ptr和其具有相同的功能,在unique_ptr管理資源只能管理一塊資源,只能被一個智慧指標的資源進行管理,不能幾個指標進行共享。其兩種實現方式在上一篇關於智慧指標的部落格中有講到.

 

template<class T>
class SharedPtr
{
public:
	//建構函式
	SharedPtr(T* ptr = NULL)//沒有傳任何實參時,預設值是空的
		:_ptr(ptr)//賦值給當前物件
		, _pCount(NULL)
	{
		if (_ptr)//如果不為空(即外部實參已經將資源給出來了)
		{
			_pCount = new int(1);//新申請一端空間,將空間中的內容給成1
			//給成1是因為外部使用者將資源給進來之後,當前只有一個物件進行管理
		}
	}
	//拷貝構造
	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)//共用同一塊空間
		, _pCount(sp._pCount)//共用同一塊引用計數
	{
		//智慧給當前物件的引用計數加1
		//當前物件如果存在,給引用計數++
		if (_pCount)
		{
			++(*_pCount);
		}
		//_pCount && ++(*_pCount);//不使用迴圈和if判斷實現上述方式
	}

	//賦值運算子的過載:sp1=sp2
	//可能會出現的情況:a、sp1為NULL;b、sp1管理了一段空間,但是這段空間
	//只是sp1一個物件在進行管理(資源獨佔)[應釋放sp1的空間,
	//在和sp2共用同一塊空間,引用計數++】;
	//c、sp1資源共享,【應將引用計數--】
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (this!=&sp)
		{
			//當前物件管理資源了,_pCount一定不為空
			//0==--(*_pCount說明當前物件自己獨立管理資源
			if (_pCount&&0==--(*_pCount)
			{
				delete _ptr;
				delete _pCount;
			}
			//當前物件在裡面沒有管理資源,但是也會共享同一塊引用計數
			_ptr = sp._ptr;
			_pCount = sp._pCount;
			if (_pCount)
				++(*_pCount);//共享同一塊空間需要將引用計數++
		}
		return *this;
	}
	//解構函式
	~SharedPtr()
	{
		if (_pCount && 0 == --(*_pCount))//當前物件獨立管理資源
		{
			delete _ptr;
			delete _pCount;
		}
	}
T& operator*()
	{
		return (*_ptr);
	}
	T& operator->()
	{
		return _ptr;
	}
	//
	int UseCount()const
	{
		return (*_pCount);
	}
private:
	T* _ptr;
	int *_pCount;//計數空間
};
void TestShared()
//sp1未管理資源
{
	SharedPtr<int> sp1;
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared1()
//測試獨立管理資源
{
	SharedPtr<int> sp1(new int);
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared2()
//sp1共享資源
{
	SharedPtr<int> sp1(new int);
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	SharedPtr<int> sp3(sp1);//sp1和sp3共享空間
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
int main()
{
	TestShared();
	return 0;
}

拷貝建構函式的思路:

存在問題,若是使用如下測試程式碼,程式會出錯:因為解構函式裡面直接對函式進行了delete操作,而此時我們建立新空間使用的是malloc

void TestShared3()
 {
	SharedPtr<int> sp1((int*)malloc(sizeof(int)));
	SharedPtr<FILE> sp2(fopen("1.txt","rb"));//rb按照只讀二進位制的形式開啟
 	SharedPtr<int> sp3(new int);//sp1和sp3共享空間
}

 不同的指標應該有不同的方式去處理指標的清理工作,

解決方式1:定製一個刪除器:

template<class T>
void DeletePtr(T*& ptr)
{
	if (ptr)
	{
		delete ptr;
		ptr = NULL;
	}
 }
//對於malloc申請的空間
template<class T>
void FreePtr(T*& ptr)
{
	if (ptr)
	{
		free(ptr);
		ptr = NULL;
	}
}
//檔案指標
void FClosedPtr(FILE*& ptr)
{
	if (ptr)
	{
		fclose(ptr);
		ptr = NULL;
	}
}

//給出函式指標
template<class T>
class _PDF
{
public:
	typedef void(*PDF)(T*&);//定義一個函式指標的型別
};


template<class T>
class SharedPtr
{
	typename typedef __PDF<T>::PDF PDF;
	//typename相當於告訴編譯器PDF是一個型別,不是靜態成員變數
public:
	//建構函式
	SharedPtr(T* ptr = NULL,PDF pdf=DeletePtr)//沒有傳任何實參時,預設值是空的
		:_ptr(ptr)//賦值給當前物件
		, _pCount(NULL)
                ,_pDf(pdf)
	{
		if (_ptr)//如果不為空(即外部實參已經將資源給出來了)
		{
			_pCount = new int(1);//新申請一端空間,將空間中的內容給成1
			//給成1是因為外部使用者將資源給進來之後,當前只有一個物件進行管理
		}
	}
	//拷貝構造
	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)//共用同一塊空間
		, _pCount(sp._pCount)//共用同一塊引用計數
	{
		//智慧給當前物件的引用計數加1
		//當前物件如果存在,給引用計數++
		if (_pCount)
		{
			++(*_pCount);
		}
		//_pCount && ++(*_pCount);//不使用迴圈和if判斷實現上述方式
	}

	//賦值運算子的過載:sp1=sp2
	//可能會出現的情況:a、sp1為NULL;b、sp1管理了一段空間,但是這段空間
	//只是sp1一個物件在進行管理(資源獨佔)[應釋放sp1的空間,
	//在和sp2共用同一塊空間,引用計數++】;
	//c、sp1資源共享,【應將引用計數--】
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (this!=&sp)
		{
			//當前物件管理資源了,_pCount一定不為空
			//0==--(*_pCount說明當前物件自己獨立管理資源
			if (_pCount&&0==--(*_pCount)
			{
				//delete _ptr;
                                _pDf(_ptr);
				delete _pCount;
			}
			//當前物件在裡面沒有管理資源,但是也會共享同一塊引用計數
			_ptr = sp._ptr;
			_pCount = sp._pCount;
			if (_pCount)
				++(*_pCount);//共享同一塊空間需要將引用計數++
		}
		return *this;
	}
	//解構函式
	~SharedPtr()
	{
		if (_pCount && 0 == --(*_pCount))//當前物件獨立管理資源
		{
			//delete _ptr;
                        _pDf(_ptr);
			delete _pCount;
		}
	}
	T& operator*()
	{
		return (*_ptr);
	}
	T& operator->()
	{
		return _ptr;
	}
	//
	int UseCount()const
	{
		return (*_pCount);
	}
private:
	T* _ptr;
	int *_pCount;//計數空間
        PDF _pDF;
};
void TestShared()
//sp1未管理資源
{
	SharedPtr<int> sp1;
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared1()
//測試獨立管理資源
{
	SharedPtr<int> sp1(new int);
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared2()
//sp1共享資源
{
	SharedPtr<int> sp1(new int);
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	SharedPtr<int> sp3(sp1);//sp1和sp3共享空間
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared3()
 {
	SharedPtr<int> sp1((int*)malloc(sizeof(int)),FreePtr);
	SharedPtr<int> sp2(fopen("1.txt","rb"),FClosedPtr);//rb按照只讀二進位制的形式開啟
 	SharedPtr<int> sp3(new int);//sp1和sp3共享空間
}
int main()
{
	TestShared3();
	return 0;
}

解決方式2:加上一個模板型別的引數

//函式引數
//相當於把Dx建立的無名物件像函式一樣使用(即仿函式):
//只需要在類裡面過載()
template<class T>
class Delete
{
public:
	//過載()
	void operator()(T*& ptr)
	{
		if (ptr)
		{
			delete ptr;
			ptr = NULL;
		}
	}
};
template<class T>
class Free
{
public:
	void operator()(T*& ptr)
	{
		if (ptr)
		{
			free(ptr);
			ptr=NULL:
		}
	}
};
//檔案指標的類,本身就是FILE*,不需要給成模板類
class FClose
{
public:
	void operator()(FILE*& ptr)
	{
		if (ptr){
			fclose(ptr);
			ptr = NULL;
		}
	}
};
template<class T,class Dx=Delete<T>>//此時Dx是一個型別
class SharedPtr
{
public:
	//建構函式
	SharedPtr(T* ptr = NULL)//沒有傳任何實參時,預設值是空的
		:_ptr(ptr)//賦值給當前物件
		, _pCount(NULL)
	{
		if (_ptr)//如果不為空(即外部實參已經將資源給出來了)
		{
			_pCount = new int(1);//新申請一端空間,將空間中的內容給成1
			//給成1是因為外部使用者將資源給進來之後,當前只有一個物件進行管理
		}
	}
	//拷貝構造
	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)//共用同一塊空間
		, _pCount(sp._pCount)//共用同一塊引用計數
	{
		//智慧給當前物件的引用計數加1
		//當前物件如果存在,給引用計數++
		if (_pCount)
		{
			++(*_pCount);
		}
		//_pCount && ++(*_pCount);//不使用迴圈和if判斷實現上述方式
	}

	//賦值運算子的過載:sp1=sp2
	//可能會出現的情況:a、sp1為NULL;b、sp1管理了一段空間,但是這段空間
	//只是sp1一個物件在進行管理(資源獨佔)[應釋放sp1的空間,
	//在和sp2共用同一塊空間,引用計數++】;
	//c、sp1資源共享,【應將引用計數--】
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (this!=&sp)
		{
			//當前物件管理資源了,_pCount一定不為空
			//0==--(*_pCount說明當前物件自己獨立管理資源
			if (_pCount&&0==--(*_pCount)
			{
				Dx()(_ptr);//直接加上()相當於建立了一個物件
				//類似於一個函式物件
				delete _pCount;
			}
			//當前物件在裡面沒有管理資源,但是也會共享同一塊引用計數
			_ptr = sp._ptr;
			_pCount = sp._pCount;
			if (_pCount)
				++(*_pCount);//共享同一塊空間需要將引用計數++
		}
		return *this;
	}
	//解構函式
	~SharedPtr()
	{
		if (_pCount && 0 == --(*_pCount))//當前物件獨立管理資源
		{
			Dx()(_ptr);//直接加上()相當於建立了一個物件
				//類似於一個函式物件
			delete _pCount;
		}
	}
	T& operator*()
	{
		return (*_ptr);
	}
	T& operator->()
	{
		return _ptr;
	}
	//
	int UseCount()const
	{
		return (*_pCount);
	}
private:
	T* _ptr;
	int *_pCount;//計數空間
};
void TestShared()
{
	SharedPtr<int,Free<int>> sp1((int*)malloc(sizeof(int)));
	SharedPtr<FILE,FClose> sp2(fopen("1.txt", "rb"));//rb按照只讀二進位制的形式開啟
	SharedPtr<int> sp3(new int);//sp1和sp3共享空間
}

int main()
{
	TestShared();
	return 0;
}

3、使用標準庫中的shared_ptr處理問題:

int main()
{
	shared_ptr<int> sp1(new int);//管理單個的空間
	cout << sp1.use_count() << endl;
	//sp1拷貝構造sp2
	shared_ptr<int> sp2(sp1);
	cout << sp1.use_count() << endl;
	return 0;
}

4、shared_ptr自身有一個很大的缺陷,有可能存在迴圈引用的問題,沒有呼叫解構函式,會發生記憶體洩漏,如下述程式:

# include"memory"
template<class T>
struct ListNode
{
	//建構函式
	ListNode(const T& data)
	:_pPre(NULL)
	, _pNext(NULL)
	, _data(data)
	{
		cout << "ListNode(const T&):" << this << endl;
	}
	~ListNode()
	{
		cout << "~ListNode():" << this<<endl;
	}
	//ListNode<T>* _pPre;
	//ListNode<T>* _pNext;
	shared_ptr<ListNode<T>> _pPre;
	shared_ptr < ListNode<T>> _pNext;
	T _data;
};
void TestSharedPtr()
{
	//ListNode<int>* p=new ListNode<T> (10);
	shared_ptr<ListNode<int>> sp1(new ListNode<int> (10));
	shared_ptr<ListNode<int>> sp2(new ListNode<int>(20));
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
	//將兩個節點連結起來
	sp1->_pNext = sp2;
	sp2->_pPre = sp1;
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
}
int main()
{
	TestSharedPtr();
	system("pause");
	return 0;
}

結果為2,沒有進行析構、釋放

5、解決迴圈引用的問題:weak_ptr,weak_ptr不能獨立的管理空間。和shared_ptr一起使用,解決迴圈引用的問題,此時會呼叫解構函式【迴圈引用的場景,面試重要程度:5顆星】

# include"memory"
template<class T>
struct ListNode
{
	//建構函式
	ListNode(const T& data)
	:_data(data)
	{
		cout << "ListNode(const T&):" << this << endl;
	}
	~ListNode()
	{
		cout << "~ListNode():" << this<<endl;
	}
	//ListNode<T>* _pPre;
	//ListNode<T>* _pNext;
	weak_ptr<ListNode<T>> _pPre;
	weak_ptr < ListNode<T>> _pNext;
	T _data;
};
void TestSharedPtr()
{
	//weak_ptr<int> wp(new int);//不能單獨使用,必須依附在shared_ptr之上
	//ListNode<int>* p=new ListNode<T> (10);
	shared_ptr<ListNode<int>> sp1(new ListNode<int> (10));
	shared_ptr<ListNode<int>> sp2(new ListNode<int>(20));
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
	//將兩個節點連結起來
	sp1->_pNext = sp2;
	sp2->_pPre = sp1;
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
}
int main()
{
	TestSharedPtr();
	system("pause");
	return 0;
}

結果: