A std::weak_ptr is a smart pointer introduced in C++11 that provides a non-owning reference to an object managed by a std::shared_ptr. Unlike shared_ptr, it does not contribute to the reference count, making it useful for observing objects without extending their lifetime.
- Observes objects without affecting their ownership or lifetime.
- Safely checks whether the managed object still exists before accessing it.
Example: Creating and Using a weak_ptr
#include <iostream>
#include <memory>
using namespace std;
int main() {
shared_ptr<int> sp = make_shared<int>(50);
weak_ptr<int> wp = sp;
if (auto ptr = wp.lock()) {
cout << *ptr << endl;
}
return 0;
}
Output
50
Explanation
- sp owns the dynamically allocated integer.
- wp observes the same object without increasing its reference count.
- lock() creates a temporary shared_ptr only if the object still exists
Syntax
std::weak_ptr<Type> ptr;
where Type is the type of the managed object.
Common Member Functions
The following are some member functions associated with std::weak_ptr to provide different functionalities.
| Function | Description |
|---|---|
| lock() | Returns a shared_ptr if the object still exists; otherwise returns an empty shared_ptr. |
| expired() | Returns true if the managed object has already been destroyed. |
| use_count() | Returns the number of shared_ptrs owning the object. |
| reset() | Releases the reference held by the weak_ptr. |
| swap() | Exchanges the contents of two weak_ptrs. |
| owner_before() | Compares ownership with another smart pointer. |
Preventing Circular References
The following example demonstrates how std::weak_ptr breaks circular references while allowing the managed objects to be destroyed correctly.
#include <iostream>
#include <memory>
using namespace std;
class B;
class A {
public:
shared_ptr<B> ptrB;
~A() {
cout << "A destroyed\n";
}
};
class B {
public:
weak_ptr<A> ptrA;
~B() {
cout << "B destroyed\n";
}
};
int main() {
auto a = make_shared<A>();
auto b = make_shared<B>();
a->ptrB = b;
b->ptrA = a;
return 0;
}
Output
A destroyed B destroyed
Explanation: If ptrA were a shared_ptr, both objects would keep each other alive and neither destructor would execute. Using weak_ptr breaks the ownership cycle, allowing both objects to be destroyed correctly.
Advantages of weak_ptr
The following are the major advantages of using std::weak_ptr in C++
- Prevents circular references between shared_ptr objects.
- Does not increase the reference count of the managed object.
- Allows safely checking whether an object still exists before accessing it.
- Works seamlessly with shared_ptr through the lock() function.
Best Practices
Follow these best practices to use std::weak_ptr safely and effectively in C++:
- Use weak_ptr whenever an object needs to observe another object without owning it.
- Always verify the result of lock() before accessing the managed object.
- Prefer weak_ptr over raw pointers when observing objects managed by shared_ptr.
- Use it to break circular references in object graphs.
Applications of weak_ptr
The following are some common scenarios where std::weak_ptr is widely used:
- Breaking circular references between objects that use std::shared_ptr, preventing memory leaks.
- Implementing observer patterns where objects need to monitor others without taking ownership.
- Building cache systems that temporarily reference objects without extending their lifetime.
- Managing graphs, trees, and bidirectional relationships where cyclic references are common.