Memento in C++

Behavioral Medium

In one sentence: Memento takes a sealed snapshot of an object's state so it can be restored later, without letting anyone else peek inside the snapshot.

Real-world analogy

A tournament chess game runs past the session limit, so it's adjourned. The player to move doesn't play their move on the board — they write it on a slip, seal it in an envelope, and hand the envelope to the arbiter. The clocks stop and everyone goes to dinner.

The arbiter's job is to keep that envelope safe until tomorrow, and nothing else. They may not open it, may not read it, may not hint at it over breakfast. To them it's a sealed envelope with a game number written on the outside, and that number is the entire extent of what they're entitled to know about it.

Next morning the arbiter hands the envelope back, it's opened at the board, the sealed move goes on, and the game resumes exactly where it stopped. The only person who ever reads the contents is the player who wrote them.

Mapping the analogy to the pattern:

  • The player who seals the move → the Originator: the only one who can create the snapshot and the only one who can read it back.
  • The sealed envelope → the Memento, with its two interfaces at once — wide for its author, narrow (just the game number on the outside) for everybody else.
  • The arbiter holding it overnight → the Caretaker: stores it, keeps it safe, hands it back on request, and never operates on what's inside.
  • The arbiter being forbidden to open it → encapsulation preserved. In C++ that rule is the friend declaration: the wide interface is private, and the originator is the only class that can see it.
  • Resuming from the sealed moverestore(): the originator takes its own snapshot back and returns to the state it captured.

Another angle: a locked diary left with a friend while you travel. They can shelve it, carry it, and hand it back whenever you ask — but you hold the only key, so what's written inside stays yours.

The problem

You want a "load last checkpoint" button. Your Game object holds the current level, health, and gold — all private, as they should be.

  1. Copying the state means exposing it. To save it from outside, you'd have to add getters for every private field. Now every other class can read your internals, and the encapsulation you designed is gone.
  2. Who owns the snapshot? If the game keeps its own history, it grows a second job. If some other object keeps it, that object must understand the fields it's holding — and will break the day you add one.

The solution

Split the responsibility three ways:

  1. The originator (the Game) is the only one who can create a snapshot and the only one who can read it back. Saving and loading stay inside the class that owns the data.
  2. The memento is that snapshot object. To the outside world it's opaque — maybe a label and a timestamp, nothing more.
  3. The caretaker (a stack, a list, a save-slot menu) just stores mementos and hands them back on request. It never inspects them.

The heart of the pattern is that the memento has two interfaces at once: a wide one that lets the originator read everything back, and a narrow one — often nothing but a handle — that's all any other object can see. Get that split right and you've got the pattern; get it wrong and you've just written a public data bag.

The trick in C++ is the friend declaration: this is exactly the mechanism GoF recommends for the language. Make the memento's wide interface private and the originator its friend, leaving only the narrow interface public.

Note: GoF's second implementation point saves a lot of memory. When mementos are created and handed back in a predictable order — as they are in a command history list, where undo always walks backwards — a memento only needs to record the incremental change since the last one, not a full copy of the originator's state. The price is that you can no longer restore mementos in arbitrary order; you have to rewind through the history.

Structure

Structure of the Memento pattern Structure of the Memento pattern
Structure of the Memento pattern

Three participants, and the encapsulation boundary between them is the entire point:

  • Memento — stores as much or as little of the originator's internal state as the originator chooses, and protects that state from everyone else. This is where the two interfaces live. Here: SaveGame, whose fields are private with Game as its only friend.
  • Originator — creates a memento holding a snapshot of its current state, and uses a memento to restore itself. Here: Game, via save() and restore().
  • Caretaker — keeps mementos safe and hands them back. GoF is emphatic: it never operates on or examines a memento's contents. Here: the std::vector<std::unique_ptr<SaveGame>> in main(), which can read a save's label and nothing else.

Mementos are passive. Only the originator that produced one will ever set or read its state — and sometimes the caretaker never gives it back at all, because the originator never needs to rewind that far.

C++ example

Three save fields, one friend declaration, and a main() that stores snapshots without ever being able to read them.

#include <iostream>
#include <memory>
#include <string>
#include <vector>

// The memento: an opaque snapshot. Only Game can read what's inside.
class SaveGame {
public:
    SaveGame(std::string label, std::string level, int hp, int gold)
        : label_(std::move(label)), level_(std::move(level)), hp_(hp), gold_(gold) {}

    const std::string& label() const { return label_; }   // safe metadata for anyone

private:
    friend class Game;              // ...and Game is the only one who sees the rest
    std::string label_;
    std::string level_;
    int hp_;
    int gold_;
};

class Game {
public:
    void enter(const std::string& level) { level_ = level; }
    void takeDamage(int amount)          { hp_ -= amount; }
    void collect(int gold)               { gold_ += gold; }

    std::unique_ptr<SaveGame> save(const std::string& label) const {
        return std::make_unique<SaveGame>(label, level_, hp_, gold_);
    }
    void restore(const SaveGame& s) {
        level_ = s.level_;
        hp_    = s.hp_;
        gold_  = s.gold_;
    }

    void print() const {
        std::cout << level_ << " | hp " << hp_ << " | gold " << gold_ << "\n";
    }

private:
    std::string level_ = "Village";
    int hp_ = 100;
    int gold_ = 0;
};

int main() {
    Game game;
    std::vector<std::unique_ptr<SaveGame>> saves;   // caretaker: stores, never inspects

    game.enter("Forest");
    game.collect(30);
    saves.push_back(game.save("before boss"));
    std::cout << "checkpoint:  ";
    game.print();

    game.enter("Dragon's Lair");
    game.takeDamage(85);
    game.collect(5);
    std::cout << "after fight: ";
    game.print();

    game.restore(*saves.back());
    std::cout << "loaded '" << saves.back()->label() << "': ";
    game.print();
}
Output
checkpoint:  Forest | hp 100 | gold 30
after fight: Dragon's Lair | hp 15 | gold 35
loaded 'before boss': Forest | hp 100 | gold 30

The disastrous fight is fully rolled back, yet main() can only read the save's label — level_, hp_, and gold_ are invisible to it.

When should you use it?

GoF's applicability is two bullets joined by and — both have to be true, which is the part people skip:

  • A snapshot of some part of an object's state must be saved so the object can be restored to it later — undo, checkpoints, rollback of a transaction that failed partway.
  • And a direct interface for getting that state would leak implementation details and break the object's encapsulation. If the state is already public and there's nothing to protect, you don't need this pattern — you just need a copy.

Pros and cons

Pros (GoF's consequences)

  • It preserves encapsulation boundaries. State that only the originator should manage still gets stored outside it, without other objects seeing the originator's internals. This is the reason the pattern exists.
  • It simplifies the originator. In designs where the originator keeps every version clients asked for, all the storage management lands on the originator. Letting clients hold the state they requested keeps the originator lean — and means clients never have to tell it when they're done.
  • Snapshots are plain, inert data — easy to keep, name, order, or write to disk.

Cons

  • Mementos might be expensive — GoF says it outright. If the originator has to copy a lot of information, or clients create and return mementos constantly, the overhead is real. Unless capturing and restoring state is cheap, the pattern may simply be the wrong tool.
  • The narrow/wide interface split is hard to enforce in some languages. C++'s friend is one of the cleaner answers, and even it is a blunt instrument.
  • Hidden costs land on the caretaker. It's responsible for deleting the mementos it holds, but it has no idea how much state is inside one — so an otherwise lightweight caretaker can quietly turn into a memory hog.

Tip: If forged snapshots would be a problem, make the memento's constructor private too and construct it inside Game with new wrapped in a unique_ptr. You lose std::make_unique, but now nobody outside can fabricate a save file with a million gold.

How to remember it

Memory hook: "A sealed envelope only the author may open." Anyone can carry it, file it, or hand it back; nobody else can read it. That's the contrast with plain serialization, which is a sealed envelope with the flap torn off — it produces state anyone can read and edit. If the snapshot's contents are public, it isn't a memento.

  • Command — GoF's first stated pairing: commands use mementos to hold the state an undoable operation needs to reverse itself.
  • Iterator — GoF's second: a memento can carry the state of an iteration, and a cursor-style iterator is really a small memento of "where am I in this collection."
  • Prototype — sometimes a full clone of the object is a simpler snapshot than a purpose-built memento.
  • State — also about an object's state, but state objects change behavior, while a memento is inert stored data.