C++ 智能指針——主要包括:《爲什麼需要智能指針》《RALL的使用原理》《auto,unique,share三者聯繫和區別,以及三者的實現》《RALL擴展訓練》

《一》爲什麼需要智能指針?
在我們上一節課 異常 哪一節課的時候,我們知道了之前我們提到的問題,爲下面的兩個問題,我們還沒有解決。

  1. malloc出來的空間,沒有進行釋放,存在內存泄漏的問題。
  2. 異常安全問題。如果在malloc和free之間如果存在拋異常,那麼還是有內存泄漏。這種問題就叫異常安全。
    《二》智能指針的使用及原理

(2.1)RAII

RAII(Resource Acquisition Is Initialization)是一種利用對象生命週期來控制程序資源(如內存、文件句柄、網絡連接、互斥量等等)的簡單技術。

在對象構造時獲取資源,接着控制對資源的訪問使之在對象的生命週期內始終保持有效,最後在對象析構的時候釋放資源。藉此,我們實際上把管理一份資源的責任託管給了一個對象。這種做法有兩大好處:

  1. 不需要顯式地釋放資源。
  2. 採用這種方式,對象所需的資源在其生命期內始終保持有效。

下面,我們就實現一下,下面的一段代碼來進行分析。

template<class T>
class SmartPtr {
public:
	SmartPtr(T* ptr)
		: _ptr(ptr)
	{}
	~SmartPtr()
	{
		cout << _ptr << endl;
		delete[] _ptr;
	}
private:
	T* _ptr;
};

void func()
{
vector<int> v;  //這裏還沒有使用智能指針,會出現內存沒有釋放,
	v.at(0) = 10;

	int* p = new int;
	SmartPtr<int> sp1(p);


	/*vector<int> v(10);
	size_t pos;
	cin >> pos;
	v.at(pos) = 10;

	int* p = new int;
	SmartPtr<int> sp1(p);*/
}
int main()
{
	try{
		func();

	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

當我們使用沒有被屏蔽的代碼的時候,我們可以發現它的內存是不沒有釋放的,下面我們看一下執行的程序。
在這裏插入圖片描述

當我們打開第二段代碼的時候,可以發現,無論在什麼樣的情況下面,內存總是被釋放的。
在這裏插入圖片描述
(2)下面我們就解決一下這個問題,看一下,下面的代碼:

//c++98 auto_ptr  管理權轉移,設計缺陷,嚴謹使用,
void test_auto_ptr()//一般不採取這個,問題不能被好的解決。
{
	auto_ptr<int> ap(new int);
	*ap = 10;
	cout << *ap << endl;

	auto_ptr<int> copy(ap);
	cout << *ap << endl;
	cout << *copy << endl;
}

//c++11 防止拷貝,簡單粗暴的設計,鼓勵使用
void test_unique_ptr()
{
	unique_ptr<int> up(new int);
	*up = 10;
	cout << *up << endl;

	//unique_ptr<int> copy(up);
	//cout << *ap << endl;
	//cout << *copy << endl;
}

//c++11 引用計數,支持拷貝,鼓勵使用
void test_shared_ptr()
{
	shared_ptr<int> sp(new int);
	*sp = 10;
	cout << *sp << endl;

	shared_ptr<int> copy(sp);
	cout << *sp << endl;
	cout << *copy << endl;
}

int main()
{
	//test_auto_ptr();
	//test_unique_ptr();
	test_shared_ptr();

	return 0;
}

在這裏面,我們分別使用了auto,unique,share 這三種智能指針,但是,這三種的能裏是截然不同的,在我們使用auto的時候,它的管理直接就被轉移了,而且存在很大的缺陷,不建議被使用,在下面使用的unique的時候,她很好的解決了這個問題,但是它不能被拷貝,它是防止拷貝的,我們提議可以使用,最後面的share,這個放到最後面,當然,它是很強的,不僅能很好的使用,而且可以可以被拷貝。對照,上面的代碼,我們執行了一下程序,我們看一下結果;
在這裏插入圖片描述
這張圖片很好說明了,unique和share的區別和聯繫了,

《二》我們就是實現一下,三者的聯繫關係,以及函數調用

namespace DPF
{
	template<class T>
	class AutoPtr
	{
	public:
		AutoPtr(T* ptr)
			:_ptr(ptr)
		{}
	~AutoPtr()
	{
		if (_ptr)
		{
			cout << "delete:" << _ptr << endl;
			delete _ptr;
		}
	}


	// BIT::AutoPtr<int> copy(ap);
	AutoPtr(AutoPtr<T>& ap)
	{
		_ptr = ap._ptr;
		ap._ptr = nullptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};

template<class T>
class UniquePtr
{
public:
	UniquePtr(T* ptr)
		:_ptr(ptr)
	{}

	~UniquePtr()
	{
		if (_ptr)
		{
			cout << "delete:" << _ptr << endl;
			delete _ptr;
		}
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()


	{
			return _ptr;
		}

		// C++11 防拷貝
		//UniquePtr(UniquePtr<T>& ap) = delete;
	private:
		// 防拷貝 C++98 1.只聲明不實現 2.私有
		UniquePtr(UniquePtr<T>& ap);
	private:
		T* _ptr;
	};

	template<class T>
	class SharedPtr    //引用計數
	{
	public:
		SharedPtr(T* ptr)
			:_ptr(ptr)
			, _pcount(new int(1))
			, _pmtx(new mutex)
		{}

		~SharedPtr()
		{
			/*if (--(*_pcount) == 0)
			{
			cout << "delete:" << _ptr << endl;
			delete _ptr;
			delete _pcount;
			}*/

			Realase();
		}

		SharedPtr(const SharedPtr<T>& sp)
			:_ptr(sp._ptr)
			, _pcount(sp._pcount)
			, _pmtx(sp._pmtx)
		{
			//++(*_pcount);
			AddRefCount();
		}

		// sp1 = sp2
		SharedPtr<T>& operator=(const SharedPtr<T>& sp)
		{
			//if (this != &sp)  //遇到自己給自己賦值的情況
			if (_ptr != sp._ptr)
			{
				/*if (--(*_pcount) == 0)
				{
				delete _ptr;
				delete _pcount;
				}*/

				Realase();

				_ptr = sp._ptr;
				_pcount = sp._pcount;
				//++(*_pcount);
				AddRefCount();//代替上面註銷部分,以上代碼也是這樣。
			}

			return *this;
		}

		void AddRefCount()
		{
			_pmtx->lock();
			++(*_pcount);
			_pmtx->unlock();
		}

		void Realase()
		{
			bool deleteflag = false;
			_pmtx->lock();
			if (--(*_pcount) == 0)
			{
				cout << "delete" << _ptr << endl;
				delete _pcount;
				delete _ptr;
				deleteflag = true;
			}
			_pmtx->unlock();

			if (deleteflag == true)
				delete _pmtx;
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		int use_count()
		{
			return *_pcount;
		}

	private:
		T* _ptr;
		int* _pcount;
		mutex* _pmtx;
	};
}

void TestAutoPtr()
{
	DPF::AutoPtr<int> ap(new int);
	DPF::AutoPtr<int> copy(ap);
}

void TestUniquePtr()
{
	DPF::UniquePtr<int> ap(new int);
	//DPF::UniquePtr<int> copy(ap);//防止拷貝的
}

void TestSharedPtr()
{
	DPF::SharedPtr<int> sp(new int);
	DPF::SharedPtr<int> copy(sp);

	DPF::SharedPtr<int> sp1(new int);
	DPF::SharedPtr<int> sp2(sp1);

	sp = sp1;
	copy = sp1;

	//sp1 = sp1;
	//sp1 = sp2;
}

int main()
{
	//TestAutoPtr();
	//TestUniquePtr();
	TestSharedPtr();

	return 0;
}

通過這三者的分析以及代碼的實現,調用,這三種不同的函數,有着不一樣的功能,下面,同學們,可以看着代碼的實現,來使用這三種接口,《這幾種,我都已經測試過了,沒有什麼異常了,可以直接使用了》

《三》RALL擴展訓練

#include<iostream>
#include <memory>
#include <thread>
#include <mutex>

using namespace std;


template<class lock>
class UnipueLock
{
public:
	UnipueLock(lock& lock)
	:_lock(lock)
	{
		_lock.lock();
	}
	~UnipueLock()
	{
		_lock.unlock();
	}
private:
	lock& _lock;//成員變量給的是引用。
};

int main()
{
	mutex mtx;
	UnipueLock<mutex> lock(mtx);

	return 0;
}

上面的代碼,就是根據加鎖,解鎖的智能指針方案,來實現異常安全導致的死鎖問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章