Smart Pointers: unique_ptr and shared_ptr
Applying RAII to heap memory, so you never write a raw delete yourself.
What you'll learn
- Create heap-allocated objects with std::make_unique instead of raw new
- Explain that std::unique_ptr automatically deletes its object when it goes out of scope
- Distinguish unique_ptr's single-owner model from shared_ptr's reference-counted, shared-ownership model
Explanation
std::unique_ptr (from <memory>) is an RAII wrapper around a raw pointer to heap memory: it owns the object it points to, and its destructor calls delete automatically when the unique_ptr itself goes out of scope -- you never write a raw delete for memory owned by a unique_ptr. The idiomatic way to create one is std::make_unique<Widget>(1), which allocates a Widget and wraps it in a unique_ptr<Widget>, rather than writing std::unique_ptr<Widget>(new Widget(1)) yourself.
As its name suggests, unique_ptr models single ownership: exactly one unique_ptr owns a given object at a time. It can't be copied (copying a unique_ptr is a compile error), only moved -- transferring ownership from one unique_ptr to another, after which the original no longer owns anything. This is deliberately restrictive: it makes "who is responsible for deleting this" unambiguous, at compile time, for every object a unique_ptr manages.
std::shared_ptr relaxes that restriction for cases where genuinely shared ownership is needed: multiple shared_ptrs can point to the same object simultaneously, and it uses reference counting internally -- the object is deleted only once the last shared_ptr owning it is destroyed or reset, not before. This is more flexible than unique_ptr but has real runtime overhead (maintaining that count) and complexity (a reference cycle between shared_ptrs can leak memory, since the count never reaches zero) that unique_ptr doesn't have -- the general modern-C++ guidance is to reach for unique_ptr by default, and only use shared_ptr when you genuinely need multiple simultaneous owners.
Guided lab
Predict: A unique_ptr's automatic cleanup
Read this program and predict exactly what it prints, in order.
#include <iostream>
#include <memory>
class Widget {
public:
Widget(int id) : id_(id) {
std::cout << "Widget " << id_ << " created" << std::endl;
}
~Widget() {
std::cout << "Widget " << id_ << " destroyed" << std::endl;
}
int id() const { return id_; }
private:
int id_;
};
int main() {
std::unique_ptr<Widget> w = std::make_unique<Widget>(1);
std::cout << "Using widget " << w->id() << std::endl;
return 0;
}Stuck? Get a hint.
Common mistakes
- Writing a raw `new`/`delete` pair for a resource that could instead be owned by a unique_ptr, losing RAII's automatic cleanup guarantee.
- Trying to copy a unique_ptr directly, forgetting it's move-only -- ownership transfers, it never duplicates.
- Reaching for shared_ptr by default 'just in case,' instead of using unique_ptr unless multiple simultaneous owners are genuinely needed.
Knowledge check
Takeaway
Prefer std::make_unique for heap allocation by default -- its destructor deletes automatically, and single ownership keeps cleanup unambiguous; reach for shared_ptr only when genuinely shared ownership is needed.
Summary
unique_ptr applies RAII to heap memory with single, move-only ownership and automatic deletion; shared_ptr allows shared ownership via reference counting, at the cost of runtime overhead and cycle-leak risk.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.