Smart Pointers
Smart pointers automate memory management, preventing leaks and simplifying ownership.
std::unique_ptr enforces exclusive ownership and is lightweight; std::shared_ptr allows
shared ownership via reference counting; std::weak_ptr breaks reference cycles. They call the
appropriate destructor automatically when the pointer goes out of scope. Using smart pointers eliminates most manual
delete calls, making code exception-safe and easier to reason about, which is foundational for robust
modern C++ applications.
🧠 What are Smart Pointers?
Smart pointers are C++ objects that automatically manage memory allocation and deallocation, preventing memory leaks and dangling pointers while providing safer alternatives to raw pointers.
#include <memory>
// Smart pointer automatically cleans up memory
std::unique_ptr<int> ptr = std::make_unique<int>(42);
std::cout << *ptr << std::endl; // Output: 42
Output:
42
Types of Smart Pointers
unique_ptr
Exclusive ownership of a resource
std::unique_ptr<int> ptr = std::make_unique<int>(10);
shared_ptr
Shared ownership with reference counting
std::shared_ptr<int> ptr = std::make_shared<int>(20);
weak_ptr
Non-owning observer to break cycles
std::weak_ptr<int> weak = shared_ptr;
Benefits
Automatic cleanup and safety
// No delete needed!
🔹 unique_ptr Example
std::unique_ptr provides exclusive ownership of a dynamically allocated object, automatically
deleting it when the pointer goes out of scope. It is non-copyable but movable, making it ideal for
resource management in a single-owner context. For example,
auto ptr = std::make_unique<Player>("Alice"); ensures the Player is destroyed
automatically. This eliminates memory leaks, enforces clear ownership semantics, and is essential for implementing
RAII in modern C++ applications.
#include <iostream>
#include <memory>
class Player {
public:
Player(std::string name) : name_(name) {
std::cout << "Player " << name_ << " created\n";
}
~Player() {
std::cout << "Player " << name_ << " destroyed\n";
}
void play() { std::cout << name_ << " is playing\n"; }
private:
std::string name_;
};
int main() {
std::unique_ptr<Player> player = std::make_unique<Player>("Alice");
player->play();
// Automatic cleanup when player goes out of scope
return 0;
}
Output:
Player Alice created
Alice is playing
Player Alice destroyed
🔹 shared_ptr Example
std::shared_ptr enables shared ownership of an object using reference counting, deleting it when
the last shared_ptr is destroyed. Multiple pointers can manage the same resource, making it suitable
for shared data structures. For example, auto sp1 = std::make_shared<int>(100); auto sp2 = sp1; shares
ownership. This simplifies lifetime management in complex scenarios but requires caution to avoid circular
references, which can be resolved with std::weak_ptr.
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptr1 = std::make_shared<int>(100);
std::cout << "Reference count: " << ptr1.use_count() << std::endl;
{
std::shared_ptr<int> ptr2 = ptr1; // Share ownership
std::cout << "Reference count: " << ptr1.use_count() << std::endl;
std::cout << "Value: " << *ptr2 << std::endl;
} // ptr2 goes out of scope
std::cout << "Reference count: " << ptr1.use_count() << std::endl;
return 0;
}
Output:
Reference count: 1
Reference count: 2
Value: 100
Reference count: 1
🔹 weak_ptr Example
std::weak_ptr holds a non-owning reference to an object managed by std::shared_ptr,
preventing circular references. It does not increase the reference count and must be converted to a
shared_ptr to access the object. For example, weak_ptr can break cycles in graph
structures. This ensures objects are properly deallocated when no strong references remain, enhancing memory safety
in interconnected data models like caches and observer patterns.
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> shared = std::make_shared<int>(42);
std::weak_ptr<int> weak = shared;
std::cout << "shared use_count: " << shared.use_count() << std::endl;
if (auto locked = weak.lock()) { // Convert to shared_ptr safely
std::cout << "Value: " << *locked << std::endl;
}
shared.reset(); // Release shared ownership
if (weak.expired()) {
std::cout << "Object has been destroyed" << std::endl;
}
return 0;
}
Output:
shared use_count: 1
Value: 42
Object has been destroyed