天天看點

muduo網絡庫——enable_shared_from_this和弱回調1. 問題引入2. 解決方案3. 弱回調4. 擴充

1. 問題引入

來看這麼一段代碼

class StockFactory : boost::nocopyable
{
public:
	std::shared_ptr<Stock> get(const std::string& key);
	
private:
	void deleteStock(Stock* stock)
	{
		if (stock)
		{
			MutexLockGuard lock(_mutex);
			stocks_.erase(stock->key());
		}

		delete stock;
	}
};

std::shared_ptr<Stock> StockFactory::get(const std::string& key)
{
	std::shared_ptr<Stock> pStock;
	MutexLockGuard lock(_mutex);
	std::weak_ptr<Stock>& wkStock = stocks_[key];
	pStock = wkStock.lock();
	if (!pStock)
	{
		pStock.reset(new Stock(key),
			boost::bind(&StockFactory::deleteStock, this, _1));
		wkStock = pStock;   //更新stocks_
	}
	return pStock;
}
           

問題:

StockFactory::get()把原始指針this儲存到了boost::function中,如果StockFactory的生命期比Stock短,那麼Stock析構的時候取回調StockFactory::deleteStock就會core dump。

2. 解決方案

使用enable_shared_from_this,這是一個以其派生類為模闆類型實參的基類模闆。繼承它,this指針就會變成shared_ptr。

class StockFactory : public boost::enable_shared_from_this<StockFactory>,
	boost::nocopyable
{
    //...
};

std::shared_ptr<Stock> StockFactory::get(const std::string& key)
{
	//...
	pStock.reset(new Stock(key),
		boost::bind(&StockFactory::deleteStock, shared_from_this(), _1));
	//...
}
           

boost::function裡儲存了一份shared_ptr,可以保證調用StockFactory::deleteStock的時候StockFactory對象還活着。

注意: shared_from_this()不能在構造函數裡面調用,因為在構造StockFactory的時候,還沒有交給shared_ptr接管。

但是同樣引入一個問題,StockFactory的生命期似乎被意外延長了。。。

3. 弱回調

解決方法:

利用weak_ptr,把它綁定到boost::function裡,這樣對象的生命周期不會被延長。然後在回調的時候先嘗試提升為shared_ptr,如果提升成功了,說明接受的回調對象還存在,則執行回調;如果提升失敗,則不處理。

class StockFactory : public boost::enable_shared_from_this<StockFactory>,
	boost::nocopyable
{
public:
	std::shared_ptr<Stock> get(const std::string& key);
	
private:
	void deleteStock(const boost::weak_ptr<StockFactory>& wkFactory, Stock* stock)
	{
		shared_ptr<StockFactory> factory(wkFactory.lock());
		if (stock)   //如果擷取的強指針存在,則執行清理操作
		{
			MutexLockGuard lock(_mutex);
			stocks_.erase(stock->key());
		}

		delete stock;
	}

private:
	mutable MutexLock mutex_;
	std::map<std::string, weak_ptr<Stock>> stocks_;
};

std::shared_ptr<Stock> StockFactory::get(const std::string& key)
{
	std::shared_ptr<Stock> pStock;
	MutexLockGuard lock(_mutex);
	std::weak_ptr<Stock>& wkStock = stocks_[key];
	pStock = wkStock.lock();
	if (!pStock)
	{
		pStock.reset(new Stock(key),
			boost::bind(&StockFactory::deleteStock, boost::weak_ptr<StockFactory>(shared_from_this()), _1));
		//強制把shared_from_this()轉型為weak_ptr,不會延長生命周期
        //因為boost::bind拷貝的實參類型,不是形參類型
		wkStock = pStock;   //更新stocks_
	}
	return pStock;
}
           

4. 擴充

1)關于shared_ptr的技術陷阱:

shared_ptr會意外延長對象的生命周期。shared_ptr是強引用,隻要有一個指向x對象的shared_ptr存在,該對象就不會被析構。

2)boost::bind會發生實參拷貝

class Foo {
	void Do() {}
};

std::shared_ptr<Foo> pFoo(new Foo);
boost::function<void()> func = boost::bind(&Foo::Do, pFoo);
           

如此例中所示,func持有了std::shared_ptr的一份拷貝,有可能會不經意延長pFoo的生命周期。

繼續閱讀