C++ Runtime Polymorphism
In this page:
Runtime Polymorphism Explained
Runtime polymorphism lets a C++ program decide, while it's actually running, which specific function implementation to execute — a behavior known as late binding or dynamic binding, in contrast to decisions the compiler can make ahead of time.
Example: Runtime Polymorphism Explained
#include <iostream>
class Animal {
public:
virtual void speak() { std::cout << "Animal" << std::endl; }
};
class Dog : public Animal {
public:
void speak() override { std::cout << "Dog" << std::endl; }
};
int main() {
Animal *a = new Dog();
a->speak();
delete a;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Using Base Class Pointers
Using base class pointers to refer to objects of various derived types lets you handle many different concrete components through a single, unified interface, without the calling code needing to know or care exactly which derived type it's dealing with.
Example: Using Base Class Pointers
#include <iostream>
class Shape {
public:
virtual void draw() { std::cout << "Shape" << std::endl; }
};
class Circle : public Shape {
public:
void draw() override { std::cout << "Circle" << std::endl; }
};
class Square : public Shape {
public:
void draw() override { std::cout << "Square" << std::endl; }
};
int main() {
Shape *shapes[2] = {new Circle(), new Square()};
for (int i = 0; i < 2; i++) shapes[i]->draw();
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Using Base References
Passing derived objects to functions that accept a parent-class reference parameter is an especially clean way to write polymorphic code, since it avoids the null-checking and explicit pointer syntax that base-pointer-based polymorphism requires.
Example: Using Base References
#include <iostream>
class Animal {
public:
virtual void speak() { std::cout << "Animal" << std::endl; }
};
class Dog : public Animal {
public:
void speak() override { std::cout << "Dog" << std::endl; }
};
void makeSpeak(Animal &a) {
a.speak();
}
int main() {
Dog d;
makeSpeak(d);
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Runtime vs Compile-time Performance
Dynamic binding does introduce a small performance cost from the VTABLE lookup needed to resolve which function to call at runtime, but in exchange it buys substantial structural flexibility — new derived types can be added later without changing any existing polymorphic code.
Example: Runtime vs Compile-time Performance
#include <iostream>
class Animal {
public:
virtual void speak() { std::cout << "Animal" << std::endl; }
};
class Dog : public Animal {
public:
void speak() override { std::cout << "Dog" << std::endl; }
};
int main() {
Animal *a = new Dog();
a->speak();
delete a;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Real-world Polymorphic Design
Real-world software relies heavily on runtime polymorphism to stay extensible over time — payment processors that support new payment methods, or messaging systems that support new transport protocols, are both commonly built around a polymorphic base type.
Example: Real-world Polymorphic Design
#include <iostream>
class PaymentMethod {
public:
virtual void pay() { std::cout << "Generic payment" << std::endl; }
};
class CreditCard : public PaymentMethod {
public:
void pay() override { std::cout << "Paid with credit card" << std::endl; }
};
int main() {
PaymentMethod *method = new CreditCard();
method->pay();
delete method;
return 0;
}
Login to try C/C++/Java/PHP code in the editor
Chapter Quiz — Complete all 11 topics to unlock
0/11 topics done
Complete these topics first: