Chapter 4b - Inheritance & Polymorphism
Inheritanceβ
Inheritance lets a derived class (child) reuse and extend the behavior of a base class (parent). The child gets all public and protected members of the parent automatically.
// Base class
class Animal {
public:
string name;
int age;
Animal(string n, int a) : name(n), age(a) {}
void eat() {
cout << name << " is eating." << endl;
}
void sleep() {
cout << name << " is sleeping." << endl;
}
};
// Derived class
class Dog : public Animal {
public:
string breed;
Dog(string n, int a, string b) : Animal(n, a), breed(b) {}
void bark() {
cout << name << " says: Woof!" << endl;
}
};
int main() {
Dog rex("Rex", 3, "Labrador");
rex.eat(); // inherited from Animal
rex.sleep(); // inherited from Animal
rex.bark(); // defined in Dog
}
Access in Inheritanceβ
| Member in Base | public inheritance | protected inheritance | private inheritance |
|---|---|---|---|
public | public in child | protected in child | private in child |
protected | protected in child | protected in child | private in child |
private | not accessible | not accessible | not accessible |
Use public inheritance almost always. The others are rare.
π§ͺ Inheritance
Calling the Parent Constructorβ
The derived class constructor must forward arguments to the base class constructor using the initializer list.
class Cat : public Animal {
public:
bool isIndoor;
Cat(string n, int a, bool indoor)
: Animal(n, a), // call base constructor first
isIndoor(indoor)
{}
void describe() {
cout << name << " (age " << age << ") β "
<< (isIndoor ? "indoor" : "outdoor") << " cat." << endl;
}
};
Mini quiz β inheritance setupβ
Inheritance
1 / 3This means Dogβ¦
class Dog : public Animal {
Pick an answer to see if you are right.
Method Overridingβ
A derived class can override a base class method by redefining it with the same signature.
class Animal {
public:
string name;
Animal(string n) : name(n) {}
void speak() {
cout << name << " makes a sound." << endl;
}
};
class Dog : public Animal {
public:
Dog(string n) : Animal(n) {}
void speak() { // overrides Animal::speak
cout << name << " says: Woof!" << endl;
}
};
class Cat : public Animal {
public:
Cat(string n) : Animal(n) {}
void speak() {
cout << name << " says: Meow!" << endl;
}
};
Virtual Functions & Polymorphismβ
Without virtual, C++ decides which method to call based on the pointer type (static dispatch). With virtual, it decides based on the actual object type at runtime (dynamic dispatch).
class Animal {
public:
string name;
Animal(string n) : name(n) {}
virtual void speak() { // <-- virtual keyword
cout << name << " makes a sound." << endl;
}
};
class Dog : public Animal {
public:
Dog(string n) : Animal(n) {}
void speak() override { // <-- override keyword (C++11)
cout << name << " says: Woof!" << endl;
}
};
class Cat : public Animal {
public:
Cat(string n) : Animal(n) {}
void speak() override {
cout << name << " says: Meow!" << endl;
}
};
int main() {
Animal* animals[3];
animals[0] = new Dog("Rex");
animals[1] = new Cat("Whiskers");
animals[2] = new Dog("Buddy");
for (int i = 0; i < 3; i++) {
animals[i]->speak(); // calls the right version for each object
}
for (int i = 0; i < 3; i++) delete animals[i];
}
Rex says: Woof!
Whiskers says: Meow!
Buddy says: Woof!
override keywordoverride (C++11) tells the compiler "this method must override a virtual method in the base." If you mistype the name, the compiler will catch it. Always use override.
π§ͺ Polymorphism
Challenge: triangle areaβ
Finish triangleArea for a right triangle with legs a and b (area = a * b / 2).
| a | b | Return |
|---|---|---|
3 | 4 | 6 |
6 | 8 | 24 |
Virtual Destructorsβ
If you ever delete a derived object through a base pointer, the base class destructor must be virtual, otherwise only the base destructor runs and the derived part leaks.
class Base {
public:
virtual ~Base() { cout << "Base destroyed." << endl; }
};
class Derived : public Base {
public:
~Derived() override { cout << "Derived destroyed." << endl; }
};
int main() {
Base* obj = new Derived();
delete obj; // Derived destroyed. then Base destroyed. (correct order)
}
If a class has any virtual function, give it a virtual ~Destructor().
Abstract Classes & Pure Virtual Functionsβ
A pure virtual function has no implementation in the base class and forces derived classes to provide one. A class with at least one pure virtual function is abstract β you cannot instantiate it directly.
class Shape {
public:
string color;
Shape(string c) : color(c) {}
virtual double area() = 0; // pure virtual
virtual double perimeter() = 0; // pure virtual
void describe() {
cout << color << " shape | area=" << area()
<< " perimeter=" << perimeter() << endl;
}
};
red shape | area=78.5397 perimeter=31.4159
blue shape | area=24 perimeter=20
π§ͺ Abstract Shape
Mini quiz β virtual and abstractβ
Virtual destructors and abstract classes
1 / 3If you delete a derived object through a base pointer, the base destructor should beβ¦
Pick an answer to see if you are right.
The Inheritance Hierarchyβ
Shape (abstract)
/ \
Circle Rectangle
|
Square (Rectangle with w == h)
class Square : public Rectangle {
public:
Square(string c, double side) : Rectangle(c, side, side) {}
};
Multiple Inheritanceβ
C++ allows a class to inherit from more than one base class. Use carefully β it can lead to the "diamond problem."
class Flyable {
public:
virtual void fly() { cout << "Flying!" << endl; }
};
class Swimmable {
public:
virtual void swim() { cout << "Swimming!" << endl; }
};
class Duck : public Flyable, public Swimmable {
public:
void quack() { cout << "Quack!" << endl; }
};
π§ͺ Multiple inheritance
Mini quiz β hierarchy and multiple inheritanceβ
Hierarchies
1 / 3In a typical shape tree, Circle isβ¦
Pick an answer to see if you are right.
Exercisesβ
Extend the Shape hierarchy with a Triangle class. Add a method isLarger(Shape* other) to the base class that compares areas.