Inheritance and Polymorphism
Inheritance and Polymorphism¶
1. What is Inheritance?¶
Inheritance is when a new class (child) inherits properties and methods from an existing class (parent).
#include <iostream>
#include <string>
// Parent class (base class)
class Animal {
public:
std::string name;
void eat() {
std::cout << name << " is eating." << std::endl;
}
void sleep() {
std::cout << name << " is sleeping." << std::endl;
}
};
// Child class (derived class)
class Dog : public Animal {
public:
void bark() {
std::cout << name << ": Woof woof!" << std::endl;
}
};
class Cat : public Animal {
public:
void meow() {
std::cout << name << ": Meow!" << std::endl;
}
};
int main() {
Dog dog;
dog.name = "Buddy";
dog.eat(); // Inherited method
dog.bark(); // Dog's own method
Cat cat;
cat.name = "Whiskers";
cat.sleep(); // Inherited method
cat.meow(); // Cat's own method
return 0;
}
2. Inheritance Access Specifiers¶
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
// public inheritance (most common)
class PublicDerived : public Base {
// publicVar -> public
// protectedVar -> protected
// privateVar -> inaccessible
};
// protected inheritance
class ProtectedDerived : protected Base {
// publicVar -> protected
// protectedVar -> protected
// privateVar -> inaccessible
};
// private inheritance
class PrivateDerived : private Base {
// publicVar -> private
// protectedVar -> private
// privateVar -> inaccessible
};
Access Specifier Summary¶
| Parent Member | public inheritance | protected inheritance | private inheritance |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | inaccessible | inaccessible | inaccessible |
3. Constructor and Destructor Call Order¶
#include <iostream>
class Base {
public:
Base() {
std::cout << "Base constructor" << std::endl;
}
~Base() {
std::cout << "Base destructor" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived constructor" << std::endl;
}
~Derived() {
std::cout << "Derived destructor" << std::endl;
}
};
int main() {
Derived d;
return 0;
}
Output:
Base constructor
Derived constructor
Derived destructor
Base destructor
Calling Parent Constructor¶
#include <iostream>
#include <string>
class Person {
protected:
std::string name;
int age;
public:
Person(std::string n, int a) : name(n), age(a) {
std::cout << "Person constructor" << std::endl;
}
};
class Student : public Person {
private:
int studentId;
public:
// Call parent constructor
Student(std::string n, int a, int id)
: Person(n, a), studentId(id) { // Call in initializer list
std::cout << "Student constructor" << std::endl;
}
void show() const {
std::cout << "Name: " << name << ", Age: " << age
<< ", Student ID: " << studentId << std::endl;
}
};
int main() {
Student s("Alice", 20, 20210001);
s.show();
return 0;
}
4. Function Overriding¶
Child class redefines parent's function.
#include <iostream>
class Animal {
public:
void speak() {
std::cout << "Animal makes a sound." << std::endl;
}
};
class Dog : public Animal {
public:
void speak() { // Override
std::cout << "Woof woof!" << std::endl;
}
};
class Cat : public Animal {
public:
void speak() { // Override
std::cout << "Meow!" << std::endl;
}
};
int main() {
Animal a;
Dog d;
Cat c;
a.speak(); // Animal makes a sound.
d.speak(); // Woof woof!
c.speak(); // Meow!
return 0;
}
Calling Parent Function¶
class Dog : public Animal {
public:
void speak() {
Animal::speak(); // Call parent function
std::cout << "Woof woof!" << std::endl;
}
};
5. Virtual Functions¶
Call the appropriate function at runtime (dynamic binding).
Problem: Static Binding¶
#include <iostream>
class Animal {
public:
void speak() {
std::cout << "Animal sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() {
std::cout << "Woof woof!" << std::endl;
}
};
int main() {
Dog dog;
Animal* ptr = &dog;
ptr->speak(); // "Animal sound" (problem!)
return 0;
}
Solution: virtual Keyword¶
#include <iostream>
class Animal {
public:
virtual void speak() { // Add virtual
std::cout << "Animal sound" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() override { // override (C++11, optional)
std::cout << "Woof woof!" << std::endl;
}
};
class Cat : public Animal {
public:
void speak() override {
std::cout << "Meow!" << std::endl;
}
};
int main() {
Dog dog;
Cat cat;
Animal* ptr1 = &dog;
Animal* ptr2 = &cat;
ptr1->speak(); // Woof woof! (correct!)
ptr2->speak(); // Meow! (correct!)
return 0;
}
override Keyword (C++11)¶
class Base {
public:
virtual void foo(int x) {}
};
class Derived : public Base {
public:
void foo(int x) override {} // OK
// void foo(double x) override {} // Error! Signature mismatch
// void bar() override {} // Error! No bar in parent
};
final Keyword (C++11)¶
class Base {
public:
virtual void foo() final {} // Cannot override further
};
class Derived : public Base {
public:
// void foo() override {} // Error! final function
};
// Prevent class inheritance
class FinalClass final {};
// class Derived2 : public FinalClass {}; // Error!
6. Virtual Destructor¶
Base class destructor must be virtual.
#include <iostream>
class Base {
public:
Base() { std::cout << "Base created" << std::endl; }
virtual ~Base() { std::cout << "Base destroyed" << std::endl; } // virtual!
};
class Derived : public Base {
private:
int* data;
public:
Derived() {
data = new int[100];
std::cout << "Derived created" << std::endl;
}
~Derived() {
delete[] data;
std::cout << "Derived destroyed" << std::endl;
}
};
int main() {
Base* ptr = new Derived();
delete ptr; // Thanks to virtual, Derived destructor is also called
return 0;
}
Output:
Base created
Derived created
Derived destroyed
Base destroyed
7. Pure Virtual Functions and Abstract Classes¶
Pure Virtual Functions¶
class Shape {
public:
// Pure virtual function (= 0)
virtual double getArea() const = 0;
virtual double getPerimeter() const = 0;
virtual ~Shape() = default;
};
// Shape shape; // Error! Cannot instantiate abstract class
Implementing Abstract Classes¶
#include <iostream>
#include <cmath>
class Shape {
public:
virtual double getArea() const = 0;
virtual double getPerimeter() const = 0;
virtual void draw() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double getArea() const override {
return M_PI * radius * radius;
}
double getPerimeter() const override {
return 2 * M_PI * radius;
}
void draw() const override {
std::cout << "Drawing circle. Radius: " << radius << std::endl;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() const override {
return width * height;
}
double getPerimeter() const override {
return 2 * (width + height);
}
void draw() const override {
std::cout << "Drawing rectangle. "
<< width << " x " << height << std::endl;
}
};
int main() {
Circle c(5);
Rectangle r(4, 3);
Shape* shapes[] = {&c, &r};
for (Shape* s : shapes) {
s->draw();
std::cout << " Area: " << s->getArea() << std::endl;
std::cout << " Perimeter: " << s->getPerimeter() << std::endl;
}
return 0;
}
8. Multiple Inheritance¶
Can inherit from multiple parent classes.
#include <iostream>
class Flyable {
public:
void fly() {
std::cout << "Flying." << std::endl;
}
};
class Swimmable {
public:
void swim() {
std::cout << "Swimming." << std::endl;
}
};
class Duck : public Flyable, public Swimmable {
public:
void quack() {
std::cout << "Quack quack!" << std::endl;
}
};
int main() {
Duck duck;
duck.fly(); // From Flyable
duck.swim(); // From Swimmable
duck.quack(); // Duck's own
return 0;
}
Diamond Problem¶
#include <iostream>
class Animal {
public:
int age;
};
class Mammal : public Animal {};
class Bird : public Animal {};
// Diamond problem
class Bat : public Mammal, public Bird {
// age is inherited twice!
};
int main() {
Bat bat;
// bat.age = 5; // Error! Ambiguous
bat.Mammal::age = 5; // Explicit specification
bat.Bird::age = 10;
return 0;
}
Solution with Virtual Inheritance¶
#include <iostream>
class Animal {
public:
int age;
};
class Mammal : virtual public Animal {}; // virtual inheritance
class Bird : virtual public Animal {}; // virtual inheritance
class Bat : public Mammal, public Bird {
// Only one age exists
};
int main() {
Bat bat;
bat.age = 5; // OK!
std::cout << bat.age << std::endl;
return 0;
}
9. Interface Pattern¶
A class with only pure virtual functions.
#include <iostream>
#include <string>
// Interface
class Printable {
public:
virtual void print() const = 0;
virtual ~Printable() = default;
};
class Serializable {
public:
virtual std::string serialize() const = 0;
virtual void deserialize(const std::string& data) = 0;
virtual ~Serializable() = default;
};
// Implement multiple interfaces
class Document : public Printable, public Serializable {
private:
std::string content;
public:
Document(const std::string& c) : content(c) {}
void print() const override {
std::cout << "Document content: " << content << std::endl;
}
std::string serialize() const override {
return "DOC:" + content;
}
void deserialize(const std::string& data) override {
if (data.substr(0, 4) == "DOC:") {
content = data.substr(4);
}
}
};
int main() {
Document doc("Hello, World!");
Printable* p = &doc;
p->print();
Serializable* s = &doc;
std::cout << s->serialize() << std::endl;
return 0;
}
10. RTTI (Run-Time Type Information)¶
dynamic_cast¶
#include <iostream>
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
public:
void derivedOnly() {
std::cout << "Derived only function" << std::endl;
}
};
int main() {
Base* base = new Derived();
// Safe downcasting
Derived* derived = dynamic_cast<Derived*>(base);
if (derived) {
derived->derivedOnly();
}
Base* base2 = new Base();
Derived* derived2 = dynamic_cast<Derived*>(base2);
if (derived2 == nullptr) {
std::cout << "Cast failed" << std::endl;
}
delete base;
delete base2;
return 0;
}
typeid¶
#include <iostream>
#include <typeinfo>
class Animal {
public:
virtual ~Animal() = default;
};
class Dog : public Animal {};
class Cat : public Animal {};
int main() {
Animal* a1 = new Dog();
Animal* a2 = new Cat();
std::cout << typeid(*a1).name() << std::endl; // Dog related
std::cout << typeid(*a2).name() << std::endl; // Cat related
if (typeid(*a1) == typeid(Dog)) {
std::cout << "a1 is a Dog." << std::endl;
}
delete a1;
delete a2;
return 0;
}
11. Summary¶
| Concept | Description |
|---|---|
class Derived : public Base |
Inheritance |
virtual |
Virtual function (dynamic binding) |
override |
Explicit override |
final |
Prevent inheritance/override |
= 0 |
Pure virtual function |
| Abstract class | Contains pure virtual functions |
virtual ~Base() |
Virtual destructor |
dynamic_cast |
Safe downcasting |
Next Step¶
Let's learn about STL containers in 10_STL_Containers.md!