Inheritance
Inheritance lets a new class build on an existing one: a SavingsAccount is an Account with interest; a Button is a Widget you can click. The derived class gets the base class's members and adds or specialises behaviour.
Deriving a class
#include <iostream> #include <string> #include <utility> class Account { public: Account(std::string owner, double balance) : balance_(balance), owner_(std::move(owner)) {} void deposit(double amount) { if (amount > 0) balance_ += amount; } double balance() const { return balance_; } const std::string& owner() const { return owner_; } protected: double balance_; // accessible to derived classes private: std::string owner_; }; class SavingsAccount : public Account { public: SavingsAccount(std::string owner, double balance, double rate) : Account(std::move(owner), balance), rate_(rate) {} // construct the base first void add_interest() { balance_ += balance_ * rate_; } private: double rate_; }; int main() { SavingsAccount s{"Ana", 1000, 0.03}; s.deposit(500); // inherited from Account s.add_interest(); // added by SavingsAccount std::cout << s.owner() << ": " << s.balance() << '\n'; }
Ana: 1545
class SavingsAccount : public Account means every SavingsAccount is an Account. The derived constructor calls the base constructor in its initialiser list.
Access levels
| Member is | Accessible from the class itself | From derived classes | From outside |
|---|---|---|---|
public | yes | yes | yes |
protected | yes | yes | no |
private | yes | no | no |
Prefer keeping data private even in base classes and giving derived classes protected member functions to work with; protected data lets every derived class break the base class's rules.
Construction and destruction order
#include <iostream> struct Base { Base() { std::cout << "Base constructed\n"; } ~Base() { std::cout << "Base destroyed\n"; } }; struct Derived : Base { Derived() { std::cout << "Derived constructed\n"; } ~Derived() { std::cout << "Derived destroyed\n"; } }; int main() { Derived d; }
Base constructed Derived constructed Derived destroyed Base destroyed
The base part is built first and destroyed last — like the foundations of a building.
Hiding vs. overriding
A derived class can declare a function with the same name as one in its base. Without virtual (next lesson), the version called depends on the declared type, not the real object — usually not what you want:
#include <iostream> struct Animal { void speak() const { std::cout << "...\n"; } }; struct Dog : Animal { void speak() const { std::cout << "Woof\n"; } }; int main() { Dog d; d.speak(); const Animal& a = d; a.speak(); // calls Animal::speak: no virtual dispatch }
Woof ...
Using base-class members
Call a base version explicitly with Base::function(), and bring base constructors into the derived class with using:
#include <iostream> #include <string> class Logger { public: explicit Logger(std::string prefix) : prefix_(std::move(prefix)) {} void log(const std::string& msg) const { std::cout << prefix_ << msg << '\n'; } private: std::string prefix_; }; class TimestampLogger : public Logger { public: using Logger::Logger; // inherit the constructors void log(const std::string& msg) const { Logger::log("[12:00] " + msg); // reuse the base version } }; int main() { TimestampLogger log{"app: "}; log.log("started"); }
app: [12:00] started
Inheritance or composition?
Inheritance models "is-a" relationships and is the right tool for polymorphism. When one class merely uses another — a Car has an Engine — make it a member instead (composition). Composition is simpler, more flexible and easier to change, so prefer it unless you need to treat derived objects through a base-class interface.
final
Marking a class final forbids deriving from it; marking a virtual function final forbids further overriding. Use it when a class isn't designed to be extended.
Try it yourself
Create a base class Employee with a name and salary, and a derived Manager with a bonus. Print both people's total pay.
Priya: 52000 Tom: 69000