026 weak_ptr

作者: 赵者也 | 来源:发表于2020-03-03 10:03 被阅读0次

weak_ptr 是一种不控制所指向对象生存期的智能指针,它指向由一个 shared_ptr 管理的对象,将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用计数,一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。即使有 weak_ptr 指向对象,对象也还是会被释放,因此, weak_ptr 的名字抓住了这种智能指针“弱”共享对象的特点。

weak_ptr 说明
weak_ptr<T> w 空 weak_ptr 可以指向类型为 T 的对象
weak_ptr<T> w(sp) 与 shared_ptr sp 指向相同对象的 weak_ptr,T 必须能转换为 sp 指向的类型
w = p p 可以是一个 shared_ptr 或一个 weak_ptr,赋值后 w 与 p 共享对象
w.reset() 将 w 置为空
w.use_count() 与 w 共享对象的 shared_ptr 的数量
w.expired() 若 w.use_count() 为 0,返回 true,否则返回 false
w.lock() 如果 expired 为 true,返回一个空 shared_ptr;否则返回一个指向 w 的对象的 shared_ptr

当我们创建一个 weak_ptr 时,要用一个 shared_ptr 来初始化它:

auto p = make_shared<int>(42);
weak_ptr<int> wp(p); //wp 弱共享 p;p 的引用计数未改变

本例中 wp 和 p 指向相同的对象,由于是弱共享,创建 wp 不会改变 p 的引用计数;wp 指向的对象可能被释放掉。

由于对象可能不存在,我们不能使用 weak_ptr 直接访问对象,而必须调用 lock。此函数检查 weak_ptr 指向的对象是否仍存在,如果存在,lock 返回一个指向共享对象的 shared_ptr。与任何其他 shared_ptr 类似,只要此 shared_ptr 存在,它所指向的底层对象也就会一直存在。例如:

if (shared_ptr<int> np = wp.lock()) { // 如果 np 不为空则条件成立
    // 在 if 中,np 与 p 共享对象
}

在这段代码中,只有当 lock 调用返回 true 时我们才会进入 if 语句体。在 if 中,使用 np 访问共享对象是安全的。

核查指针类

作为 weak_ptr 用途的一个展示,我们将为 StrBlob 类定义一个伴随指针类。我们的指针类将命名为 StrBlobPtr,会保存一个 weak_ptr,指向 StrBlob 的 data 成员,这是初始化时提供给它的,通过使用 weak_ptr,不会影响一个给定的 StrBlob 所指向的 vector 的生存期。但是,可以阻止用户访问一个不再存在的 vector 的企图。

StrBlobPtr 会有两个数据成员:wptr,或者为空,或者指向一个 StrBlob 中的 vector;curr,保存当前对象所表示的元素的下标,类似它的伴随类 StrBlob,我们的指针类也有一个 check 成员来检查解引用 StrBlobPtr 是否安全:

// 对于访问一个不存在元素的尝试,StrBlobPtr 抛出一个异常
class StrBlobPtr {
public:
    StrBlobPtr(): curr(0) {}
    StrBlobPtr(StrBlob &a, size_t sz = 0): 
            wptr(a.data), curr(sz) {}
    std::string & deref() const;
    StrBlobPtr& incr(); // 前缀递增
private:
    // 若检查成功,check 返回一个指向 vector 的 shared_ptr
    std::shared_ptr<std::vector<std::string>>
            check(std::size_t, const std::string& ) const;
    // 保存一个 weak_ptr,意味看底层 vector 可能会被销毁
    std::weak_ptr<std::vector<std::string>> wptr;
    std:size_t curr; // 在数组中的当前位置
}

默认构造函数生成一个空的 StrBlobPtr,其构造函数初始化列表将 curr 显式初始化为 0,并将 wptr 隐式初始化为一个空 weak_ptr,第二个构造函数接受一个 StrBlob 引用和一个可选的索引值,此构造函数初始化 wptr,令其指向给定 StrBlob 对象的 shared_ptr 中的 vector,并将 curr 初始化为 sz 的值。我们使用了默认参数,表示默认情况下将 curr 初始化为第一个元素的下标。我们将会看到,StrBlob 的 end 成员将会用到参数 sz。

值得注意的是,我们不能将 StrBlobPtr 绑定到一个 const StrBlob 对象。这个限制是由于构造函数接受一个非 const StrBlob 对象的引用而导致的。

StrBlobPtr 的 check 成员与 StrBlob 中的同名成员不同,它还要检查指针指向的 vector 是否还存在:

std::shared_ptr<std::vector<std::string>>
StrBlobPtr::check(std::size_t i, const std::string &msg) const {
    auto ret = wptr.lock(); // vector 还存在吗?
    if (!ret){
        throw std::runtime_error("unbound StrBlobPtr");
    }
    if (i >= ret->size()){
        throw  std::out_of_range(msg);
    }
    return ret; // 否则,返回指向 vector 的 shared_ptr
}

由于一个 weak_ptr 不参与其对应的 shared_ptr 的引用计数,StrBlobPtr 指向的 vector 可能已经被释放了。如果 vector 已销毁,lock 将返回一个空指针。在本例中,任何 vector 的引用都会失败,于是抛出一个异常。否则,check 会检查给定索引,如果索引值合法,check 返回从 lock 获得的 shared_ptr。

指针操作

我们将定义名为 deref 和 incr 的函数,分别用来解引用和递增 StrBlobPtr。

deref 成员调用 check,检查使用 vector 是否安全以及 curr 是否在合法范围内:

std::string& StrBlobPtr::deref() const {
    auto p = check(curr, "dereference past end");
    return (*p)[curr]; //(*p) 是对象所指向的 vector
}

如果 check 成功,p 就是一个 shared_ptr,指向 StrBlobPtr 所指向的 vector。表达式 (*p)[curr] 解引用 shared_ptr 来获得 vector,然后使用下标运算符提取并返回 curr 位置上的元素。

incr 成员也调用 check:

// 前缀递增:返回递增后的对象的引用
StrBlobPtr& StrBlobPtr::incr() {
    // 如果 curr 已经指向容器的尾后位置,就不能递增它
    check(curr, "increment past end of StrBlobPtr");
    ++curr; // 推进当前位置
    return *this;
}

当然,为了访问 data 成员,我们的指针类必须声明为 StrBlob 的 friend 我们还要为 StrBlob 类定义 begin 和 end 操作,返回一个指向它自身的 StrBlobPtr:

// 对于 StrBlob 中的友元声明来说,此前置声明是必要的
class StrBlobPtr;
class StrBlob {
    friend class StrBlobPtr;
    // 其他成员
    // 返回指向首元素和尾后元素的 StrBlobPtr
    StrBlobPtr begin() {return StrBlobPtr(*this); }
    StrBlobPtr end() {
        auto ret = StrBlobPtr(*this, data->size());
        return ret;
    }
};

相关文章

  • 026 weak_ptr

    weak_ptr 是一种不控制所指向对象生存期的智能指针,它指向由一个 shared_ptr 管理的对象,将一个 ...

  • weak_ptr的使用场景

    weak_ptr只能从shared_ptr对象构建。 weak_ptr并不影响动态对象的生命周期,即其存在与否并不...

  • 1 - 智能指针

    https://www.nowcoder.com/discuss/418992 智能指针的实现,weak_ptr为...

  • 简书周刊026发布

    《简书周刊026》epub下载地址 《简书周刊026》豆瓣阅读地址 《简书周刊026》多看阅读地址 《简书周刊02...

  • 亏麻了

    【实盘日记】:026 “股手”日记第026天: 【日期】:2022.12.29 【今日】:-3788。 【当月】:...

  • 2017每日一问丨概念篇026

    026、什么是钱?

  • weak_ptr lock 理解

    创建新的 std::shared_ptr 对象,它共享被管理对象的所有权。若无被管理对象,即 *this 为空,则...

  • std::weak_ptr用法

    一、特性 std::weak_ptr并不是一种独立的智能指针,而是std::shared_ptr的一种扩充。std...

  • 智能指针

    C++ 11 智能指针 unique_ptr、shared_ptr 与 weak_ptr C++ 11 中有 un...

  • c++primer 14.30-14.42

    14.30定义常量版本的* -> 14.31因为StrBlobPtr的私有属性有一个weak_ptr,不需要考虑...

网友评论

    本文标题:026 weak_ptr

    本文链接:https://www.haomeiwen.com/subject/onmxaqtx.html