C++ smart pointers and ownership
Use unique_ptr for single ownership, shared_ptr for shared ownership, and weak_ptr to observe.
#include <iostream>
#include <memory>
#include <string>
struct Resource {
std::string name;
explicit Resource(std::string n) : name(std::move(n)) { std::cout << "create " << name << "\n"; }
~Resource() { std::cout << "destroy " << name << "\n"; }
};
int main() {
auto a = std::make_unique<Resource>("A");
auto b = std::move(a);
std::cout << "a is null: " << (a == nullptr) << "\n";
{
auto shared = std::make_shared<Resource>("S");
auto second = shared;
std::cout << "owners: " << shared.use_count() << "\n";
}
std::weak_ptr<Resource> weak;
{
auto temp = std::make_shared<Resource>("T");
weak = temp;
std::cout << "alive inside: " << !weak.expired() << "\n";
}
std::cout << "alive after: " << !weak.expired() << "\n";
std::cout << "end of main\n";
}create A a is null: 1 create S owners: 2 destroy S create T alive inside: 1 destroy T alive after: 0 end of main destroy A
Explanation
Smart pointers manage heap objects automatically, following RAII: the object is destroyed when its owner goes away. std::unique_ptr has exactly one owner, cannot be copied, and can be moved to transfer ownership. After a move the source is null. It costs nothing extra compared with a raw pointer.
std::shared_ptr allows several owners and keeps a reference count. The object is destroyed when the last owner is gone. std::weak_ptr observes a shared object without owning it, so it does not keep it alive, and expired tells whether it still exists. Prefer unique_ptr by default and make_unique and make_shared over new.
Explore the dry run
#include <iostream>#include <memory>#include <string> struct Resource { std::string name; explicit Resource(std::string n) : name(std::move(n)) { std::cout << "create " << name << "\n"; } ~Resource() { std::cout << "destroy " << name << "\n"; }}; int main() { auto a = std::make_unique<Resource>("A"); auto b = std::move(a); std::cout << "a is null: " << (a == nullptr) << "\n"; { auto shared = std::make_shared<Resource>("S"); auto second = shared; std::cout << "owners: " << shared.use_count() << "\n"; } std::weak_ptr<Resource> weak; { auto temp = std::make_shared<Resource>("T"); weak = temp; std::cout << "alive inside: " << !weak.expired() << "\n"; } std::cout << "alive after: " << !weak.expired() << "\n"; std::cout << "end of main\n";}Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| make_unique creates the object and a unique_ptr that owns it. | — | create A | |
| Ownership moves from a to b. unique_ptr cannot be copied, only moved. | — | create A | |
| After the move a no longer owns anything. | — | create A a is null: 1 | |
| Copying a shared_ptr adds an owner. | reference count = 2 | create A a is null: 1 | |
| Two shared_ptrs own S. When both leave the block, S is destroyed. | reference count = 2 | create A a is null: 1 create S owners: 2 destroy S | |
| weak observes T without owning it. | reference count = 2 | create A a is null: 1 create S owners: 2 destroy S | |
| While temp exists, the object is alive. | reference count = 2 | create A a is null: 1 create S owners: 2 destroy S create T alive inside: 1 destroy T | |
| T was destroyed when temp left its block, so the weak_ptr is expired. | reference count = 2 | create A a is null: 1 create S owners: 2 destroy S create T alive inside: 1 destroy T alive after: 0 | |
| b still owns A. It is destroyed when main returns, after this line. | reference count = 2 | create A a is null: 1 create S owners: 2 destroy S create T alive inside: 1 destroy T alive after: 0 end of main destroy A |
Cost and common mistakes
unique_ptr costs nothing extra; shared_ptr updates a reference count (atomic) on copies.
- Two objects holding shared_ptrs to each other form a cycle and are never destroyed. Break the cycle with a weak_ptr.
- Never create two unrelated smart pointers from the same raw pointer. Both would try to delete the object.
- Pass a smart pointer by value only when transferring or sharing ownership. Otherwise pass a reference or a raw pointer to the object.
Try it yourself
What would happen if you tried to copy a unique_ptr?
Reveal answer and reasoning
The program would not compile, because unique_ptr's copy constructor is deleted. You must use std::move.