2

Hi i am reading C++ primer 5th addition and have some doubts in the section of weak_ptr. It is written that

By using a weak_ptr, we don’t affect the lifetime of the vector to which a given StrBlob points. However, we can prevent the user from attempting to access a vector that no longer exists.

Then they have given the following code as an example:

#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; } 

My questions are as follows:

  1. How can we prevent the user from attempting to access a vector that no longer exists? I can't come up with a use case,how can we use the above quoted statement in this example?
  2. How does this example shows/verifies that we can prevent the user from attempting to access a vector that no longer exists? *If this example does not shows what they have written then why is this example there in the book?*Note that i have written if.
7
  • Have you looked up what happens when you do auto ret = wptr.lock()? Commented Mar 8, 2021 at 13:14
  • Yes i know what happens when we write auto ret = wptr.lock() and i understand what is happening in this statement. Here we are checking if there are any shared_ptr pointing to the same memory to which wptr points and depending upon the result we get a nullptr or a share_ptr. Commented Mar 8, 2021 at 13:18
  • @JasonLiam followed by the line where ret is tested to see if the shared_ptr has already released the object if(!ret){ Note that the control block persists until all shared and weak pointers have been destroyed. Commented Mar 8, 2021 at 13:28
  • 1
    Well the main() function is empty. So the program does exactly nothing. In other words yes, it's a very bad example. Maybe this will help? Commented Mar 8, 2021 at 13:28
  • 1
    I guess it's a question of how you define access to the vector. In a multithreaded environment that deref function certainly won't stop the vector from going away before the string is manipulated via that potentially dangling reference. Commented Mar 8, 2021 at 13:32

1 Answer 1

1

1. How can we prevent the user from attempting to access a vector that no longer exists?

We can prevent it by exchanging a weak_ptr for a shared_ptr. weak_ptr::lock() does that. It atomically checks if the pointed-to object still exists and increments the corresponding shared_ptr ref count, thus "blocking" any possible deletion from that point on.

So after this line:

 auto ret = wptr.lock(); 

ret will be a shared_ptr that either owns the object or doesn't, and that fact will not change for as long as ret exists.

Then with a simple test you can safely check if there is an object or not:

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

At the end the function does return ret;, which returns a copy of it, thus still preventing an object from being deleted (ref count is again incremented and then decremented). So as long as you own an instance of shared_ptr, you can rest assured the object will continue to exist.

However, here we have a problem:

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

This returns a reference to std::string inside a vector which, after p goes out of scope is held only by weak_ptr, i.e. a potentially dangling reference (which is no different from a dangling pointer).

2. How does this example shows/verifies that we can prevent the user from attempting to access a vector that no longer exists?

Apparently it doesn't. Just ignore it.

Sign up to request clarification or add additional context in comments.

1 Comment

To be safe, it should be std::shared_ptr<std::string> deref() with shared_ptr alias constructor.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.