Reusing a Class by Extending It
You've built classes with constructors and cleaned them up with destructors. Inheritance is the next step: instead of copying the members of one class into another, you declare that a new class is a specialized version of an existing one and let it inherit everything automatically.
The existing class is the base class (or parent); the new one is the derived class (or child). A derived class starts with all of the base's data and behavior, then adds or changes what makes it different. This is C++'s primary tool for the "is-a" relationship: a Dog is an Animal, a SavingsAccount is a BankAccount.
Basic Syntax: class Derived : public Base
You inherit by following the derived class name with a colon, an access specifier, and the base class name. The most common form is public inheritance.
Dog never declares name or eat(), yet both work on a Dog object because they were inherited from Animal. The derived class is free to add members like bark() that the base knows nothing about.
protected: Members for Children Only
A private base member is not accessible inside the derived class - inheritance does not break encapsulation. When you want the base's internals hidden from the outside world but available to subclasses, use the protected access specifier.
Think of the three levels as concentric rings: private is "only this class," protected is "this class and its descendants," and public is "everyone." See access-specifiers for the full picture.
Constructor and Destructor Order
A derived object contains a base subobject, and that base part must be alive before the derived part is built. So construction runs base-first, destruction runs derived-first (the exact reverse). If the base needs constructor arguments, you pass them through the member-initializer list.
The output makes the ordering concrete:
Animal ctor: Rex // base built first
Dog ctor // then the derived part
Dog dtor // destroyed in reverse...
Animal dtor: Rex // ...base last
If you forget the : Animal(n) and the base has no default constructor, the code won't compile - C++ has no idea how to build the base part. A base class you intend to inherit from should almost always declare a destructor (and, as the next page shows, often a virtual one).
Overriding: Redefining a Base Method
A derived class can replace an inherited method by declaring one with the same signature. You can still reach the original through Base::method().
This is plain name hiding, not polymorphism: which describe() runs is decided at compile time by the static type of the variable. That's a critical limitation - if you call through a Shape& or Shape* that actually points at a Circle, you'll still get Shape::describe(). Fixing that requires virtual, which is the topic of the next page.
Watch Out for Object Slicing
Because a base reference or pointer can refer to a derived object, it's tempting to copy a derived object into a base-typed variable. Don't - the derived part gets sliced off.
a is a genuine Animal, not a Dog wearing a base label, so breed simply doesn't exist on it. To work with derived objects polymorphically you must use a base reference (Animal&) or pointer (Animal*), never a base value. Slicing is silent - it compiles cleanly and just quietly drops data - which makes it one of the easier inheritance bugs to ship.
Common Mistakes to Avoid
- Expecting
privatebase members to be reachable in the child. They aren't. Useprotectedfor data the derived class legitimately needs, and keep truly internal stateprivate. - Forgetting to forward base constructor arguments. If the base has no default constructor, you must call it explicitly in the derived constructor's initializer list (
: Base(args)). - Slicing a derived object into a base value. Copying a
Doginto anAnimaldrops everythingDog-specific. Pass and store base references or pointers instead. - Overusing inheritance for code reuse. Inheritance models "is-a." If the relationship is really "has-a" (a
Carhas anEngine), prefer composition - a member object - over inheriting.
Next: Virtual Functions
The override you just saw was resolved at compile time, so calling through a base pointer ignored the derived version. The next page, virtual-functions, introduces the virtual keyword and override - the mechanism that makes the runtime type decide which method runs, unlocking true polymorphism and explaining why base classes need virtual destructors.
Frequently Asked Questions
What is inheritance in C++?
Inheritance lets you define a new class (the derived class) in terms of an existing one (the base class). The derived class automatically gets the base's data members and member functions, and can add new ones or replace existing behavior. It models an "is-a" relationship - a Dog is an Animal - and is the main way C++ reuses and extends code across class hierarchies.
What is the difference between public and private inheritance in C++?
With public inheritance (class Dog : public Animal), the base's public interface stays public in the derived class, so a Dog is-a Animal and can be used wherever an Animal is expected. With private inheritance, the inherited members become private - the derived class reuses the base's implementation but is not substitutable for it. Public inheritance is by far the common case; reach for private only for "implemented-in-terms-of" reuse.
In what order do constructors and destructors run with inheritance in C++?
Constructors run base-first: the base class is fully constructed before the derived constructor body executes. Destructors run in the exact reverse order - derived first, then base. This guarantees that while a derived object is being built up or torn down, every part it depends on already exists (or still exists).