DMarlon Studio

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.

C++ · unique_ptr and shared_ptr in DMarlon Studio: the example's code and its output in the terminal

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 song

Output · 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_unique and std::make_shared, not new.
  • unique_ptr: one owner; it moves with std::move and cannot be copied.
  • shared_ptr: several owners; the object lives while any of them remains.
  • reset() lets go of the object; an empty pointer is false.
  • Prefer unique_ptr unless 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?