1.11.2 弱回调
把shared_ptr 绑(boost::bind)到boost:function 里,那么回调的时候Stock-Factory 对象始终存在,是安全的。这同时也延长了对象的生命期,使之不短于绑得的boost:function 对象。
有时候我们需要“如果对象还活着,就调用它的成员函数,否则忽略之”的语意,就像Observable::notifyObservers() 那样,我称之为“弱回调”。这也是可以实现的,利用weak_ptr,我们可以把weak_ptr 绑到boost::function 里,这样对象的生命期就不会被延长。然后在回调的时候先尝试提升为shared_ptr,如果提升成功,说明接受回调的对象还健在,那么就执行回调;如果提升失败,就不必劳神了。
使用这一技术的完整StockFactory 代码如下:
- class StockFactory : public boost::enable_shared_from_this<StockFactory>,
- boost::noncopyable
- {
- public:
- shared_ptr<Stock> get(const string& key)
- {
- shared_ptr<Stock> pStock;
- MutexLockGuard lock(mutex_);
- weak_ptr<Stock>& wkStock = stocks_[key]; // 注意wkStock 是引用
- pStock = wkStock.lock();
- if (!pStock)
- {
- pStock.reset(new Stock(key),
- boost::bind(&StockFactory::weakDeleteCallback,
- boost::weak_ptr<StockFactory>(shared_from_this()),
- _1));
- // 上面必须强制把shared_from_this() 转型为weak_ptr,才不会延长生命期,
- // 因为boost::bind 拷贝的是实参类型,不是形参类型
- wkStock = pStock;
- }
- return pStock;
- }
- private:
- static void weakDeleteCallback(const boost::weak_ptr<StockFactory>& wkFactory,
- Stock* stock)
- {
- shared_ptr<StockFactory> factory(wkFactory.lock()); // 尝试提升
- if (factory) // 如果factory 还在,那就清理stocks_
- {
- factory->removeStock(stock);
- }
- delete stock; // sorry, I lied
- }
- void removeStock(Stock* stock)
- {
- if (stock)
- {
- MutexLockGuard lock(mutex_);
- stocks_.erase(stock->key());
- }
- }
- private:
- mutable MutexLock mutex_;
- std::map<string, weak_ptr<Stock> > stocks_;
- };
- 两个简单的测试:
- void testLongLifeFactory()
- {
- shared_ptr<StockFactory> factory(new StockFactory);
- {
- shared_ptr<Stock> stock = factory->get("NYSE:IBM");
- shared_ptr<Stock> stock2 = factory->get("NYSE:IBM");
- assert(stock == stock2);
- // stock destructs here
- }
- // factory destructs here
- }
- void testShortLifeFactory()
- {
- shared_ptr<Stock> stock;
- {
- shared_ptr<StockFactory> factory(new StockFactory);
- stock = factory->get("NYSE:IBM");
- shared_ptr<Stock> stock2 = factory->get("NYSE:IBM");
- assert(stock == stock2);
- // factory destructs here
- }
- // stock destructs here
- }
这下完美了,无论Stock 和StockFactory 谁先挂掉都不会影响程序的正确运行。这里我们借助shared_ptr 和weak_ptr 完美地解决了两个对象相互引用的问题。
当然,通常Factory 对象是个singleton,在程序正常运行期间不会销毁,这里只是为了展示弱回调技术15,这个技术在事件通知中非常有用。
本节的StockFactory 只有针对单个Stock 对象的操作,如果程序需要遍历整个stocks_,稍不注意就会造成死锁或数据损坏(§2.1),请参考§2.8 的解决办法。