Home/C++/Polymorphism and composition

Polymorphism and composition

Substitution, virtual destruction and owned behavior

Runtime polymorphism lets a caller use behavior through a stable interface. A virtual destructor is necessary when deleting derived objects through a base owner. Mark overrides explicitly so a const or parameter mismatch becomes a compiler diagnostic rather than an accidental unrelated method.

cpp
struct Operation {
 virtual ~Operation()=default;
 virtual Int apply(Int value) const=0;
};

Passing a derived object by base value slices away its derived state. A reference or pointer preserves dynamic dispatch. An abstract base cannot be stored by value, so a vector of unique_ptr<Operation> expresses both heterogeneous behavior and exclusive lifetime. Prefer composition when behavior is a collaborator rather than an is-a relationship; a Pipeline owns operations without inheriting from each operation type.

An interface has behavioral promises beyond method signatures. Each operation must accept the values the pipeline supplies and preserve the error contract. Liskov substitution fails if one implementation unexpectedly mutates shared state or narrows accepted arguments. A const virtual method promises no mutation through its own members unless marked mutable; it does not make external pointees immutable.

cpp
std::vector<std::unique_ptr<Operation>> operations;
// Each operation transforms the previous result, in insertion order.

Throwing from a later operation should propagate normally. Do not catch every failure and return zero, because zero may be a valid result and would erase error information. Owning behavior makes lifetime obvious: destroying the pipeline destroys its operations exactly once. A null operation is rejected at insertion rather than causing a deferred crash during execution. This is a small composition exercise, not a requirement to build class hierarchies for plain records.

Put it into practice

Implement the exercise in a full-screen workspace, then run its tests.

Open practice
Next lessonErrors, exceptions, and resource guarantees