Public inheritance states an is-a relationship: every derived object must honor the observable promises of the base. Protected or private inheritance models implementation reuse and is usually clearer as composition. Keep hierarchies shallow and give each virtual operation one stable meaning.
Pass the base by reference or pointer to preserve dynamic type. Passing by value slices away the derived portion.
#include <iostream>
#include <memory>
struct Shape {
virtual ~Shape() = default;
virtual double area() const = 0;
};
struct Square final : Shape {
explicit Square(double side) : side{side} {}
double area() const override { return side * side; }
double side;
};
int main() {
std::unique_ptr<Shape> shape = std::make_unique<Square>(4.0);
std::cout << shape->area() << '\n';
}
16
Multiple inheritance works well for small independent interfaces. Shared base state can create a diamond; virtual inheritance provides one shared base subobject but increases construction and design complexity.
Base subobjects initialize before members and derived construction; destruction occurs in reverse. Initialize the base and members in initializer lists, understanding that member declaration order, not list order, controls initialization. A derived constructor cannot repair an invalid base that was constructed incorrectly.
Do not expose base data as protected merely for convenience. Protected behavior or composed collaborators create a narrower dependency. Mark leaf classes or operations final when extension would violate an invariant or when the design intentionally closes the hierarchy.
If a base type is intended for polymorphic use, deleting a derived object through a base pointer must invoke the complete destructor chain. Give the base a virtual destructor, use override on derived virtual methods, and prefer smart pointers for ownership.
Inheritance should represent substitutability, not merely shared fields. Favor composition when the derived type must disable, reinterpret, or reject behavior promised by the base.
#include <iostream>
#include <memory>
struct Base { virtual ~Base() { std::cout << "base\n"; } };
struct Derived : Base { ~Derived() override { std::cout << "derived\n"; } };
int main() {
std::unique_ptr<Base> item = std::make_unique<Derived>();
}
derived
base
The virtual base destructor preserves complete cleanup when ownership is expressed through the base type.
Try this next
0 of 2 completed
Deleting a derived object through a base pointer must run both destructors. Without a virtual destructor, that deletion has undefined behavior.
It prevents a diamond-shaped hierarchy from containing two separate copies of the same base subobject.
Use composition when one object merely uses or owns another. Inheritance should represent a genuine substitutable “is-a” relationship.
Explore 500+ free tutorials across 20+ languages and frameworks.