std::weak_ptr
并不是一种独立的智能指针,而是std::shared_ptr
的一种扩充
std::weak_ptr
一般是通过std::shared_ptr
来创建的。当使用std::shared_ptr
完成初始化std::weak_ptr
的时刻,两者就治涉到了相同的位置,但是std::weak_ptr
并不影响所指涉到的对象的引用计数:
auto spw = std::make_shared<Widget>(); // spw构造完成后,指涉到Widget的引用计数置为1
std::weak_ptr<Widget> wpw(spw); // wpw和spw指涉到同一个Widget.引用计数保持为1
// ...
spw = nullptr; // 引用计数变为0,Widget对象被析狗。wpw空悬
std::weak_ptr
的空悬,也被称作失效,可以直接测试:
if(wpw.expired()) // 若wpw不再指涉到任何对象
一种形式是std::weak_ptr::lock
,它返回一个std::shared_ptr
.如果sd::weak_ptr
已经失效,则std::shared_ptr
为空。
考虑一个工厂函数,该函数基于唯一的ID来创建一些指涉到只读对象的智能指针。根据条款18,它返回一个std::shared_ptr
:
std::unique_ptr<const Widget> loadWidget(WidgetID id);
如果loadWidget执行了文件或者数据库的I/O操作,并且ID会被频繁重复使用,一个合理优化是撰写一个能够完成loadWidget的工作,但又能缓存结果的函数。然而用缓存所用过的Widget造成缓存拥塞,可能本身就会引起性能问题,因此一个合理的优化就是缓存的Widget不再有用时將其删除。
对于这个带缓存的工厂函数而言,返回std::unique_ptr
并不十分合适。缓存管理器的指针需要能够校验它们合适会空悬,因为工厂函数的用户用完由该工厂函数返回的对象后,该对象就將被析狗,此时相应的缓存条目將会空悬。因此应该缓存std::weak_ptr
,一种可以检测空悬的指针。这也意味着,该工厂函数的返回值应该为std::shared_ptr
,因为只有当对象的生存期托管给std::shared_ptr
时,std::weak_ptr
才能检测空悬。如下是带缓存的loadWidget的一个快速而粗糙的实现版本
std::shared_ptr<const Widget> fastLoadWidget(WidgetID id)
{
static::std::unordered_map<WidgetID, std::weak_ptr<const Widget>> cache;
auto objPtr = cache[id].lock(); //obj的类型是std::shared_ptr,指向缓存的对象
if (!objPtr) {
objPtr = loadWidget(id); //如果对象不在缓存中,则加载,并缓存
cache[id] = objPtr;
}
return objPtr;
}