C++ Smart Pointers
C++ smart pointers are objects that store pointers and automatically clean up the dynamic memory they own. They help modern C++ programs avoid many common new and delete mistakes.
Smart pointers are defined in the <memory> header. Instead of remembering to call delete yourself, you choose the smart pointer type that matches how the object should be owned.
Why Smart Pointers Matter
Manual dynamic memory works, but it is easy to forget cleanup, delete the same pointer twice, or use memory after it has been released. Smart pointers use an important C++ idea: when an object goes out of scope, its destructor runs automatically. The smart pointer destructor releases the owned object at the right time.
In most modern C++ code, prefer smart pointers over raw owning pointers. Raw pointers can still be useful for observing an object without owning it, but they should usually not be responsible for cleanup.
Common Smart Pointer Types
| Type | Use it when |
|---|---|
std::unique_ptr<T> |
One smart pointer owns the object. |
std::shared_ptr<T> |
Several smart pointers share ownership of the same object. |
std::weak_ptr<T> |
You need to refer to an object managed by shared_ptr without increasing its owner count. |
unique_ptr
A std::unique_ptr has exclusive ownership. This means only one unique_ptr owns the object at a time. When the unique_ptr is destroyed, it deletes the object automatically.
Create one with std::make_unique. This is usually clearer and safer than writing new yourself.
#include <iostream>
#include <memory>
#include <string>
class Task {
public:
std::string name;
Task(std::string taskName) {
name = taskName;
std::cout << "Creating " << name << std::endl;
}
~Task() {
std::cout << "Destroying " << name << std::endl;
}
void show(void) {
std::cout << "Task: " << name << std::endl;
}
};
int main(void) {
std::unique_ptr<Task> current = std::make_unique<Task>("backup");
current->show();
std::unique_ptr<Task> next = std::move(current);
if (current == nullptr) {
std::cout << "current is empty" << std::endl;
}
next->show();
return 0;
}
Output:
Creating backup
Task: backup
current is empty
Task: backup
Destroying backup
The expression std::make_unique<Task>("backup") creates a dynamic Task and returns a unique_ptr that owns it. The arrow operator, ->, lets you call methods on the object it points to.
You cannot copy a unique_ptr, because that would create two owners for one object. To transfer ownership, use std::move. After the move, current is empty and next owns the Task.
shared_ptr
A std::shared_ptr allows shared ownership. The object stays alive as long as at least one shared_ptr owns it. When the last owner is destroyed or reset, the object is deleted.
#include <iostream>
#include <memory>
int main(void) {
std::shared_ptr<int> first = std::make_shared<int>(42);
std::cout << "Value: " << *first << std::endl;
std::cout << "Owners: " << first.use_count() << std::endl;
{
std::shared_ptr<int> second = first;
std::cout << "Owners inside block: " << first.use_count() << std::endl;
}
std::cout << "Owners after block: " << first.use_count() << std::endl;
return 0;
}
Output:
Value: 42
Owners: 1
Owners inside block: 2
Owners after block: 1
Here, first owns an int. Inside the nested block, second is copied from first, so both smart pointers share the same object. When the block ends, second is destroyed and the owner count goes back down.
weak_ptr
A std::weak_ptr works with shared_ptr, but it does not own the object. It is useful when one object needs to refer to another without keeping it alive. Before using a weak_ptr, you normally convert it to a temporary shared_ptr and check that the object still exists.
Which One Should You Choose?
- Use
std::unique_ptrby default for dynamic objects with one clear owner. - Use
std::shared_ptronly when shared ownership is truly needed. - Use
std::weak_ptrto break shared ownership cycles or observe a shared object without owning it. - Prefer
std::make_uniqueandstd::make_sharedinstead of directly writingnew.
Takeaway: smart pointers let C++ manage owned dynamic memory automatically, so your code can focus on ownership instead of manual cleanup.
