Prototype in C++

Creational Medium

In one sentence: Prototype gives every object a virtual clone() method, so you can copy it through a base-class pointer without knowing its real type — and without slicing it in half.

Real-world analogy

An office print room keeps a drawer of master forms, each one already filled in with the parts that never change: the letterhead, the department codes, the legal small print at the bottom. Nobody retypes those from a blank sheet.

You ask for a copy by name — "the expense claim" — and the master goes through the copier. The copier reproduces whatever is on the sheet, at full fidelity, without reading a word of it; it doesn't need to know an expense claim from a purchase order. Then you write your own numbers onto the copy, and the master goes back into the drawer untouched.

A new kind of form joins the system the same way any other did: someone fills one in properly and drops it in the drawer. No new machine, no new procedure, no one rewrites the copier.

Mapping the analogy to the pattern:

  • A master form in the drawer → the Prototype: a live, fully configured instance, not a class. "Expense claim" and "purchase order" are ConcretePrototypes.
  • Running it through the copierclone(). The copier works at the exact fidelity of the sheet it was handed, which is what virtual dispatch buys you — copying the top half of the page and calling it a form is slicing.
  • Writing your numbers on the copy → mutating the clone afterwards; GoF's Initialize step, done after cloning because clone() takes no arguments.
  • The drawer, labelled by form name → the prototype registry, or prototype manager. Adding a master to it registers a new kind of object at run time.
  • Asking for "a copy of the expense claim" → the Client, which never learns the concrete type and never consults a blueprint.

Another angle: a locksmith cutting a spare key. They trace your existing key, not the lock's specification — the original is the template, and the copy comes out identical without anyone knowing what door it opens.

The problem

You have a drawing canvas holding std::unique_ptr<Shape> objects — circles, text boxes, whatever plugins added later. The user selects them all and presses Ctrl+D to duplicate.

You cannot simply write Shape copy = *original;. That is object slicing: Shape is smaller than Circle, so the radius is silently chopped off and the copy loses its type. What survives is a bare Shape, not a circle.

The obvious workaround is worse:

  1. Ask what it is. A chain of dynamic_cast tests — "is it a Circle? then new Circle(*c)" — which you must extend for every new shape, in every place you copy.
  2. You can't extend it anyway. A shape from a plugin isn't in your if chain, so it silently fails to copy.

The solution

Let each class copy itself, since it is the only code that knows its own type:

  1. Declare virtual std::unique_ptr<Shape> clone() const = 0; on the base.
  2. Each concrete class overrides it with a single line: return std::make_unique<Circle>(*this); — the compiler-generated copy constructor runs at the exact type, so nothing is sliced.
  3. The caller writes shape->clone() and gets back a full-fidelity duplicate through a base pointer.

This is the classic C++ prototype idiom, and it is worth memorising: virtual dispatch picks the type, the copy constructor does the work.

GoF's framing goes one step further than "copy this object". A prototype is a living template: you keep one fully configured instance around and stamp out copies of it, so a "kind of object" becomes a value you can register at run time rather than a class you have to compile in. In GoF's music editor, every tool in the palette is the same GraphicTool class holding a different prototype — a whole note, a half note — and adding a new kind of note adds no class at all.

Note: GoF flags two implementation problems the clone() one-liner hides. The first is shallow versus deep copy: C++'s generated copy constructor copies members one by one, so raw pointers and shared_ptrs end up shared between original and copy. That is fine for a flyweight-ish resource and a bug for anything the copy is supposed to own. The second is initialising the clone: you cannot pass setup values through clone() without breaking its uniform signature (different prototypes need different numbers of parameters), so GoF's advice is to clone first and then call an Initialize operation or ordinary setters on the copy.

Structure

Structure of the Prototype pattern Structure of the Prototype pattern
Structure of the Prototype pattern

Three participants, and the entire pattern rests on a single operation:

  • Prototype — declares the interface for cloning itself. Shape, with its pure virtual clone().
  • ConcretePrototype — implements the clone operation, returning a copy of itself at its own exact type. Circle and Label.
  • Client — creates a new object by asking a prototype to clone itself, and never learns what type came back. The duplication loop in main() below.

Notice what is missing compared with the other creational patterns: there is no factory or creator hierarchy at all. The prototype and the thing that produces products are the same object.

C++ example

Duplicating a selection on a canvas without ever asking what the shapes are.

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

class Shape {
public:
    virtual ~Shape() = default;

    // The prototype operation: every shape knows how to copy itself.
    virtual std::unique_ptr<Shape> clone() const = 0;
    virtual void draw() const = 0;

    void moveBy(int dx, int dy) { x_ += dx; y_ += dy; }

protected:
    Shape(int x, int y) : x_(x), y_(y) {}
    int x_, y_;
};

class Circle : public Shape {
public:
    Circle(int x, int y, int radius) : Shape(x, y), radius_(radius) {}

    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Circle>(*this);   // copy ctor, exact type, no slicing
    }
    void draw() const override {
        std::cout << "Circle at (" << x_ << "," << y_ << ") r=" << radius_ << "\n";
    }

private:
    int radius_;
};

class Label : public Shape {
public:
    Label(int x, int y, std::string text) : Shape(x, y), text_(std::move(text)) {}

    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Label>(*this);
    }
    void draw() const override {
        std::cout << "Label at (" << x_ << "," << y_ << ") \"" << text_ << "\"\n";
    }

private:
    std::string text_;
};

int main() {
    std::vector<std::unique_ptr<Shape>> canvas;
    canvas.push_back(std::make_unique<Circle>(10, 10, 5));
    canvas.push_back(std::make_unique<Label>(0, 40, "Sale!"));

    // Duplicate the selection, offset to the right. No idea what's in it.
    std::vector<std::unique_ptr<Shape>> copies;
    for (const auto& shape : canvas) {
        auto copy = shape->clone();
        copy->moveBy(100, 0);
        copies.push_back(std::move(copy));
    }

    std::cout << "Originals:\n";
    for (const auto& s : canvas) s->draw();
    std::cout << "Duplicates:\n";
    for (const auto& s : copies) s->draw();
}
Output
Originals:
Circle at (10,10) r=5
Label at (0,40) "Sale!"
Duplicates:
Circle at (110,10) r=5
Label at (100,40) "Sale!"

The duplicates kept their radius and their text — proof that each object was copied at its real type, and the originals were left untouched.

When should you use it?

GoF's applicability opens with the same clause as Abstract Factory — when the system should be independent of how its products are created and represented — and then adds three specifics. Use Prototype when:

  • The classes to instantiate are only known at run time, for example because they arrive by dynamic loading. Code that cannot name a constructor can still call clone().
  • You want to avoid a hierarchy of factory classes that mirrors the hierarchy of products. This is GoF's "reduced subclassing" benefit, and the reason Prototype and Factory Method are alternatives rather than partners.
  • Instances differ only by a handful of state combinations. Register one ready-made prototype per combination and clone it, instead of re-running the same setup code with the right arguments every time.
  • Filling in an object's state is expensive or restricted — a database round trip, a parse, a heavy computation. Do it once into a prototype and clone from then on; the registry becomes a cache.
  • In C++ specifically: you need to copy objects held behind a base-class pointer, where *a = *b would slice the object in half.

Pros and cons

Pros (GoF's consequences)

  • Products can be added and removed at run time. A new kind of product enters the system by registering an instance — no compilation, no new class.
  • New "kinds" of object come from varying values, not from writing classes. Configure an instance differently, register it as a prototype, and you have effectively defined a new class without programming; GoF notes this can cut the number of classes in a system dramatically.
  • New kinds can also come from varying structure. A composite object — GoF's example is a subcircuit built from parts — can itself be a prototype, provided its clone() is a deep copy.
  • Reduced subclassing. There is no Creator hierarchy paralleling the product hierarchy, because objects duplicate themselves.
  • In C++, it is the only correct way to copy a polymorphic object through a base pointer, and it is often faster than rebuilding one when initialisation is expensive.

Cons

  • Every subclass must implement clone() — GoF calls this the pattern's main liability. It is painful to retrofit onto classes that already exist, and a class that forgets returns a copy of the wrong type, a bug the compiler will not catch.
  • Cloning is hard when internals don't cooperate: members that cannot be copied, or circular references between objects, make a correct clone() genuinely difficult.
  • Deep versus shallow copy is your decision every time, and the default C++ copy constructor quietly picks shallow.

Note: Add override on every clone() and consider a covariant return type in leaf classes (std::unique_ptr<Circle> clone() const) — the compiler rejects the covariant form for unique_ptr, so many codebases keep a private doClone() returning the exact type and a public clone() wrapping it. When the set of prototypes isn't fixed, GoF suggests a prototype manager: a registry mapping a name to a prototype, which clients query before cloning, so the catalogue can be extended at run time.

How to remember it

Memory hook: "Copy the filled-in form instead of filling in a blank one." You keep one completed example and photocopy it. The sibling test: Factory Method subclasses a creator to build a new object; Prototype asks an existing object to duplicate itself, so there is no creator hierarchy at all.

  • Abstract Factory — GoF calls these competing patterns, but they combine well: a factory can hold one prototype per product and clone them on demand instead of subclassing per family.
  • Composite and Decorator — GoF notes that designs leaning heavily on either usually benefit from Prototype, since cloning a whole assembled tree is exactly what these designs need.
  • Factory Method — the alternative that creates by subclassing a creator. Prototype avoids that hierarchy, at the price of usually needing an Initialize operation to set up the copy.
  • Memento — also captures object state, but for later restoration rather than for producing a second live object.