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的生命周期。