Modern (Effective) C++ - std::weak_ptr

This article is inspired from Item 20 of Effective Modern C++ by Scott Meyers.

There is yet another smart pointer called std::weak_pointer which does not participate in the ownership of the raw pointer. Instead it's sole existence is attached to a shared_ptr and inform us, whether shared_ptr still exists or is dangling. std::weak_ptr is not a standalone smart pointer instead it was created to augment the std::shared_ptr. Weak pointers cannot be dereferenced or checked against null.

#include <iostream>
#include <memory>
using namespace std;
int main()
{
std::shared_ptr<int> sp = std::make_shared<int>(23);
std::weak_ptr<int> wpr(sp);
std::weak_ptr<int> wpr1{ sp }; // uniform initialization.
std::shared_ptr<int> sp2 = std::make_shared<int>(24);
// Existing weak pointer made to point to new shared pointer.
// Not recommended, but possible.
wpr = sp2;
// the weak pointer expired API is used to check if the weak pointer is
// pointing to a non dangling shared pointer
if (!wpr.expired())
{
std::cout << "Weak pointer pointing to valid shared ptr\n";
std::shared_ptr<int> spNew{wpr};
std::cout << *spNew << '\n';
}
}
In real code, after verifying that the weak_pointer still points to a valid shared_ptr, we go ahead and create another copy of shared_ptr inorder to use it. But if we perform the check using the expired API, and then obtain the shared_ptr, we potentially introduce a race condition in multi-threaded environments, where the shared_ptr is deleted (in another thread) after  the call to expired API (returns false if shared_ptr still exists) and before creating a new shared_ptr.

Hence there was a necessity of creating a mechanism which performs the check and create a new shared_ptr atomically. In fact there are two mechanisms for it -
1. Using the lock API
 shared_ptr<int> sp2 = wpr.lock();

This API creates a new shared_ptr, sp2,  if wpr is still valid.Otherwise sp2 is set to null.

2. Using shared_pointer constructor
shared_ptr<int> sp2(wpr);

If the wpr is still valid, sp2 is created, Otherwise a bad_weak_ptr exception is thrown.

#include <iostream>
#include <memory>
using namespace std;
int main()
{
// Mechanism 1.
// Using the lock API
std::shared_ptr<int> sp1 = std::make_shared<int>(23);
std::weak_ptr<int> wpr1(sp1);
auto sp2 = wpr1.lock();
std::cout << *sp2 << '\n';
sp1 = nullptr; // freeing the shared pointer
sp2 = nullptr; // freeing the shared pointer
sp2 = wpr1.lock();
if (sp2 == nullptr)
{
std::cout << "Weak pointer no longer pointing to valid shared memory location\n";
}
// Mechanism 2.
// Using the shared pointer constructor
std::shared_ptr<int> sp3 = std::make_shared<int>(23);
std::weak_ptr<int> wpr3{ sp3 };
// auto sp4{ wpr3 }; auto creates new weak pointer from existing weak pointer
std::shared_ptr<int> sp4{ wpr3 };
if (sp4 != nullptr)
{
cout << "Shared ptr still valid\n";
}
sp3 = nullptr; // freeing the shared pointer
sp4 = nullptr; // freeing the shared pointer
try
{
std::shared_ptr<int> sp5{ wpr3 };
}
catch (std::bad_weak_ptr& e)
{
std::cout << "Weak pointer no longer pointing to valid shared memory location\n";
std::cout << e.what() << '\n';
}
}

#include <iostream>
#include <memory>
using namespace std;
int main()
{
std::shared_ptr<int> sp1 = std::make_shared<int>(23);
std::weak_ptr<int> wpr1(sp1);
std::cout << "*sp1 = " << *sp1 << '\n';
std::cout << "Size of shared pointer is " << sizeof(sp1) << '\n';
std::cout << "Size of weak pointer is" << sizeof(wpr1) << '\n';
}
If you run the above example, you will see the size of weak_ptr is the same as that of shared_ptr. Weak pointer points to the same control block as it's shared pointer. When a weak_ptr is created, destroyed, or copied a second reference count (weak pointer reference count) is manipulated. Note that when shared_ptr becomes dangling ( i.e the raw pointer is freed), but if weak pointer count is more than one, the control block will NOT get freed.

I will write about the applications of weak pointer in a separate article.

Modern (Effective) C++ - std::weak_ptr Modern (Effective) C++ -  std::weak_ptr Reviewed by zeroingTheDot on February 05, 2018 Rating: 5

No comments:

Powered by Blogger.