Lesson 7 · 7 of 20
unique_ptr and shared_ptr · C++
Use C++ smart pointers, unique_ptr and shared_ptr, so memory frees itself: songs with a single owner or shared by several playlists.

A smart pointer is an object that holds a pointer and applies RAII: when it goes away, it frees the memory. No more new and delete: std::make_unique<Song>("Demo take") creates the song and returns a `std::unique_ptr` that owns it. Song's destructor prints a message so you can see when each one is freed.
A unique_ptr has a single owner, so it cannot be copied, but it can be moved: std::move(demo) hands ownership to keeper and leaves demo empty (in a condition, an empty pointer is false). keeper.reset() frees the song right there; otherwise it would have been freed at the end of main. The arrow works just as with a plain pointer: demo->title.
`std::shared_ptr` allows several owners and keeps count of them, which use_count() reports. hit and the two playlists, rock and party, share the same song, so there are 3 owners. After clearing rock and calling hit.reset() there is one left, and the song lives on in party.
The song is freed when its last owner goes away: the party vector, at the end of main. That is why "(freed Summer Nights)" appears after "End of main". Use unique_ptr by default, as it costs nothing over a plain pointer, and shared_ptr only when ownership really is shared.
Code · main.cpp
#include <iostream>#include <memory>#include <string>#include <vector>struct Song { std::string title; explicit Song(std::string t) : title(t) {} ~Song() { std::cout << " (freed " << title << ")\n"; }};int main() { // unique_ptr: a single owner that frees the object when it goes away. std::unique_ptr<Song> demo = std::make_unique<Song>("Demo take"); std::cout << "Unique: " << demo->title << "\n"; std::unique_ptr<Song> keeper = std::move(demo); // ownership moves; it cannot be copied std::cout << "demo is " << (demo ? "full" : "empty") << " now\n"; keeper.reset(); // freed right here // shared_ptr: several owners and a count of them. auto hit = std::make_shared<Song>("Summer Nights"); std::vector<std::shared_ptr<Song>> rock, party; rock.push_back(hit); party.push_back(hit); std::cout << "Owners of " << hit->title << ": " << hit.use_count() << "\n"; rock.clear(); hit.reset(); std::cout << "Left in party: " << party[0]->title << " (owners: " << party[0].use_count() << ")\n"; std::cout << "End of main\n";} // party is destroyed: its last owner frees the songOutput · c++ -std=c++17 main.cpp -o main && ./main
Unique: Demo take demo is empty now (freed Demo take) Owners of Summer Nights: 3 Left in party: Summer Nights (owners: 1) End of main (freed Summer Nights)
Key points
- Create objects with
std::make_uniqueandstd::make_shared, notnew. unique_ptr: one owner; it moves withstd::moveand cannot be copied.shared_ptr: several owners; the object lives while any of them remains.reset()lets go of the object; an empty pointer isfalse.- Prefer
unique_ptrunless you need to share.
Exercise
Create a third playlist, chill, add hit to it before rock.clear() and print use_count() after each step. When is the song freed now?