天天看點

智能指針4-----》weak_ptr

在說weak_ptr之前,需要回憶一下shared_ptr的知識。

shared_ptr是采用引用計數的智能指針,多個shared_ptr執行個體可以指向同一個動态對象,并維護了一個共享的引用計數器。對于引用計數發實作的計數,總是避免不了循環引用的問題。

如下執行個體:

class Child;
class Parent
{
public:
	shared_ptr<Child> child;
	Parent() { cout << "Creat Parent" << endl; }
	~Parent() { cout << "Destory Parent" << endl; }
	void hi() const{ cout << "hello" << endl; }
};
class Child
{
public:
	shared_ptr<Parent> parent;
	Child() { cout << "Creat Child" << endl; }
	~Child() { cout << "Destory Child" << endl; }
};

int main()
{
	shared_ptr<Parent> par(new Parent());
	shared_ptr<Child> pch(new Child());
	par->child = pch;
	pch->parent = par;
	pch->parent->hi();
	return 0;
}
           

結果:

智能指針4-----》weak_ptr

上面代碼的運作結果很明顯可以看出來沒有調用Parent和Child的析構函數,這是因為Parent和Chlid的對象内部,具有各自指向對方的shared_ptr,加上par和pch這兩個shared_ptr,說明每個對象的引用計數都是2。當程式退出時,即使par和pch被銷毀,也僅僅是導緻引用計數變為1,是以并未銷毀Parent和Child。

為了解決上面這樣的問題,C++11引入了weak_ptr,來打破這種循環。

1、weak_ptr

weak_ptr是為了配合shared_ptr而引入的一種智能指針,它指向一個由shared_ptr管理的對象而不影響所指對象的生命周期,也就是将一個weak_ptr綁定到一個shared_ptr不會改變shared_ptr的引用計數。

無論是否有weak_ptr指向,一旦最後一個指向對象的shared_ptr被銷毀,對象就會被釋放。

2、weak_ptr的使用

  • weak_ptr的建立:
int main()
{
	shared_ptr<int> sp(new int(5));
	cout << "建立前sp的引用計數" << sp.use_count() << endl;
	weak_ptr<int> wp(sp);
	cout << "建立後sp的引用計數" << sp.use_count() << endl;
	return 0;
}
           

結果:

智能指針4-----》weak_ptr
  • 判斷weak_ptr對象是否存在

    因為weak_ptr并不改變其所共享的shared_ptr執行個體的引用計數,那麼weak_ptr所指向的對象可能已經被釋放了,此時,我們就不能使用weak_ptr直接通路對象。

    c++中提供了lock函數來實作該功能:如果對象存在,lock()函數傳回一個指向該對象的shared_ptr,否則傳回一個空的shared_ptr。

#include <iostream>
using namespace std;

class Object
{
private:
	int value;
public:
	Object(int x = 0) :value(x) { cout << "Object Constructor" << endl; }
	~Object() { cout << "Object Destructor" << endl; }
	int GerValue() const { return value; }
};
int main()
{
	shared_ptr<Object> sp(new Object(10));
	weak_ptr<Object> wp(sp);
	if (shared_ptr<Object> pa = wp.lock())
	{
		cout << pa->GerValue() << endl;
	}
	else
	{
		//expired函數判斷所指對象是否已經被銷毀
		cout << wp.expired() << endl;
		cout << "wp 引用的對象為空" << endl;
	}
	return 0;
}
           
  • weak_ptr并沒有重載operator->和operator*操作符,是以不可直接通過weak_ptr使用對象,典型的做法是調用其lock函數來獲得shared_ptr示例,進而通路原始對象。

3、weak_ptr函數的仿寫

template<typename T>
	class RefCnt
	{
	private:
		T* mPtr;
		std::atomic<int> mCnt_s;
		std::atomic<int> mCnt_w;
	public:
		RefCnt(T* ptr = nullptr) :mPtr(ptr)
		{
			if (mPtr != nullptr)
			{
				mCnt_s = 1;
				mCnt_w = 1;
			}
		}
		int load_s() const { return mCnt_s.load(); }
		int load_w() const { return mCnt_w.load(); }
		void addRef_w()
		{
			mCnt_w++;
		}
		int delRef_w()
		{
			return --mCnt_w;
		}
		void addRef_s()
		{
			mCnt_s++;
		}
		int delRef_s()
		{
			return --mCnt_s;
		}
		~RefCnt() {}
	};
	template<typename T>
	class MyDeletor
	{
	public:
		void operator()(T* ptr) const
		{
			delete ptr;
		}
	};

	template<typename T> class weak_ptr;
	template<typename T,typename Deletor=MyDeletor<T>>
	class shared_ptr
	{
	public:
		typedef T* pointer;
		typedef T  element_type;
		typedef Deletor delete_type;

		delete_type get_deleter()
		{
			return delete_type();
		}
	public:
		shared_ptr(T* ptr = nullptr) : mPtr(ptr)
		{
			mpRefCnt = new RefCnt<T>(mPtr);
		}
		shared_ptr(const shared_ptr<T>& src) :mPtr(src.mPtr), mpRefCnt(src.mpRefCnt)
		{
			mpRefCnt->addRef_s();
		}
		shared_ptr& operator=(const shared_ptr<T>& src)
		{
			if (src == this)
			{
				return *this;
			}

			if (0 == mpRefCnt->delRef_s())
			{
				get_deleter()(mPtr);
				delete mpRefCnt;
				mPtr = nullptr;
				mpRefCnt = nullptr;
			}
			mPtr = src.mPtr;
			mpRefCnt = src.mpRefCnt;
			mpRefCnt->addRef_s();
			return *this;
		}
		~shared_ptr()
		{
			if (0 == mpRefCnt->delRef_s())
			{
				get_deleter()(mPtr);
				delete mpRefCnt;
			}
			mPtr = nullptr;
			mpRefCnt = nullptr;
		}
		T* get()const { return mPtr; }
		T& operator*()const { return *get(); }
		T* operator->()const { return get(); }
		long use_count() const { return mpRefCnt->load_s(); }
		void reset(T* p = nullptr)
		{
			if (0 == mpRefCnt->delRef_s())
			{
				get_deleter()(mPtr);
				delete mpRefCnt;
			}
			mPtr = p;
			mpRefCnt = new RefCnt<T>(mPtr);
		}
		operator bool()const
		{
			return get() != nullptr;
		}
		bool unique()const
		{
			if (mPtr != nullptr && use_count() == 1)
			{
				return true;
			}
			return false;
		}
		shared_ptr(const weak_ptr<T>& _w)
		{
			mPtr = _w.mPtr;
			_w.mpRefCnt->addRef_s();
			mpRefCnt = _w.mpRefCnt;
		}
		friend class weak_ptr<T>;
	private:
		T* mPtr;
		RefCnt<T>* mpRefCnt;
	};

	template<class T>
	class weak_ptr
	{
	public: //給出預設構造和拷貝構造,其中拷貝構造不能有從原始指針進行構造
		weak_ptr() :mPtr(nullptr), mpRefCnt(nullptr) {};
		weak_ptr(shared_ptr<T>& s) :mPtr(s.mPtr), mpRefCnt(s.mpRefCnt)
		{
			mpRefCnt->addRef_w();
		}
		weak_ptr(weak_ptr<T>& w) :mPtr(w.mPtr), mpRefCnt(w.mpRefCnt)
		{
			mpRefCnt->addRef_w();
		}
		~weak_ptr()
		{
			release();
		}
		weak_ptr<T>& operator=(const weak_ptr<T>& w)
		{
			if (this != &w)
			{
				release();
				mpRefCnt = w.mpRefCnt;
				mpRefCnt->addRef_w();
				mPtr = w.mPtr;
			}
			return *this;
		}
		weak_ptr& operator=(const shared_ptr<T>& s)
		{
			release();
			mpRefCnt = s.mpRefCnt;
			mpRefCnt->addRef_w();
			mPtr = s.mPtr;
			return *this;
		}

		//通過調用lock()方法,檢查對象是否存在,若存在,則傳回一個指向共享對象的shared_ptr.
		shared_ptr<T> lock() const
		{
			return shared_ptr<T>(*this);
		}
		int use_count() const
		{
			return mpRefCnt->load_w();
		}
		//判斷指向該指針的對象的shared_ptr是否為nullptr,即就是所指對象是否被銷毀
		bool expired() const
		{
			if (mpRefCnt)
			{
				return mpRefCnt->load_s() == 0;
			}
			return true;
		}
		friend class shared_ptr<T>;//友善weak_ptr與share_ptr設定引用計數和指派
	protected:
		void release()
		{
			if (mpRefCnt != nullptr)
			{
				mpRefCnt->delRef_w();
				if (mpRefCnt->load_s() == 0 && mpRefCnt->delRef_w() == 0)
				{
					delete mpRefCnt;		
				}
				mpRefCnt = nullptr;
			}
		}

	private:
		T* mPtr;
		RefCnt<T>* mpRefCnt;
	};
           

繼續閱讀