Inheritance
Inheritance and Composition
Section titled “Inheritance and Composition”Inheritance and composition are fundamental OOP concepts that enable code reuse and establish “is-a” and “has-a” relationships between classes. This chapter covers how to create class hierarchies in C++.
What is Inheritance?
Section titled “What is Inheritance?”Inheritance allows a class (derived class) to inherit properties and behaviors from another class (base class):
┌─────────────────────────────────────────────────────────────┐│ Inheritance Hierarchy │├─────────────────────────────────────────────────────────────┤│ ││ ┌─────────────┐ ││ │ Animal │ ← Base Class ││ │ (name, age) │ ││ │ +eat() │ ││ │ +sleep() │ ││ └──────┬──────┘ ││ │ ││ ┌──────────────┼──────────────┐ ││ ▼ ▼ ▼ ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ Dog │ │ Cat │ │ Bird │ ││ │ +bark() │ │ +meow() │ │ +sing() │ ││ └──────────┘ └──────────┘ └──────────┘ ││ Derived Classes ││ │└─────────────────────────────────────────────────────────────┘Basic Inheritance
Section titled “Basic Inheritance”// Base classclass Animal {protected: // Accessible in derived classes std::string name; int age;
public: Animal(const std::string& n, int a) : name(n), age(a) {}
void eat() { std::cout << name << " is eating\n"; }
void sleep() { std::cout << name << " is sleeping\n"; }
void display() const { std::cout << "Name: " << name << ", Age: " << age << "\n"; }};
// Derived classclass Dog : public Animal {private: std::string breed;
public: Dog(const std::string& n, int a, const std::string& b) : Animal(n, a), breed(b) {}
void bark() { std::cout << name << " says: Woof!\n"; }
void displayInfo() const { display(); // Call base class method std::cout << "Breed: " << breed << "\n"; }};
int main() { Dog dog("Buddy", 3, "Golden Retriever"); dog.eat(); // Inherited from Animal dog.sleep(); // Inherited from Animal dog.bark(); // Specific to Dog dog.displayInfo();}Access Specifiers in Inheritance
Section titled “Access Specifiers in Inheritance”| Base Access | Public Inheritance | Protected Inheritance | Private Inheritance |
|---|---|---|---|
public | public | protected | private |
protected | protected | protected | private |
private | inaccessible | inaccessible | inaccessible |
class Base {public: int publicVar;protected: int protectedVar;private: int privateVar;};
class PublicDerived : public Base { // publicVar is public // protectedVar is protected // privateVar is inaccessible};
class PrivateDerived : private Base { // All become private};Most common: Public inheritance (“is-a” relationship)
Constructors in Inheritance
Section titled “Constructors in Inheritance”Base class constructors are called before derived class constructors:
class Base {public: Base() { std::cout << "Base constructor\n"; } ~Base() { std::cout << "Base destructor\n"; }};
class Derived : public Base {public: Derived() { std::cout << "Derived constructor\n"; } ~Derived() { std::cout << "Derived destructor\n"; }};
int main() { Derived d;}Output:
Base constructorDerived constructorDerived destructorBase destructorCalling Specific Base Constructor
Section titled “Calling Specific Base Constructor”class Animal {public: Animal(const std::string& n) : name(n) {}
protected: std::string name;};
class Dog : public Animal {public: Dog(const std::string& n, const std::string& b) : Animal(n), breed(b) {} // Calls Animal(n)
private: std::string breed;};Function Overriding
Section titled “Function Overriding”Derived classes can override base class functions:
class Shape {public: virtual void draw() { std::cout << "Drawing a shape\n"; }
virtual ~Shape() {}};
class Circle : public Shape {public: void draw() override { // Override keyword (C++11) std::cout << "Drawing a circle\n"; }};
class Square : public Shape {public: void draw() override { std::cout << "Drawing a square\n"; }};
int main() { Shape* shapes[] = {new Circle(), new Square()};
for (auto* s : shapes) { s->draw(); // Polymorphic call }}Output:
Drawing a circleDrawing a squareVirtual Functions
Section titled “Virtual Functions”Use virtual to enable polymorphism:
class Base {public: void nonVirtual() { std::cout << "Base non-virtual\n"; }
virtual void virtualFunc() { std::cout << "Base virtual\n"; }
virtual ~Base() {}};
class Derived : public Base {public: void nonVirtual() { std::cout << "Derived non-virtual\n"; }
void virtualFunc() override { std::cout << "Derived virtual\n"; }};
int main() { Base* ptr = new Derived(); ptr->nonVirtual(); // "Base non-virtual" (static binding) ptr->virtualFunc(); // "Derived virtual" (dynamic binding) delete ptr;}Composition (“Has-a” Relationship)
Section titled “Composition (“Has-a” Relationship)”Composition models “has-a” relationships where one class contains another:
class Engine {private: int horsepower;
public: Engine(int hp) : horsepower(hp) {}
void start() { std::cout << "Engine started\n"; } int getHorsepower() const { return horsepower; }};
class Car {private: std::string model; Engine engine; // Car "has an" Engine
public: Car(const std::string& m, int hp) : model(m), engine(hp) {}
void start() { std::cout << model << " starting...\n"; engine.start(); }};
int main() { Car car("Tesla Model 3", 283); car.start();}Inheritance vs Composition
Section titled “Inheritance vs Composition”| Inheritance | Composition |
|---|---|
| ”is-a” relationship | ”has-a” relationship |
| Tightly coupled | Loosely coupled |
| Harder to change at runtime | Flexible, can change at runtime |
| Use when true “is-a” | Use when “has-a” or “uses” |
Guidelines:
- Prefer composition over inheritance
- Use inheritance for true “is-a” relationships
- Use composition for “has-a” relationships
// Inheritance: Dog IS an Animalclass Dog : public Animal { };
// Composition: Car HAS an Engineclass Car { Engine engine; // Not inheritance};Multiple Inheritance
Section titled “Multiple Inheritance”A class can inherit from multiple base classes:
class Printable {public: virtual void print() const = 0;};
class Loggable {public: virtual void log() const = 0;};
class Document : public Printable, public Loggable {public: void print() const override { std::cout << "Printing document\n"; }
void log() const override { std::cout << "Logging document\n"; }};Diamond Problem
Section titled “Diamond Problem”When a class inherits from two classes that both inherit from a common base:
A / \ B C \ / DSolution: Virtual inheritance
class A {public: int value;};
class B : virtual public A {};class C : virtual public A {};
class D : public B, public C {}; // Only one AComplete Example: Shape Hierarchy
Section titled “Complete Example: Shape Hierarchy”#include <iostream>#include <vector>#include <memory>
// Abstract base classclass Shape {protected: std::string color;
public: Shape(const std::string& c) : color(c) {}
// Pure virtual function - must be overridden virtual double area() const = 0; virtual void draw() const = 0;
virtual ~Shape() {}};
class Circle : public Shape {private: double radius;
public: Circle(double r, const std::string& c) : Shape(c), radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
void draw() const override { std::cout << "Drawing circle with radius " << radius << " and color " << color << "\n"; }};
class Rectangle : public Shape {private: double width, height;
public: Rectangle(double w, double h, const std::string& c) : Shape(c), width(w), height(h) {}
double area() const override { return width * height; }
void draw() const override { std::cout << "Drawing rectangle " << width << "x" << height << " with color " << color << "\n"; }};
int main() { std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(5, "Red")); shapes.push_back(std::make_unique<Rectangle>(4, 6, "Blue"));
double totalArea = 0; for (const auto& shape : shapes) { shape->draw(); totalArea += shape->area(); }
std::cout << "Total area: " << totalArea << "\n";
return 0;}Key Takeaways
Section titled “Key Takeaways”- Inheritance creates “is-a” relationships between classes
- Use
publicinheritance for typical inheritance - Base class constructors run before derived class constructors
- Use
virtualfunctions for polymorphism - Use
overridekeyword to catch errors - Prefer composition (“has-a”) over inheritance (“is-a”)
- Use virtual inheritance to solve the diamond problem
- Abstract classes with pure virtual functions define interfaces
Next Steps
Section titled “Next Steps”Let’s continue learning about polymorphism in more detail.
Next Chapter: 11_polymorphism.md - Polymorphism and Virtual Functions