Composite in C++

Structural Medium

In one sentence: Composite builds objects into a tree that represents a part-whole hierarchy, and gives leaves and branches the same interface, so client code never has to ask "is this one thing or many?"

Real-world analogy

An order goes out in an army: take the hill. The general doesn't repeat it ten thousand times. She gives it once, to a division commander. He gives the same order to his brigades, they give it to companies, companies to squads, and eventually a private hears "take the hill" and starts walking up it.

Every level received the identical verb — carry out this order — and every level either does the thing itself or passes it down and lets the levels below do it. It works the same in reverse. Ask "report your strength" and a private answers one; a squad asks its members and adds up; a division asks its brigades and adds up. Nobody at the top writes a special case for how deep the chain happens to be today.

The crucial part is what the general does not need to know. Handed a unit, she gives it the order without first checking whether it's a single sentry or an entire corps. If she had to ask, every order would come with a stack of ifs, and reorganising the army would break all of them.

Mapping the analogy to the pattern:

  • The private → the Leaf, who carries out the order personally and has nobody below.
  • The squad, company, division → the Composite, which holds subordinate units and satisfies the order by passing it down.
  • "Carry out this order" / "report your strength" → the Component operation, declared once for everything in the chain.
  • Each unit forwarding to the units under it → the recursion over children; the tree walks itself, so the caller never writes the loop.
  • The general → the Client, holding a unit through the shared interface and never asking which kind it is.

Another angle: a playlist that can contain playlists. Press play on a single track or on a folder of nested folders — same button, music comes out. Ask for the running time and each level sums whatever is beneath it.

The problem

You need the total price of an order that may contain products, boxes of products, and boxes of boxes — nested to any depth.

  1. Client code has to branch constantly. Every loop becomes if (it's a product) ... else if (it's a box) ..., and the box branch needs its own recursion.
  2. The depth is unknown. You can't write a fixed number of nested loops for a structure the user builds at runtime.
  3. Type checks spread. Adding a "gift wrap" container means finding and updating every if in the codebase.

GoF's example is a drawing editor where users group shapes into pictures, and group those pictures into bigger pictures. The naive design gives you Line, Text and a separate container class — and then every piece of code that touches the drawing has to keep straight which is which, even though the user thinks of them identically.

The solution

Give the simple thing and the container the same base interface, then let the container do its work by asking its children:

  1. Declare a Component interface with the operation you care about (price(), render(), size()).
  2. A leaf implements it directly — a product just returns its own price.
  3. A composite implements it by looping over its children and combining their results, and its children are themselves Components.

The recursion now lives inside the tree, not in the client. Client code calls one method on the root and gets an answer for the whole structure.

Note: GoF's implementation section flags a decision that catches everyone: where do add() and remove() live? Declaring them on Component buys transparency — every node has one interface, so clients truly can't tell leaves from composites — at the cost of safety, since Leaf::add() has to fail somehow at run time. Declaring them only on Composite buys compile-time safety and loses transparency. GoF explicitly picks transparency as the pattern's default, and suggests making add/remove fail loudly (an exception) rather than silently. Another GoF tip worth stealing: don't put the child container in Component just because the interface lives there — every leaf then pays for a child list it will never use.

Structure

Structure of the Composite pattern Structure of the Composite pattern
Structure of the Composite pattern

Four participants, one of which is recursive:

  • Component — declares the interface for everything in the tree, implements default behavior where it makes sense, and declares the child-management interface (the transparency-versus-safety call above). Optionally exposes a parent reference. In the example: Item.
  • Leaf — a node with no children; it does the primitive work itself. In the example: Product.
  • Composite — stores child components, implements the child-management operations, and implements the shared operation by delegating to its children. In the example: Box.
  • Client — manipulates the whole structure through the Component interface, and never learns the difference. In the example: main.

The collaboration is the recursion: a request to a Leaf is handled on the spot; a request to a Composite is usually forwarded to its children, with extra work done before or after.

C++ example

An order of nested boxes that prices and prints itself.

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

// The interface shared by a single product and a box of things.
class Item {
public:
    virtual ~Item() = default;
    virtual double price() const = 0;
    virtual void print(const std::string& indent) const = 0;
};

// Leaf: knows its own price and nothing else.
class Product : public Item {
public:
    Product(std::string name, double price)
        : name_(std::move(name)), price_(price) {}
    double price() const override { return price_; }
    void print(const std::string& indent) const override {
        std::cout << indent << name_ << " $" << price_ << "\n";
    }
private:
    std::string name_;
    double price_;
};

// Composite: owns children and delegates the work to them.
class Box : public Item {
public:
    explicit Box(std::string label) : label_(std::move(label)) {}
    void add(std::unique_ptr<Item> item) { items_.push_back(std::move(item)); }

    double price() const override {
        double total = 0;
        for (const auto& item : items_) total += item->price();
        return total;
    }
    void print(const std::string& indent) const override {
        std::cout << indent << "Box " << label_ << " ($" << price() << ")\n";
        for (const auto& item : items_) item->print(indent + "  ");
    }
private:
    std::string label_;
    std::vector<std::unique_ptr<Item>> items_;
};

int main() {
    auto accessories = std::make_unique<Box>("accessories");
    accessories->add(std::make_unique<Product>("USB cable", 5));
    accessories->add(std::make_unique<Product>("Charger", 15));

    auto order = std::make_unique<Box>("order #42");
    order->add(std::make_unique<Product>("Keyboard", 60));
    order->add(std::move(accessories));

    order->print("");
    std::cout << "Total: $" << order->price() << "\n";
}
Output
Box order #42 ($80)
  Keyboard $60
  Box accessories ($20)
    USB cable $5
    Charger $15
Total: $80

main calls price() once on the root and gets 80 — the tree walked itself, and no client code ever checked whether an item was a product or a box. (This example takes the safety side of the trade-off: add() is declared on Box only. Move it up to Item and you get GoF's transparent version.)

When should you use it?

GoF's applicability list is two bullets, and both must hold. Use Composite when:

  • You need to represent a part-whole hierarchy of objects — things made of things made of things: file systems, UI widget trees, org charts, nested orders, scene graphs.
  • You want clients to ignore the difference between an individual object and a composition of objects, and to treat everything in the structure uniformly.

If only the first is true — you have a tree but clients genuinely need to handle nodes differently — you have a tree, not a Composite.

Pros and cons

Pros (GoF's consequences)

  • It defines class hierarchies of primitive and composite objects. Primitives compose into composites, which compose again, recursively — so wherever client code expects a primitive, it can take a whole subtree instead.
  • It makes the client simple. No tag-and-case-statement functions over node types, no hand-rolled recursion; clients normally don't know, and shouldn't care, which kind of node they hold.
  • It makes new component types easy to add. A new Leaf or Composite subclass works with existing structures and existing client code without edits.

Cons

  • It can make your design overly general. The flip side of easy extension: you can't use the type system to say "this composite only accepts these children" — you're stuck with run-time checks.
  • The shared interface tends to drift toward the lowest common denominator, becoming too vague to be useful.
  • Deep trees make debugging and performance reasoning harder; a single price() call may touch thousands of nodes. GoF's suggested remedy is caching traversal results in the composite, which then needs cache invalidation up the parent chain.
  • Ownership needs a decision. In C++ the usual rule is that a composite deletes its children — unless leaves are immutable and shared.

How to remember it

Memory hook: "Boxes all the way down, and the box is also a thing." If the container and the contained answer the same question, it's a Composite. Its near neighbour is Decorator, and the difference is arity plus purpose: a Composite holds many children and aggregates them; a Decorator holds exactly one child and adds behavior to it. GoF puts it as a one-liner — a decorator is a degenerate composite with a single component, except it isn't there for aggregation at all.

  • Decorator — often used with Composite. When they're combined they share a parent class, which means decorators have to support Add, Remove and GetChild too.
  • Chain of Responsibility — the child-to-parent link in a composite is the natural chain to pass a request along.
  • Flyweight — lets you share components between trees, at the price of giving up parent pointers.
  • Iterator — the standard way to traverse a composite without exposing its internals.
  • Visitor — pulls an operation that would otherwise be smeared across every Leaf and Composite class into one place.
  • Builder — handy for assembling a complex composite tree step by step.