Class hierarchies are built using inheritance, a core mechanism that allows a derived class to absorb the members of a base class. This relationship promotes code reuse and models real-world hierarchies.
Basic Syntax
A derived class is declared by following its name with a colon, an access specifier, and the base class name:
class Vehicle {
public:
void start() {
std::cout << "Vehicle started.\n";
}
};
class Car : public Vehicle {
public:
void honk() {
std::cout << "Car honks!\n";
}
};
int main() {
Car myCar;
myCar.start(); // inherited
myCar.honk(); // own method
}
Output:
Vehicle started.
Car honks!
Access Control During Inheritance
The visibility of inherited members depends on the access specifier:
publicinheritance keeps base class access levels unchanged.protectedinheritance turns public and protected base members into protected members of the derived class.privateinheritacne makes all base members private in the derived class.
Moreover, the friend keyword can grant external functions or classes access to private and protected parts of a class.
Constructors and Destructors
When a derived object is created, the base class constructor runs first, followed by the derived constructor. Destruction happens in reverse order.
class Parent {
public:
Parent() { std::cout << "Parent constructed\n"; }
~Parent() { std::cout << "Parent destructed\n"; }
};
class Child : public Parent {
public:
Child() { std::cout << "Child constructed\n"; }
~Child() { std::cout << "Child destructed\n"; }
};
Creating a Child object produces:
Parent constructed
Child constructed
Child destructed
Parent destructed
Multiple Inheritance
A derived class can inherit from more then one base class. Name clashes must be resolved explicitly.
class Printer {
public:
void operate() { std::cout << "Printing...\n"; }
};
class Scanner {
public:
void operate() { std::cout << "Scanning...\n"; }
};
class AllInOne : public Printer, public Scanner {
public:
void operate() {
Printer::operate();
Scanner::operate();
}
};
Calling operate() on an AllInOne object outputs:
Printing...
Scanning...
Virtual Inheritance
When multiple inheritance leads to a commmon base class (the diamond problem), virtual inheritance ensures only one copy of the base exists.
class Core {
public:
void identify() { std::cout << "Core\n"; }
};
class ModuleA : virtual public Core {};
class ModuleB : virtual public Core {};
class System : public ModuleA, public ModuleB {};
Usage:
System sys;
sys.identify(); // unambiguous, single Core subobject
Output:
Core
Careful use of inheritance reduces duplication and clarifies design. Choosing between ordinary and virtual inheritance depends on the intended object model and how shared base classes should behave.