polymorphism
Polymorphism (dynamic dispatch) is the ability to call derived class methods through a base class pointer or reference. In C++, you declare virtual functions using the virtual keyword on the base class, and the appropriate derived class method is selected at runtime through a mechanism called dynamic dispatch.
Syntax
// ========================================
// Polymorphism basics
// ========================================
// Define a virtual function on the base class with the virtual keyword
class Base {
public:
virtual void method() {
// base class implementation
}
// Virtual destructor — ensures the derived class destructor is called on delete
virtual ~Base() {}
};
// Override in the derived class using the override keyword (C++11 and later)
class Derived : public Base {
public:
void method() override {
// derived class implementation
}
};
// Calling a virtual function through a base class pointer invokes the derived implementation
Base* obj = new Derived();
obj->method(); // Derived::method() is called (dynamic dispatch)
delete obj;
// dynamic_cast for safe downcasting
Derived* d = dynamic_cast<Derived*>(obj);
if (d != nullptr) {
// downcast succeeded
}
Syntax Reference
| Syntax / Keyword | Description |
|---|---|
| virtual void f() | Declares a virtual function. When called through a base class pointer, the derived class implementation is selected. |
| void f() override | Overrides a virtual function in a derived class. The override keyword (C++11+) causes a compile error if no matching virtual function exists in the base class. |
| virtual ~Base() | Declares a virtual destructor. Ensures the derived class destructor is called when deleting through a base class pointer. |
| Dynamic dispatch | At runtime the virtual function table (vtable) is consulted and the method matching the actual object type is called. |
| dynamic_cast<T*>(ptr) | Safe downcast using runtime type information (RTTI). Returns nullptr on failure (pointer version) or throws std::bad_cast (reference version). |
Sample Code
yakuza_polymorphism.cpp
// ========================================
// yakuza_polymorphism.cpp
// Demonstrates polymorphism using a class
// hierarchy based on Yakuza (Like a Dragon)
// characters
// ========================================
#include <iostream>
#include <string>
#include <vector>
// ========================================
// Base class: Fighter (common interface)
// ========================================
class Fighter {
protected:
std::string name;
int hp;
public:
Fighter(const std::string& name, int hp) : name(name), hp(hp) {}
// virtual keyword makes this a virtual function (overridable in derived classes)
virtual void attack() {
std::cout << name << " attacks." << std::endl;
}
// Virtual functions can be left as-is to use the default implementation
virtual void introduce() {
std::cout << "Name: " << name << " HP: " << hp << std::endl;
}
// A virtual destructor is required.
// Without it, the derived class destructor is not called when deleting
// through a base class pointer.
virtual ~Fighter() {
std::cout << name << " destructor called." << std::endl;
}
};
// ========================================
// Derived class: KiryuKazuma
// ========================================
class KiryuKazuma : public Fighter {
public:
KiryuKazuma() : Fighter("Kiryu Kazuma", 9999) {}
// override keyword verifies that a matching virtual function exists in the base class
void attack() override {
std::cout << name << " activates \"Dragon of Dojima\"! Delivers a powerful punch." << std::endl;
}
};
// ========================================
// Derived class: MajimaGoro
// ========================================
class MajimaGoro : public Fighter {
public:
MajimaGoro() : Fighter("Majima Goro", 8500) {}
void attack() override {
std::cout << name << " activates \"Mad Dog of Shimano\"! Unleashes a flurry of knife strikes." << std::endl;
}
};
// ========================================
// Derived class: AkiyamaShun
// ========================================
class AkiyamaShun : public Fighter {
public:
AkiyamaShun() : Fighter("Akiyama Shun", 7800) {}
void attack() override {
std::cout << name << " lands \"Brawler Kick\"! Sends the opponent flying with a flying kick." << std::endl;
}
};
// ========================================
// Receives a base class pointer — dynamic dispatch selects the correct attack()
// ========================================
void battleScene(Fighter* fighter) {
fighter->introduce();
fighter->attack();
std::cout << std::endl;
}
int main() {
// Store derived class instances in a base class pointer array
std::vector<Fighter*> party;
party.push_back(new KiryuKazuma());
party.push_back(new MajimaGoro());
party.push_back(new AkiyamaShun());
std::cout << "=== Battle Scene ===" << std::endl << std::endl;
// Calling through Fighter* invokes each derived class's attack() — this is polymorphism
for (int i = 0; i < (int)party.size(); i++) {
battleScene(party[i]);
}
// dynamic_cast checks whether a pointer points to a specific derived type
std::cout << "=== dynamic_cast demo ===" << std::endl;
Fighter* unknown = party[0];
KiryuKazuma* kiryu = dynamic_cast<KiryuKazuma*>(unknown);
if (kiryu != nullptr) {
std::cout << "This character is Kiryu Kazuma." << std::endl;
}
std::cout << std::endl << "=== Cleanup ===" << std::endl;
// The virtual destructor ensures derived class destructors are called correctly
for (int i = 0; i < (int)party.size(); i++) {
delete party[i];
}
return 0;
}
g++ -std=c++11 yakuza_polymorphism.cpp -o yakuza_polymorphism ./yakuza_polymorphism === Battle Scene === Name: Kiryu Kazuma HP: 9999 Kiryu Kazuma activates "Dragon of Dojima"! Delivers a powerful punch. Name: Majima Goro HP: 8500 Majima Goro activates "Mad Dog of Shimano"! Unleashes a flurry of knife strikes. Name: Akiyama Shun HP: 7800 Akiyama Shun lands "Brawler Kick"! Sends the opponent flying with a flying kick. === dynamic_cast demo === This character is Kiryu Kazuma. === Cleanup === Kiryu Kazuma destructor called. Majima Goro destructor called. Akiyama Shun destructor called.
Common mistake 1: Missing virtual destructor in the base class
When deleting through a base class pointer, if the destructor is not virtual the derived class destructor is not called, potentially causing resource leaks.
// NG:
class Base {
public:
~Base() {} // not virtual — derived class destructor will not be called
};
class Derived : public Base {
public:
~Derived() { /* resource cleanup */ }
};
Base* obj = new Derived();
delete obj; // Derived::~Derived() is not called (resource leak risk)
OK: Add virtual to the base class destructor.
// OK:
class Base {
public:
virtual ~Base() {} // virtual ensures the derived destructor is always called
};
Common mistake 2: Typo in function signature without the override keyword
Without override, a mismatched function signature silently defines a new function instead of overriding the virtual one. The expected dynamic dispatch does not occur and the bug may be hard to find.
// NG:
class Fighter {
public:
virtual void attack(int power) {}
};
class KiryuKazuma : public Fighter {
public:
void attack() {} // different signature — defines a new function, not an override
};
OK: Use override so the compiler catches signature mismatches.
// OK:
class KiryuKazuma : public Fighter {
public:
void attack(int power) override {} // correct signature with override
};
Overview
Polymorphism lets you call derived-class-specific behavior through a base class pointer or reference and is a cornerstone of object-oriented design. In C++, functions marked with virtual become virtual functions; at runtime the vtable is consulted and the method corresponding to the actual object type is selected. When overriding in a derived class, the override keyword (C++11+) is often used to catch signature mismatches at compile time. When deleting objects through a base class pointer, the base class must have a virtual destructor (virtual ~Base()); without it, the derived class destructor is not called and resources may leak. dynamic_cast performs a safe downcast using runtime type information (RTTI), but heavy use may indicate a design worth revisiting from an open/closed principle perspective.
If you find any errors or copyright issues, please contact us.