C++: Polymorphism

Last updated: 2026-08-26

In Lesson 31, we learned about inheritance — letting derived classes reuse base class code.

But in real-world scenarios, you need to use a base class pointer to uniformly handle different derived class objects.

This is polymorphism — "one interface, multiple implementations."


1. What Is Polymorphism?

(1) 1.1 Polymorphism in Everyday Life

Real-Life Example Program Equivalent
You tell a dog to "speak," it barks Calling speak() on a base class pointer, but executing the derived class version
You tell a cat to "speak," it meows The same function call produces different behavior for different objects

The essence of polymorphism: Using a base class pointer to point to a derived class object, so that calling a virtual function dynamically binds to the derived class implementation.

(2) 1.2 Why Do We Need Polymorphism?

Without polymorphism (tedious):

▶ Example 2: Object-Oriented Programming Demo (Difficulty ⭐)

CPP
#include <iostream>

class Dog {
public:
 void speak() { std::cout << "Woof!" << std::endl; }
};

class Cat {
public:
 void speak() { std::cout << "Meow!" << std::endl; }
};

int main() {
 Dog* d = new Dog();
 Cat* c = new Cat();
 
 d->speak();
 c->speak();
 // ❌ Have to handle different types separately
 
 return 0;
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
Woof!
Meow!

With polymorphism (concise):

CPP
#include <iostream>

class Animal {
public:
 virtual void speak() { std::cout << "Animal speaks" << std::endl; }
};

class Dog : public Animal {
public:
 void speak() override { std::cout << "Woof!" << std::endl; }
};

class Cat : public Animal {
public:
 void speak() override { std::cout << "Meow!" << std::endl; }
};

int main() {
 Animal* animals = {new Dog(), new Cat()};
 
 for (int i = 0; i < 2; i++) {
 animals[i]->speak(); // ✅ Polymorphism: automatically calls the correct version
 }
 
 return 0;
}


2. Virtual Functions (virtual)

(1) 2.1 Basic Usage

Declare a function as virtual in the base class, and override it in the derived class.

CPP
#include <iostream>
#include <string>

class Animal {
protected:
 std::string name;
 
public:
 Animal(const std::string& n) : name(n) {}
 
 // Declare as virtual function
 virtual void speak() {
 std::cout << name << " makes a sound." << std::endl;
 }
 
 // Base class should also have a virtual destructor!
 virtual ~Animal() {}
};

class Dog : public Animal {
public:
 Dog(const std::string& n) : Animal(n) {}
 
 void speak() override {
 std::cout << name << " says: Woof!" << std::endl;
 }
};

int main() {
 Animal* a = new Dog("Buddy");
 a->speak(); // ✅ Output: Buddy says: Woof!
 
 delete a; // ✅ Will call Dog's destructor first, then Animal's
 return 0;
}

⚠️ Key Point: When a base class has virtual functions, the destructor must also be declared virtual! Otherwise, delete on a base class pointer won't call the derived class's destructor.


(2) 2.2 The override Keyword (C++11)

C++11 introduced the override keyword, which lets the compiler check whether you're actually overriding a virtual function.

CPP
class Dog : public Animal {
public:
 // ✅ With override, the compiler will check
 void speak() override { ... }
 
 // ❌ If the signature is wrong (e.g., void speak(int x)), the compiler will error
 // void speak(int x) override { ... } // Compile error: no function to override
};

💡 Recommendation: Always use override — let the compiler catch bugs for you.



3. Pure Virtual Functions and Abstract Classes

(1) 3.1 What Is a Pure Virtual Function?

A pure virtual function is a virtual function that is only declared but not implemented in the base class, marked with = 0.

CPP
class Animal {
public:
 // Pure virtual function
 virtual void speak() = 0;
 
 virtual ~Animal() {}
};

A class with pure virtual functions is called an "abstract class" — it cannot be instantiated and can only serve as a base class.

CPP
Animal a; // ❌ Compile error: cannot instantiate abstract class
Animal* p = new Dog("Buddy"); // ✅ Can use a base class pointer to point to a derived class

(2) 3.2 Why Do We Need Pure Virtual Functions?

To force derived classes to override the function.

CPP
class Animal {
public:
 virtual void speak() = 0; // Force derived classes to implement
};

class Dog : public Animal {
public:
 void speak() override {
 std::cout << "Woof!" << std::endl;
 }
 // ✅ If you forget to override speak(), the compiler will error
};


4. How Virtual Functions Work (Optional)

(1) 4.1 Virtual Function Table (vtable)

C++ implements polymorphism using a virtual function table (vtable):

  1. For classes with virtual functions, the compiler generates a vtable (a table storing virtual function addresses)
  2. Each object contains a hidden vptr (a pointer to the vtable)
  3. When a virtual function is called, the correct function address is found through vptr

💡 Tip: This is why objects of classes with virtual functions occupy an extra pointer's worth of space (8 bytes on 64-bit systems).



5. Practice: Geometric Shape Area Calculation

▶ Example 1: Using Polymorphism to Calculate Areas of Different Shapes (Difficulty ⭐⭐)

CPP 📖 Display only
#include <iostream>
#include <vector>
#include <cmath>

// Abstract base class
class Shape {
public:
 virtual double area() = 0;
 virtual void printInfo() = 0;
 virtual ~Shape() {}
};

// Circle
class Circle : public Shape {
private:
 double radius;
 
public:
 Circle(double r) : radius(r) {}
 
 double area() override {
 return 3.14159 * radius * radius;
 }
 
 void printInfo() override {
 std::cout << "Circle, radius = " << radius 
 << ", area = " << area() << std::endl;
 }
};

// Rectangle
class Rectangle : public Shape {
private:
 double width, height;
 
public:
 Rectangle(double w, double h) : width(w), height(h) {}
 
 double area() override {
 return width * height;
 }
 
 void printInfo() override {
 std::cout << "Rectangle, " << width << " x " << height 
 << ", area = " << area() << std::endl;
 }
};

int main() {
 std::vector<Shape*> shapes;
 shapes.push_back(new Circle(5.0));
 shapes.push_back(new Rectangle(4.0, 6.0));
 
 for (Shape* s : shapes) {
 s->printInfo();
 }
 
 // Free memory
 for (Shape* s : shapes) {
 delete s;
 }
 
 return 0;
}
44 logic lines (exceeds 40-line limit, display only)

Output:

TEXT 📖 Display only
Circle, radius = 
Rectangle,  x 

❓ FAQ

Q: Why must the destructor be virtual if there are virtual functions? A: If you delete a base class pointer pointing to a derived class object, and the base class destructor is not virtual, only the base class destructor will be called, causing resource leaks in the derived class.

CPP
Animal* a = new Dog("Buddy");
delete a; // ❌ If ~Animal() is not virtual, Dog's destructor won't be called

Golden Rule: If a base class has virtual functions, ~BaseClass() must be virtual.

Q: What's the difference between override and final? A: > - override: tells the compiler this function overrides a base class virtual function (lets the compiler check for you) > - final: tells the compiler this function cannot be overridden (used in derived classes)

CPP
class Dog : public Animal {
public:
void speak() override final { ... } // final: derived classes can no longer override speak()
};

Q: When should I use polymorphism? A: When you need to handle different types of objects through a unified interface.

Typical scenarios:

  • Games: all characters inherit from Character, managed uniformly with Character*
  • GUI: all controls inherit from Widget, drawn uniformly with Widget*
  • File systems: all files inherit from File, read/written uniformly with File*

▶ Example 3: Virtual Function Polymorphism (Difficulty ⭐)

CPP
#include <iostream>
#include <string>

class Shape {
public:
    virtual double area() { return 0; }
    virtual ~Shape() {}
};

class Circle : public Shape {
    double radius;
public:
    Circle(double r) : radius(r) {}
    double area() override { return 3.14 * radius * radius; }
};

int main() {
    Shape* s = new Circle(2.0);
    std::cout << "Surface Product: " << s->area() << std::endl;
    delete s;

    return 0;
}
▶ Try it Yourself

Output:

TEXT 📖 Display only
Surface Product: 
💡 Tip: Use virtual to declare virtual functions and override to explicitly indicate overriding. A base class pointer calling a derived class method is polymorphism.



📖 Summary

📝 Exercises

  1. Basic (Difficulty ⭐): Define a Vehicle base class with a pure virtual function void move(). Have Car and Bicycle inherit from it, outputting "The car is driving on the road" and "The bicycle is riding on the bike lane" respectively.

  2. Intermediate (Difficulty ⭐⭐): Extend the above program by adding a void stop() virtual function, with Car outputting "The car pulls over" and Bicycle outputting "The bicycle brakes".

  3. Challenge (Difficulty ⭐⭐⭐): Design an employee salary system:

  4. Employee abstract base class (name, employee ID, virtual double calcSalary() = 0)

  5. FullTimeEmployee (monthly salary)

  6. PartTimeEmployee (hourly wage × hours worked)

  7. Use vector<Employee*> to store all employees and uniformly call calcSalary()

  8. Virtual functions: declared in base class, overridden in derived class, runtime polymorphism


6. 🚀 Next Steps

Now that you've learned polymorphism, let's move on to Templates (Lesson 33) — making functions and classes support arbitrary types for true "generic programming"!

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏