Smart pointers in C++ (std::shared_ptr
, std::unique_ptr
and std::weak_ptr
) are used to manage the automatic allocation and deallocation of memory, helping to avoid problems such as memory leaks and dangling pointers (pointers that point to a memory location that has already been deallocated).
Let’s see the difference between the main smart pointers!
std::shared_ptr
shared_ptr
is made, this counter is incremented. When all shared_ptr
s that point to the same object are destroyed (counter reaches zero), the pointed object is deallocated.Example:
#include <iostream>
#include <memory>
struct Resource{
Resource(){ std::cout << "Resource acquired\n"; }
~Resource(){ std::cout << "Resource destroyed\n"; }
};
int main(){
std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>();
{
std::shared_ptr<Resource> ptr2 = ptr1; // ptr2 also shares the object.
std::cout << "Reference count: " << ptr1.use_count() << "\n"; // Print the counter.
}
// When ptr2 goes out of scope, the counter is decremented, but the resource still exists.
std::cout << "Reference count: " << ptr1.use_count() << "\n"; // Print the counter.
// When ptr1 goes out of scope, the object is destroyed, since the counter reaches 0.
}
Output:
Resource acquired
Reference count: 2
Reference count: 1
Resource destroyed
std::unique_ptr
unique_ptr
can point to the same resource at a time.std::move
(move semantics) which ensures that the object will be managed by a single pointer.Example:
#include <iostream>
#include <memory>
struct Resource {
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};
int main() {
std::unique_ptr<Resource> ptr1 = std::make_unique<Resource>();
// std::unique_ptr<Resource> ptr2 = ptr1; // This would not compile, copying not allowed.
std::unique_ptr<Resource> ptr2 = std::move(ptr1); // Transfer of ownership.
if (!ptr1) {
std::cout << "ptr1 is null\n";
}
// When ptr2 goes out of scope, the resource is automatically destroyed.
}
Output:
Resource acquired
ptr1 is null
Resource destroyed
std::weak_ptr
shared_ptr
without contributing to the reference count.shared_ptr
reference each other, creating a cycle that prevents the memory from being freed).lock()
, which returns a std::shared_ptr
if the object still exists.Example:
#include <iostream>
#include <memory>
struct Resource {
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};
int main(){
std::shared_ptr<Resource> ptr1 = std::make_shared<Resource>();
std::weak_ptr<Resource> weakPtr = ptr1; // weakPtr observes but does not contribute to the counter.
if(auto tempPtr = weakPtr.lock()){ // Checks if the resource still exists.
std::cout << "Resource is still alive\n";
}else{
std::cout << "Resource has been destroyed\n";
}
ptr1.reset(); // Releases the resource.
if(auto tempPtr = weakPtr.lock()){
std::cout << "Resource is still alive\n";
}else{
std::cout << "Resource has been destroyed\n";
}
}
Output:
Resource acquired
Resource is still alive
Resource destroyed
Resource has been destroyed
That is, shared_ptr
: Owned by multiple owners, with reference counting to manage the destruction of the object. unique_ptr
: Exclusive ownership, cannot be copied, only moved. weak_ptr
: Observes the resource managed by a shared_ptr
, but does not interfere with the reference counting, used to avoid reference cycles.