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