Understanding Polymorphism in C++
Polymorphism enables objects of different classes to be treated as objects of a common base class, allowing the same function call to produce different behaviors depending on the object type. For example, consider a ticket purchasing system where a regular person pays full price, a student pays half price, and a soldier gets priority service.
Conditions for Polymorphism
Polymorphism requires inheritance and two key conditions:
- Virtual functions must be called through base class pointers or references.
- Derived classes must override the base class virtual functions.
class Customer {
public:
virtual void purchaseTicket() { std::cout << "Full price ticket" << std::endl; }
};
class StudentCustomer : public Customer {
public:
virtual void purchaseTicket() override { std::cout << "Half price ticket" << std::endl; }
};
void processPurchase(Customer& cust) {
cust.purchaseTicket();
}
int main() {
Customer regular;
StudentCustomer student;
processPurchase(regular);
processPurchase(student);
return 0;
}
Virtual Functions and Overriding
Virtual functions are declared with the virtual keyword. Overriding occurs when a derived class provides its own implementation of a base class virtual function with identical signature.
class Base {
public:
virtual void performAction() { std::cout << "Base action" << std::endl; }
};
class Derived : public Base {
public:
virtual void performAction() override { std::cout << "Derived action" << std::endl; }
};
Exceptions in Overriding
- Covariant Return Types: When overriding, the return type can differ if it's a pointer or reference to the class type.
class Shape {};
class Circle : public Shape {};
class ShapeFactory {
public:
virtual Shape* create() { return new Shape; }
};
class CircleFactory : public ShapeFactory {
public:
virtual Circle* create() override { return new Circle; }
};
- Destructor Overriding: Base class destructors should be virtual to ensure proper cleanup of derived objects.
class Resource {
public:
virtual ~Resource() { std::cout << "Resource cleanup" << std::endl; }
};
class FileResource : public Resource {
public:
~FileResource() override { std::cout << "FileResource cleanup" << std::endl; }
};
C++11 Override and Final Keywords
C++11 introduced override and final to improve code safety:
overrideensures the function actually overrides a base class virtual function.finalprevents further overriding of virtual functions or inheritance of classes.
class Vehicle {
public:
virtual void startEngine() {}
};
class Car : public Vehicle {
public:
virtual void startEngine() override { std::cout << "Car engine started" << std::endl; }
};
class SportsCar final : public Car {
public:
virtual void startEngine() override final { std::cout << "Sports car engine started" << std::endl; }
};
Abstract Classes and Pure Virtual Functions
Abstract classes contain atleast one pure virtual function (declared with = 0). They cannot be instantiated directly and serve as interfaces that derived classes must implement.
class PaymentProcessor {
public:
virtual void processPayment(double amount) = 0;
};
class CreditCardProcessor : public PaymentProcessor {
public:
virtual void processPayment(double amount) override {
std::cout << "Processing credit card payment: $" << amount << std::endl;
}
};
class PayPalProcessor : public PaymentProcessor {
public:
virtual void processPayment(double amount) override {
std::cout << "Processing PayPal payment: $" << amount << std::endl;
}
};
Interface vs Implementation Inheritance
- Implementation Inheritance: Derived classes inherit and can use base class function implementations.
- Interface Inheritance: Derived classes inherit only the function signatures and must provide their own implementations, enabling polymorphism.
Use virtual functions specifically when polymorphism is required; otherwise, regular functions are sufficient.