Understanding C++ Polymorphism: Implementation and Principles
Virtual Functions
A function becomes virtual when prefixed with virtual.
class BaseClass {
public:
virtual void operation() {}; // This is a virtual function
};
Inheritance of Virtual Functions
Virtual function inheritance embodies interface inheritance, where the function signature (including return type, name, parameter list, and default parameters) is inherited, allowing overriding of the implementation.
Virtual Classes / Virtual Base Classes
A class containing virtual functions is a virtual class; if it serves as a base class, it is termed a virtual base class.
Overriding / Overwriting
Conditions: The function name, parameters (types only, not default parameters), and return type must match. Overriding replaces the function body of an inherited virtual function, reflecting interface inheritance. Only virtual functions can be overridden; non-virtual functions result in hiding or redefinition.
Conditions for Polymorphism
Two conditions must be met for polymorphism:
- Overriding of a virtual function.
- Calling the virtual function via a pointer or reference of the base class type.
class Person {
public:
virtual void purchaseTicket() {
std::cout << "Full fare" << std::endl;
}
};
class Student : public Person {
public:
virtual void purchaseTicket() override {
std::cout << "Half fare" << std::endl;
}
};
void processPurchase(Person& p) {
p.purchaseTicket();
}
int main() {
Person adult;
Student pupil;
processPurchase(adult); // Outputs: Full fare
processPurchase(pupil); // Outputs: Half fare
return 0;
}

Additional Polymorphic Behaviors
- Derived classes can omit
virtualfor overridden functions; if the base function is virtual, subsequent inherited versions remain virtual. - Covariance allows return types to differ in pointer or reference form, maintaining type safety.
Inheritance and Destructors
In inheritance hierarchies, destructors should be virtual to ensure proper cleanup when using base class pointers.
class Base {
public:
virtual ~Base() { std::cout << "Base destructor" << std::endl; }
};
class Derived : public Base {
public:
virtual ~Derived() override { std::cout << "Derived destructor" << std::endl; }
};
int main() {
Base* ptr = new Derived();
delete ptr; // Properly calls both destructors
return 0;
}

C++11 Keywords: override and final
final: Prevents further inheritance or overriding.class FinalClass final {}; // Cannot be inherited class Base { public: virtual void method() final {}; // Cannot be overridden };override: Ensures a function overrides a base class virtual function, generating a compile-time error if not.class Base { public: virtual void action() {} }; class Derived : public Base { public: void action() override {} // Compiles only if it overrides };
Pure Virtual Functions and Abstract Classes
A pure virtual function is declared with = 0 and defines an abstract class, which cannot be instantiated.
class AbstractClass {
public:
virtual void perform() = 0; // Pure virtual function
};
Mechanism of Polymorphism
Polymorphism is implemented through virtual function tibles (vtables). Each object with virtual functions contains a vtable pointer (_vptr) pointing to an array of function addresses.
- Vtables are generated at compile-time.
- Derived classes inherit and may modify vtables by overriding functions.
Example: Vtable Illustration
class BaseType {
public:
virtual void funcA() {}
virtual void funcB() {}
};
class DerivedType : public BaseType {
public:
void funcA() override {}
virtual void funcC() {}
};

Multiple Inheritance and Vtables
In multiple inheritance, derived classes have multiple vtables, one per base class. Additional virtual functions are appended to the first base class's vtable.
class BaseOne {
public:
virtual void op1() {}
};
class BaseTwo {
public:
virtual void op2() {}
};
class MultiDerived : public BaseOne, public BaseTwo {
public:
void op1() override {}
virtual void op3() {}
};
Static vs. Dynamic Binding
- Static binding: Resolved at compile-time (e.g., function overloading).
- Dynamic binding: Resolved at runtime via vtables (polymorphism).
Performance Considerations
Virtual function calls via pointers or references are generally slower than direct calls due to vtable lookup, but non-polymorphic calls may optimize similarly.
Key Concepts
- Inline and virtual: Can coexist but inline may be ignored in polymorphic contexts.
- Static functions cannot be virtual as they lack
thispointer. - Constructors and copy constructors cannot be virtual.
This article covers the fundamentals and internal workings of C++ polymorphism, essential for advanced object-oriented programming.