C++ Weak Pointers
A weak pointer is a smart pointer that watches an object owned by a shared pointer without owning it. A weak pointer can report whether the watched object still exists.
A shared cycle never dies
Two nodes point at each other through shared pointer members. The trace after the scope has no Node deleted line, and the watch still reports the object as alive.
#include <iostream>
#include <memory>
class Node {
public:
std::shared_ptr<Node> other;
~Node() {
std::cout << "Node deleted\n";
}
};
int main() {
std::cout << std::boolalpha;
std::weak_ptr<Node> watch;
{
auto first = std::make_shared<Node>();
auto second = std::make_shared<Node>();
first->other = second;
second->other = first;
watch = first;
std::cout << "Inside scope\n";
}
std::cout << "Expired: " << watch.expired() << "\n";
std::cout << "Scope ended\n";
return 0;
}A weak back-pointer breaks the cycle
One direction is now a weak pointer, so the pair forms no ownership cycle. Both destructor lines print at the end of the scope, and the watch reports the object as expired.
#include <iostream>
#include <memory>
class Node {
public:
std::shared_ptr<Node> next;
std::weak_ptr<Node> back;
~Node() {
std::cout << "Node deleted\n";
}
};
int main() {
std::cout << std::boolalpha;
std::weak_ptr<Node> watch;
{
auto first = std::make_shared<Node>();
auto second = std::make_shared<Node>();
first->next = second;
second->back = first;
watch = first;
std::cout << "Inside scope\n";
}
std::cout << "Expired: " << watch.expired() << "\n";
std::cout << "Scope ended\n";
return 0;
}lock reads while the object lives
lock gives a shared pointer that reads the value while the owner lives. After the owner drops, expired reports true and a fresh lock is empty.
#include <iostream>
#include <memory>
int main() {
std::cout << std::boolalpha;
std::weak_ptr<int> watch;
{
auto owner = std::make_shared<int>(33);
watch = owner;
auto locked = watch.lock();
std::cout << "Locked value: " << *locked << "\n";
std::cout << "Expired while alive: " << watch.expired() << "\n";
}
std::cout << "Expired after drop: " << watch.expired() << "\n";
auto again = watch.lock();
std::cout << "Lock after drop is empty: " << (again == nullptr) << "\n";
return 0;
}Example
#include <iostream>
#include <memory>
class Parent;
class Child {
public:
std::weak_ptr<Parent> parent;
~Child() {
std::cout << "Child deleted\n";
}
};
class Parent {
public:
std::shared_ptr<Child> child;
~Parent() {
std::cout << "Parent deleted\n";
}
};
int main() {
std::cout << std::boolalpha;
std::weak_ptr<Parent> watch;
{
auto parent = std::make_shared<Parent>();
auto kid = std::make_shared<Child>();
parent->child = kid;
kid->parent = parent;
watch = parent;
std::cout << "Inside scope\n";
}
std::cout << "Expired: " << watch.expired() << "\n";
std::cout << "Scope ended\n";
return 0;
}Key points
- The <memory> header provides std::weak_ptr.
- A weak pointer watches an object that a shared pointer owns.
- A weak pointer does not raise the use count of the object.
- expired returns true after the watched object is deleted and false while it lives.
- lock returns a shared pointer to the object, or an empty pointer if the object is gone.
- A cycle of shared pointers keeps every object in the cycle alive.
- Making one direction of a cycle a weak pointer breaks the cycle.
In this example, the parent owns the child through a shared pointer. The child watches the parent through a weak pointer, so the pair forms no cycle. Both objects are deleted when the scope ends, and the watch then reports Expired as true.
Note
A weak pointer must be locked before the watched object is read through it.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
A weak pointer watches an object owned by a shared pointer. What does the weak pointer do to the use count?