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.
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.
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.
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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'; | |
} | |
} |
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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'; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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'; | |
} |
I will write about the applications of weak pointer in a separate article.
Modern (Effective) C++ - std::weak_ptr
Reviewed by zeroingTheDot
on
February 05, 2018
Rating:

No comments: