Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does "weak.lock()" return "nullptr" with the definition of "auto weak=std::make_shared<int>(42);"?

Why does weak.lock() return nullptr in this code snippest:

   std::weak_ptr<int> weakPtr1 = std::make_shared<int>(6);
   std::cout << weakPtr1.lock() << std::endl;

whereas it works in the following one:

   std::shared_ptr<int> sharedPtr = std::make_shared<int>(99);
   std::weak_ptr<int> weakPtr2 = sharedPtr;
   std::cout << weakPtr2.lock() << std::endl;

Check cpp.sh/9gkys.

I have thought and thought about it, but I am still confused now. I would be grateful to have some help with this question.

like image 815
John Avatar asked Jun 16 '20 05:06

John


People also ask

What is nullptr keyword in C++?

The nullptr keyword is utilized for the null values in the pointer variable. It has its own syntax and representations in the C++ programming language. Let’s see the below syntax code. Above mentioned codes, we can create the function and passing the pointer arguments.

What is the purpose of a null shared_ptr?

A null shared_ptr does serve the same purpose as a raw null pointer. It might indicate the non-availability of data. However, for the most part, there is no reason for a null shared_ptr to possess a control block or a managed nullptr. But we might utilize a non-empty shared_ptr 's deleter to execute arbitrary cleanup code on block exit.

What does it mean when MSDN says null?

When it writes NULL, it means the obvious thing: A null pointer. You can translate that into the appropriate construction for the language you are using. For example, for C#, you can use null, or if you are operating in raw IntPtr s, you can use IntPtr.Zero. Bonus chatter: When MSDN says NULL, is it okay to use 0?

Is it possible to change the definition of null to nullptr?

Bonus bonus chatter: I’m told that the Visual C++ folks occasionally entertain the possibility of changing the definition of NULL to nullptr, which is permitted by the standard. However, this ends up breaking a lot of code which assumed that NULL is an integral constant evaluating to zero. For example:


2 Answers

Smart pointers, in order to do their work properly, maintain a so called control block which serves as a metadata storage, in particular, use counters. That is, each resource has an associated control block in memory (consisting of e.g. two integers) that smart pointers can refer to to know how many of them are still using/observing the resource. Obviously, each existing std::shared_ptr increases the use counter stored in the control block, so that its destructor knows whether or not it's time to release the resource on destruction. std::weak_ptr, in turn, only tracks the object and its control block. Note that here's an important detail: std::weak_ptr does not increase the use counter. That's desirable, as its main purpose is to break possible cycles between a pair of objects observing one another. That is, if two objects would store std::shared_ptrs one to another, then such a pair of objects would also keep alive one another endlessly.

How can a std::weak_ptr know if the resource can be lock()ed ? This can succeed only if the use counter is greater than zero. It knows that from the control block (which itself remains alive in memory as long as there's also non-zero weak pointers observing it).

In the first example:

std::weak_ptr<int> weakPtr1 = std::make_shared<int>(6);

both a resource (int=6) is allocated and also its control block. Use counter becomes 1, and remains so as long as std::shared_ptr is alive. Then, a std::weak_ptr is initialized, obtaining a pointer to the control block. Here, it won't increase the use counter. It will, however, increase the counter of weak pointers. At this point, both counters are 1. Then, at the semicolon ;, the temporary std::shared_ptr is destroyed. It decreases the use counter down to 0. This means there are no more shared pointers sharing ownership of the resource, which allows that resource to be released. However, there is still 1 weak pointer observing the control block, which means the control block itself will remain in the memory, so that weakPtr1 knows it won't be able to lock() the resource anymore (because that resource no longer exists).

In the second example:

std::shared_ptr<int> sharedPtr = std::make_shared<int>(99);
std::weak_ptr<int> weakPtr2 = sharedPtr;

both the resource int=99 and its control block remain alive. Hence weakPtr2 can be locked as long as sharedPtr (or any of its copies) is not destroyed.

like image 127
Piotr Skotnicki Avatar answered Oct 08 '22 09:10

Piotr Skotnicki


Your examples use copy initialization. As such, the shared_ptr constructed on the right lives only until the evaluation of the expression, and is then destroyed:

  1. In the first example this means there are no more references to the shared_ptr (we do not count the weak one), and hence lock returns null.
  2. In the second one you bind the result to a local variable, extending life time to the current block - and hence there is still a reference, and no null result.
like image 43
kabanus Avatar answered Oct 08 '22 07:10

kabanus