Smart pointers make heap ownership explicit and leak-free; they replace almost every use of new and delete.
Smart pointers (header <memory>) are RAII classes that own a heap object and delete it automatically.
std::unique_ptr<T> is sole ownership. Create it with std::make_unique<T>(args...). It can't be copied, only moved, so ownership transfers are visible in the code: auto b = std::move(a); leaves a null. It has no overhead over a raw pointer, which makes it the default choice. It also handles arrays (std::unique_ptr<int[]>) and custom deleters, for example to fclose a FILE*.
std::shared_ptr<T> is shared ownership with reference counting: each copy increments a count, and the object is deleted when the last shared_ptr goes away. Create it with std::make_shared<T>(args...), which puts the object and its control block in one allocation. The count is updated atomically, so it's safe to copy shared pointers across threads (the pointed-to object itself is not synchronized). Copying costs an atomic increment, so pass shared_ptr by reference or don't pass it at all when the callee doesn't need ownership.
std::weak_ptr<T> observes a shared object without keeping it alive. Call lock() to get a shared_ptr if the object still exists. Use it to break cycles: two objects holding shared_ptrs to each other never reach a count of zero and leak.
Guidelines:
- Prefer
unique_ptr; reach forshared_ptronly when ownership is genuinely shared. - Functions that just use an object should take
T&orT*, not a smart pointer. - Never construct two smart pointers from the same raw pointer: each would delete it.
Example
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
struct Texture {
explicit Texture(std::string n) : name{std::move(n)} {}
~Texture() { std::cout << "free " << name << '\n'; }
std::string name;
};
void inspect(const Texture& t) { std::cout << "inspect " << t.name << '\n'; } // no ownership
int main() {
auto solo = std::make_unique<Texture>("grass"); // sole owner
inspect(*solo);
std::unique_ptr<Texture> owner = std::move(solo); // ownership moved; solo is null
auto shared = std::make_shared<Texture>("sky");
std::vector<std::shared_ptr<Texture>> users{shared, shared};
std::cout << shared.use_count() << '\n'; // 3
std::weak_ptr<Texture> watcher = shared; // doesn't affect the count
shared.reset();
users.clear(); // last owner gone: "free sky"
std::cout << watcher.expired() << '\n'; // 1
} // "free grass"Remember
unique_ptrby default: zero overhead, move-only ownershipshared_ptrfor genuinely shared ownership;weak_ptrto observe and break cycles- Create with
make_unique/make_shared; passT&to functions that don't own
Watch out for
- Two objects holding
shared_ptrs to each other: the cycle keeps both alive forever. Make one side aweak_ptr.