Understanding std::weak_ptr
std::weak_ptr is a smart pointer introduced in C++11 as part of the standard library <memory> header. It complements std::shared_ptr by providing a non-owning, weak reference to an object managed by std::shared_ptr. Unlike std::shared_ptr, std::weak_ptr does not contribute to the reference count of the managed object. Instead, it allows observing the object without extending its lifetime. If the object is deleted, the std::weak_ptr automatically becomes empty, preventing access to the invalid memory.
Need of weak_ptr
The development of std::weak_ptr addresses the issue of reference cycles and potential memory leaks that may arise when using std::shared_ptr. Reference cycles occur when objects have circular dependencies, causing each object to hold a strong reference to the other, preventing them from being deallocated. By introducing std::weak_ptr, developers can break these cycles and avoid memory leaks while still being able to observe the objects when necessary.
Consider a scenario where you have two classes, Parent and Child, with a parent-child relationship managed using std::shared_ptr. Each Parent object holds a std::shared_ptr to its child, and each Child object holds a std::shared_ptr to its parent. This creates a circular reference between the parent and child objects.
Without std::weak_ptr:
#include <iostream>
#include <memory>
class Child; // Forward declaration
class Parent {
public:
std::shared_ptr<Child> childPtr; // Shared ownership of Child
~Parent() {
std::cout << "Parent destroyed\n";
}
};
class Child {
public:
std::shared_ptr<Parent> parentPtr; // Shared ownership of Parent
~Child() {
std::cout << "Child destroyed\n";
}
};
int main() {
// Create shared_ptr instances
std::shared_ptr<Parent> parent = std::make_shared<Parent>();
std::shared_ptr<Child> child = std::make_shared<Child>();
// Establish mutual (cyclic) ownership
parent->childPtr = child; // Parent owns Child
child->parentPtr = parent; // Child owns Parent
// ❗ Both shared_ptrs now keep each other alive
// ❌ Memory will NOT be freed — destructors won't be called
return 0;
}
In this example, parent and child objects form a circular reference through std::shared_ptr, resulting in a memory leak. Even after all external references to the parent and child objects are gone, they will not be deallocated because each holds a strong reference to the other.
Using std::weak_ptr to Break Circular References:
#include <memory>
class Child;
class Parent {
public:
std::weak_ptr<Child> childPtr; // Using weak_ptr instead of shared_ptr
};
class Child {
public:
std::weak_ptr<Parent> parentPtr; // Using weak_ptr instead of shared_ptr
};
int main() {
std::shared_ptr<Parent> parent = std::make_shared<Parent>();
std::shared_ptr<Child> child = std::make_shared<Child>();
// Establishing circular reference
parent->childPtr = child;
child->parentPtr = parent;
// No memory leak, as weak_ptr does not contribute to reference count
return 0;
}
In this modified example, we have replaced std::shared_ptr with std::weak_ptr for the parent-child relationship. By using std::weak_ptr, we break the strong reference cycle, ensuring that the parent and child objects can be deallocated when no external references exist.
Creating a Weak Pointer
You can create a std::weak_ptr from a std::shared_ptr or directly from a raw pointer.
std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
std::weak_ptr<int> weakPtr = sharedPtr;
// or
std::weak_ptr<int> weakPtr = std::make_shared<int>(42);
Function
1 expired() Function (Checking Validity):
- The expired() function checks whether the referenced object has been deleted (expired) or is still valid.
- It returns true if the referenced object has been deleted, and false otherwise.
Example:
std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
std::weak_ptr<int> weakPtr = sharedPtr;
// Check if the referenced object is still valid
if (!weakPtr.expired()) {
std::cout << "Referenced object is still valid." << std::endl;
} else {
std::cout << "Referenced object has been deleted." << std::endl;
}
2 lock() Function:
- The lock() function converts the std::weak_ptr to a std::shared_ptr, allowing access to the referenced object if it's still valid.
- It returns an empty std::shared_ptr if the referenced object has been deleted.
Example:
std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
std::weak_ptr<int> weakPtr = sharedPtr;
// Attempt to convert weak_ptr to shared_ptr
std::shared_ptr<int> sharedPtrCopy = weakPtr.lock();
if (sharedPtrCopy) {
std::cout << "Referenced object value: " << *sharedPtrCopy << std::endl;
} else {
std::cout << "Referenced object has been deleted." << std::endl;
}
3 use_count() Function:
- The use_count() function returns the number of std::shared_ptr instances sharing ownership of the referenced object.
- It's important to note that use_count() is not part of std::weak_ptr itself but is accessed through a std::shared_ptr obtained using the lock() function.
Example:
std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
std::weak_ptr<int> weakPtr = sharedPtr;
// Obtain a shared_ptr from weak_ptr
std::shared_ptr<int> sharedPtrCopy = weakPtr.lock();
// Access use_count through the shared_ptr
if (sharedPtrCopy) {
std::cout << "Number of shared_ptr instances: " << sharedPtrCopy.use_count() << std::endl;
}4 Reset:
- The
reset()function resets thestd::weak_ptr, releasing its reference of the objects.
Example:
weakPtr.reset(); // Releases the reference
Real-World Example: Breaking a Cycle
Let’s say you have two classes that hold shared references to each other:
#include <iostream>
#include <memory>
class Child; // Forward declaration
class Parent {
public:
std::shared_ptr<Child> childPtr; // Shared ownership of Child
~Parent() {
std::cout << "Parent destroyed\n";
}
};
class Child {
public:
std::shared_ptr<Parent> parentPtr; // Shared ownership of Parent
~Child() {
std::cout << "Child destroyed\n";
}
};
int main() {
// Create shared_ptr instances
std::shared_ptr<Parent> parent = std::make_shared<Parent>();
std::shared_ptr<Child> child = std::make_shared<Child>();
// Establish mutual (cyclic) ownership
parent->childPtr = child; // Parent owns Child
child->parentPtr = parent; // Child owns Parent
// ❗ Both shared_ptrs now keep each other alive
// ❌ Memory will NOT be freed — destructors won't be called
return 0;
}
Output of this Program:
(no output)Notice that neither Parent nor Child is destroyed, even though main() ends. That’s because their shared_ptrs are keeping each other alive:
parentownschildchildownsparentReference count never hits zero
Breaking Cycle Dependencies with std::weak_ptr
#include <iostream>
#include <memory>
class Child; // Forward declaration
class Parent {
public:
std::weak_ptr<Child> childPtr; // Non-owning reference to Child
~Parent() {
std::cout << "Parent destroyed\n";
}
};
class Child {
public:
std::weak_ptr<Parent> parentPtr; // Non-owning reference to Parent
~Child() {
std::cout << "Child destroyed\n";
}
};
int main() {
// Create shared ownership for both Parent and Child
std::shared_ptr<Parent> parent = std::make_shared<Parent>();
std::shared_ptr<Child> child = std::make_shared<Child>();
// Establish mutual references using weak_ptr
parent->childPtr = child; // Parent observes (but does not own) Child
child->parentPtr = parent; // Child observes (but does not own) Parent
// Exiting main will destroy both parent and child
// No memory leak occurs because weak_ptr does not increase the reference count
return 0;
}
Child destroyed
Parent destroyedBoth
ParentandChildusestd::weak_ptrfor non-owning references to avoid increasing reference counts.When
main()ends, bothshared_ptrs (parentandchild) go out of scope.The
ParentandChildobjects are properly destroyed, and no memory leaks occur.Destructors print confirmation that both objects were cleaned up.

Join the discussion
Sign in with your account to post comments, reply to others, and participate in the conversation.
Login to Comment