C++ 入门第 5 版中weak_ptr 的用例

问题描述

嗨,我正在阅读 C++ 入门第 5 版,并且对weak_ptr 部分有一些疑问。写的是

通过使用weak_ptr,我们不会影响给定StrBlob 指向的向量的生命周期。但是,我们可以阻止用户尝试访问不再存在的向量。

然后他们给出了以下代码作为示例:

#include<iostream>
#include<string>
#include<vector>
#include<memory>
#include<initializer_list>
using namespace std;
class StrBlobPtr;

class StrBlob {
    friend class StrBlobPtr;

public:
    typedef std::vector<std::string>::size_type size_type;
    StrBlob():data(std::make_shared<std::vector<std::string>>()){

    }
    StrBlob(std::initializer_list<std::string> il):data(make_shared<vector<std::string>>(il)){

    }
    size_type size() const {
        return data->size();
    }
    bool empty() const {
        return data->empty();
    }
    void push_back(const std::string &t){
        data->push_back(t);
    }
    std::string& front(){
        check(0,"front on empty StrBlob");
        return data->front();
    }
    std::string& front() const{
        check(0,"front on const empty StrBlob");
        return data->front();
    }
    std::string& back(){
        check(0,"back on empty StrBlob");
        return data->back();
    }
    std::string& back() const {
        check(0,"back on const empty StrBlob");
        return data->back();
    }
    void pop_back(){
        check(0,"pop_back on empty StrBlob");
        data->pop_back();
    }
private:
    std::shared_ptr<std::vector<std::string>> data;
    void check(size_type i,const std::string &msg) const{
        if(i >= data->size()){
            throw out_of_range(msg);
        }
    }
    StrBlobPtr begin();
    StrBlobPtr end();
};


class StrBlobPtr {
    public:
        typedef std::vector<std::string>::size_type size_type;
        StrBlobPtr():curr(0){

        }
        StrBlobPtr(StrBlob &a,size_type sz = 0):wptr(a.data),curr(sz){

        }
        std::string& deref() const {
            auto p = check(curr,"dereference past end");
            return (*p)[curr];
        }
        StrBlobPtr& incr(){
            check(curr,"increment past end of StrBlobPtr");
            ++curr;
            return *this;
        }
        std::shared_ptr<std::vector<std::string>> check(std::size_t i,const std::string &msg) const{
            auto ret = wptr.lock();
            if(!ret){
                throw std::runtime_error("unbound StrBlobPtr");
            }
            if(i>= ret->size()){
                throw std::out_of_range(msg);
            }
            return ret;
        }
    private:
        std::weak_ptr<std::vector<std::string>> wptr;
        size_type curr;
};

StrBlobPtr StrBlob::begin() {
        return StrBlobPtr(*this);
    }
StrBlobPtr StrBlob::end() {
        auto ret = StrBlobPtr(*this,data->size());
    }

int main(){
    

    return 0;
}

我的问题如下:

  1. 我们如何防止用户尝试访问不再存在的向量?我想不出一个用例,我们如何在这个例子中使用上面引用的语句?
  2. 此示例如何显示/验证我们可以防止用户尝试访问不再存在的向量? *如果这个例子没有显示他们所写的内容,那么为什么书中有这个例子?*请注意,我已经写了如果。

解决方法

1.我们如何防止用户尝试访问不再存在的向量?

我们可以通过将 weak_ptr 换成 shared_ptr 来防止它发生。 weak_ptr::lock() 就是这样做的。它原子地检查所指向的对象是否仍然存在并增加相应的 shared_ptr 引用计数,从而“阻止”从该点开始的任何可能的删除。

所以在这行之后:

        auto ret = wptr.lock();

ret 将是拥有或不拥有对象的 shared_ptr,只要 ret 存在,这一事实就不会改变。

然后通过一个简单的测试,您可以安全地检查是否有对象:

        if(!ret){
            /* no object anymore */
        }

最后,函数执行 return ret;,它返回它的一个副本,从而仍然防止对象被删除(引用计数再次增加然后减少)。因此,只要您拥有 shared_ptr 的实例,您就可以放心该对象将继续存在。

然而,这里有一个问题:

    std::string& deref() const {
        auto p = check(curr,"dereference past end");
        return (*p)[curr];
    }

这会返回对 std::string 内的 vector 的引用,在 p 超出范围后,该引用仅由 weak_ptr 持有,即潜在的悬空引用(即与悬空指针没有什么不同)。

2.此示例如何显示/验证我们可以阻止用户尝试访问不再存在的向量?

显然不是。直接忽略它。