天天看點

關于weak_ptr在工作中的使用

     前一段時間遇到一個問題,做的一個buff管理器,buff執行過程中需要用到釋放skill的對象,當時為了友善和執行效率,直接儲存了對象的指針(沒有用智能指針)。後來業務邏輯修改了,在buff存在期間,釋放skill對象可能已經銷毀了。這就尴尬了,buff執行過程中可能會用到已經銷毀的對象,但是buff管理器不知道,這樣會導緻未知的錯誤。

    後來處理方法是buff對象直接儲存釋放skill對象的ID,每次執行的時候,從map中取一下,如果可以取到就是表明對象還活着,取不到表示對象已經死了。這種方式我不是很喜歡,還有更好的方式就是用智能指針shared_ptr與weak_ptr配合使用。

    不同于shared_ptr通過引用計數來控制對象的銷毀,想鐵絲一樣,綁在對象上,每次指派給shared_ptr相當于多一層綁定(shared_ptr的引用計數加一),當綁定對象的所有shared_ptr都銷毀時(引用計數減到零),自動銷毀對象。而weak_ptr像是棉線綁在對象上,不會影響綁定對象。使用時要先轉化成shared_ptr才能調用對象。

    weak_ptr看名字就是比較弱的指針,影響也弱,指派不會導緻引用計數加一,它用來輔助shared_ptr的。weak_ptr可以通過一個shared_ptr或者另一個weak_ptr對象構造,進而獲得對象的觀察權。但它的構造不會引起指針引用計數的增加。

shared_ptr<T> sp(new T());  // sp 指向T對象shared_ptr引用計數加1
weak_ptr<T> w(sp);	        //與shared_ptr指向相同的對象,shared_ptr引用計數不變。
w=p;				        //p可以是shared_ptr或weak_ptr
w.reset();				    //将w置空。
w.use_count();			    //傳回與w共享對象的shared_ptr的個數。
w.lock();				    //如果use_count()為0,傳回一個空shared_ptr,否則傳回非空shared_ptr。
           

下面我寫了一個例子用weak_ptr和shared_ptr來處理,當對象銷毀後,我們再通過指針使用對象時可以知道。

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <memory> // shared_ptr weak_ptr

using namespace std;

class obj
{
public:
	obj(int _index) :index(_index)
	{};
	void update()
	{
		cout << ++index << endl;
	}
private:
	int index;
};

class objable
{
public:
	void regist(weak_ptr<obj> pObj);
	void notify();
public:
	vector<weak_ptr<obj> > objs;
};

void objable::regist(weak_ptr<obj> pObj)
{
	objs.push_back(pObj);
}

void objable::notify()
{
	for (vector<weak_ptr<obj> >::iterator it = objs.begin(); it != objs.end();)
	{
		shared_ptr<obj> pObj(it->lock());
		if (pObj)
		{
			pObj->update();
			++it;
		} 
		else
		{
			it = objs.erase(it);
		}
	}
}

int main()
{
	objable ab;
	//shared_ptr 引用計數為1
	shared_ptr<obj> obj1(new obj(1));
	ab.regist(obj1);
	ab.notify();

	{
		shared_ptr<obj> obj1(new obj(10));
		ab.regist(obj1);
		ab.notify();
	}//出來大括号obj1銷毀 智能指針引用計數減1 此時引用計數為0 對象obj(10)銷毀

	ab.notify();

	cin.get();
}
           

執行結果:

關于weak_ptr在工作中的使用

在陳碩大神在《Linux多線程服務端程式設計:使用muduo C++網絡庫》一書中處理觀察者模式用了相似的方式處理。

因為邏輯都在一個主線程中執行,不涉及race conditions,也可以這麼處理:

class obj
{
public:
	obj(int _index) :index(_index)
	{
		
	};
	void init(objable* s)
	{
		s->regist(this);
	}
	void update()
	{
		cout << ++index << endl;
	}
	~obj()
	{
		s->unregist(this);
	}
private:
	int index;
};
           

第三種方法是引入二級指針:

關于weak_ptr在工作中的使用

如果對象proxy指針永遠不會被删除,當Object對象删除時,proxy指針變為null

關于weak_ptr在工作中的使用

這樣就p2就不會變成空懸指針了