c++:智能指針(c++11版本unique_ptr、shared_ptr、weak_ptr)(二)

從前面的內容可以看出auto_ptr具有比較多的缺陷,使用時容易出錯。在C++ 11標準中出現了新的智能指針unique_ptr、 shared_ptr與weak_ptr等

一、unique_ptr

特點:所有權唯一,禁止拷貝和權限轉移

缺點:unique_ptr是簡單粗暴的防止拷貝,這種比較簡單,效率高,但是功能不全面,不支持拷貝和賦值操作。無法共享數據,可自主實現數據共享,但在釋放時會導致堆內存重複釋放導致系統崩潰。

下面是unique_ptr的實現:

#include<iostream>
#include<memory>

template<typename T>
class Unique_Ptr
{
public:
	Unique_Ptr(T* ptr)
		:mptr(ptr)
	{}
	Unique_Ptr()
	{
		delete mptr;
		mptr = NULL;
	}
	T& operator*()
	{
		return *mptr;
	}
	T* operator->()
	{
		return mptr;
	}
private:
	Unique_Ptr(Unique_Ptr<T>&);//將拷貝構造和賦值寫在私有下
	Unique_Ptr<T>& operator=(Unique_Ptr<T>&);
	T* mptr;
};
int main()
{
	int* p = new int;
	Unique_Ptr<int> up1(p);
	Unique_Ptr<int> up2(p);
	Unique_Ptr<int> up3(p);
	return 0;
}

1.禁止賦值和複製

unique_ptr禁止賦值和複製,“唯一”地擁有其所指對象,同一時刻只能有一個unique_ptr實例指向給定對象。也就是說模板類unique_ptr的copy構造函數以及等號(“=”)操作符是無法使用的。 
通過禁止複製和賦值可以較好的改善auto_ptr的所有權轉移問題。下面是其禁止複製和賦值的例子:
 

#include <iostream>
#include <memory>
using namespace std;

void Fun1( unique_ptr<int> up )
{
}

int main()
{
    unique_ptr<int> up1 = unique_ptr<int>(new int(10));

    //不允許複製,所以以下三個均錯誤
    unique_ptr<int> up2 = up1;  // error
    unique_ptr<int> up3(up1);   // error
    Fun1(up1);                  // error

    //不允許賦值('='),所以下面錯誤
    unique_ptr<int> up4;
    up4 = up1;                  // error

    return 0;
}

2.針對auto_ptr缺陷的改善

1) 管理數組指針

 因爲unique_ptr有unique_ptr< X[ ] >重載版本,銷燬動態對象時調用delete[],所以可以用unique_ptr來管理數組指針。

unique_ptr< Test[ ] > uptr1(new Test[3]);
//注意 unique_ptr<Test> uptr3(new Test[3]);是不對的
unique_ptr<int[]> uptr2(new int[5]);

2)做容器(vector, list, map)元素

vector<unique_ptr<int> > vec;
unique_ptr<int> ptr1(new int(3));
vec.push_back(std::move(ptr1));
//vec.push_back(ptr1); //由於禁止複製這樣不行

二、shared_ptr

特點:shared_ptr的實現原理是通過引用計數來實現,拷貝或賦值時將引用計數加1,析構時只有當引用計數減到0才釋放空間,否則只需將引用計數減1即可.shared_ptr共享所有權。

缺點:相互引用  內存泄漏

如圖理解引用計數和shared_ptr的關係:

 

#include<memory>

class Ref_Man   //引用計數
{
public:
	static Ref_Man* getInstance()
	{
		if (prm == NULL)
		{
			prm = new Ref_Man();
		}
		return prm;
	}
	int getRef(void* ptr)       
	{
		if (ptr == NULL)
		{
			return -1;
		}
		int index = find(ptr);
		if (index != -1)
		{
			return ref_count[index].count;
		}
		return -1;
	}
	void addRef(void* ptr)
	{
		if (ptr == NULL)
		{
			return;
		}
		int index = find(ptr);
		if (index != -1)
		{
			ref_count[index].count++;
		}
		else
		{
			ref_count[cursize].addr = ptr;
			ref_count[cursize++].count = 1;
		}
	}
	void delRef(void* ptr)
	{
		if (ptr == NULL)
		{
			return;
		}
		int index = find(ptr);
		if (index != -1)
		{
			if (getRef(ptr) != 0)
			{
				--ref_count[index].count;
			}
		}
	}
private:
	Ref_Man()
	{
		cursize = 0;
	}
	Ref_Man(const Ref_Man&);
	int find(void* ptr)
	{
		int index = -1;
		for (int i = 0; i < cursize; i++)
		{
			if (ref_count[i].addr == ptr)
			{
				index = i;
				break;
			}
		}
		return index;
	}

	typedef struct Ref
	{
		void* addr;    //堆內存地址
		int count;     //堆內存所對應的指針數目
	}Ref;
	Ref ref_count[10];
	int cursize;
	static Ref_Man* prm;
};
Ref_Man* Ref_Man::prm = NULL;

template<typename T>
class Shared_Ptr
{
public:
	Shared_Ptr(T* ptr = NULL)     //構造
		:mptr(ptr)
	{
		prm->addRef(mptr);
	}
	Shared_Ptr(Shared_Ptr<T>& rhs)    //拷貝構造
	{
		mptr = rhs.mptr;
		prm->addRef(mptr);
	}
	Shared_Ptr<T>& operator=(Shared_Ptr<T>&rhs)   //賦值
	{
		if (this != &rhs)
		{
			prm->delRef(mptr);
			if (prm->getRef(mptr))
			{
				delete mptr;
			}
			mptr = rhs.mptr;
			prm->addRef(mptr);
		}
		return *this;
	}
	~Shared_Ptr()
	{
		prm->delRef(mptr);  //--引用計數
		if (prm->getRef(mptr) == 0)   //獲取mptr引用計數,如果爲0釋放
		{
			delete mptr;
		}
		mptr = NULL;
	}
	T& operator*()    //重載*
	{
		return *mptr;
	}
	T* operator->()    //重載->
	{
		return mptr;
	}
private:
	T* mptr;
	static Ref_Man* prm;
};
template<typename T>
Ref_Man* Shared_Ptr<T>::prm = Ref_Man::getInstance();

class B;
class A
{
public:
	A()
	{
		std::cout << "A::A()" << std::endl;
	}
	~A()
	{
		std::cout << "A::~A()" << std::endl;
	}
public:
	std::weak_ptr<B> spa;
};
class B
{
public:
	B()
	{
		std::cout << "B::B()" << std::endl;
	}
	~B()
	{
		std::cout << "B::~B()" << std::endl;
	}
public:
	std::weak_ptr<A> spb;
};

int main()
{
	std::shared_ptr<A> pa(new A());  
	std::shared_ptr<B> pb(new B());
	pa->spa = pb;   //相互引用
	pb->spb = pa;
	return 0;
}

int main()
{
	int* p = new int;           //使用weak_ptr後解決了相互引用導致內存泄漏的問題
	Shared_Ptr<int> sp1(p);
	Shared_Ptr<double> spd = new double;
	Shared_Ptr<int> sp2(p);
	Shared_Ptr<int> sp3(p);
	return 0;
}

shared_ptr相互引用問題

 

  1. pa和pb兩個智能指針對象分別指向spa和spb,引用計數變成1.
  2. pa的spa指向pb,pb的spb指向pa,引用計數變成2。
  3. pa和pb析構,引用計數減到1,但是spa還指向spb。但是spb還指向spa。
  4. 也就是說spa析構了,pb就釋放了。
  5. 也就是說spb析構了,pa就釋放了。
  6. 但是spa屬於pa的成員,pa釋放了,spa纔會析構,而pa由spb管理,spb屬於pb成員,所以這就叫循環引用,誰也不會釋放。 

weak_ptr的出現,很好的解決了這個問題。

 三、weak_ptr

特點:weak_ptr是爲了配合shared_ptr而引入的一種智能指針,它更像是shared_ptr的一個助手而不是智能指針,因爲它沒有重載operator*和->,故而不具有普通指針的行爲。它的最大作用在於協助shared_ptr工作。 
weak_ptr被設計爲與shared_ptr共同工作,可以從一個shared_ptr或者另一個weak_ptr對象構造,獲得資源的觀測權。但weak_ptr沒有共享資源,它的構造不會引起指針引用計數的增加。
作用:解決強智能指針shared_ptr相互引用的問題。

代碼如下:

 

​template<typename T>
Ref_Man* Shared_Ptr<T>::prm = Ref_Man::getInstance();

class B;
class A
{
public:
	A()
	{
		std::cout << "A::A()" << std::endl;
	}
	~A()
	{
		std::cout << "A::~A()" << std::endl;
	}
public:
	std::weak_ptr<B> spa;
};
class B
{
public:
	B()
	{
		std::cout << "B::B()" << std::endl;
	}
	~B()
	{
		std::cout << "B::~B()" << std::endl;
	}
public:
	std::weak_ptr<A> spb;
};

int main()
{
	std::shared_ptr<A> pa(new A());
	std::shared_ptr<B> pb(new B());
	pa->spa = pb;
	pb->spb = pa;
	return 0;
}


int main()
{
	int* p = new int;
	Shared_Ptr<int> sp1(p);
	Shared_Ptr<double> spd = new double;
	Shared_Ptr<int> sp2(p);
	Shared_Ptr<int> sp3(p);
	return 0;
}​

因爲在執行pa->spa = pb;pb->spb = pa;時weak_ptr的spa和spb不會增加pa和pb的引用計數,這樣就保證了正常的釋放。

友情鏈接:

C++智能指針梳理

C++:RAII思想和智能指針(auto_ptr,unique_ptr,shared_ptr)詳解了引用計數部分,並且提供了線程安全的shared_ptr,很值得學習哦~

 

 

 

 

 

 

 

 

 

 

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