Pure Virtual Functions
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 59 of 104.
A pure virtual function is a virtual function that has no implementation in the base class. It's declared by assigning = 0 to the function declaration. This tells the compiler that derived classes must provide their own implementation.
class Shape {
public:
virtual double area() = 0; // Pure virtual function
virtual ~Shape() = default;
};Unlike regular virtual functions that provide a default behavior, pure virtual functions define a contract: any concrete derived class must implement this function to be instantiable. The base class simply declares what must be done, not how.
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
};
class Rectangle : public Shape {
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() override {
return width * height;
}
};Both Circle and Rectangle must implement area() because it's pure virtual in Shape. If a derived class fails to implement all pure virtual functions, it also becomes abstract and cannot be instantiated.
Pure virtual functions are essential when the base class cannot provide a meaningful default implementation. Every shape has an area, but there's no sensible way to calculate "generic shape area" without knowing the specific shape type.
Challenge
EasyLet's build a payment processing system that demonstrates how pure virtual functions enforce a contract across different payment methods. You'll create an abstract base class that defines what every payment processor must do, then implement concrete payment types that fulfill that contract.
You'll organize your code across three files:
PaymentProcessor.h: Define an abstractPaymentProcessorclass that serves as the blueprint for all payment methods. This class should have:- A protected
std::string accountIdmember - A constructor that initializes the account ID
- A pure virtual
processPayment(double amount)method — every payment type must implement this differently - A pure virtual
getProcessorName()method that returns astd::string - A virtual destructor
- A protected
PaymentMethods.h: Implement two concrete payment processors that inherit fromPaymentProcessor:CreditCardProcessor:- A private
double feePercentagemember (the transaction fee rate) - A constructor taking account ID and fee percentage
- Implement
processPayment()to calculate the fee (amount * feePercentage / 100), then print:Credit Card [<accountId>]: Charged Credit Card [<accountId>]: Charged $<amount> (Fee: $<fee>)lt;amount> (Fee: Credit Card [<accountId>]: Charged $<amount> (Fee: $<fee>)lt;fee>) - Implement
getProcessorName()to return"CreditCard"
BankTransferProcessor:- A private
std::string bankNamemember - A constructor taking account ID and bank name
- Implement
processPayment()to print:Bank Transfer [<accountId>] via <bankName>: Transferred Bank Transfer [<accountId>] via <bankName>: Transferred $<amount>lt;amount> - Implement
getProcessorName()to return"BankTransfer"
- A private
main.cpp: Read four inputs (each on a separate line):- Credit card account ID
- Credit card fee percentage (double)
- Bank account ID
- Bank name
Create both payment processors and store them in an array of
PaymentProcessor*pointers. Process a payment of100.0through each processor, displaying the processor name before each transaction:Processing with <processorName>: <processPayment output>Print a blank line between processors. Clean up your dynamically allocated objects when finished.
For example, with inputs CC-4521, 2.5, BA-7890, and National Bank:
Processing with CreditCard:
Credit Card [CC-4521]: Charged $100 (Fee: $2.5)
Processing with BankTransfer:
Bank Transfer [BA-7890] via National Bank: Transferred $100Because PaymentProcessor has pure virtual functions, you cannot instantiate it directly — only the concrete implementations that fulfill the contract can be created. This ensures every payment method provides its own specific processing logic.
Cheat sheet
A pure virtual function is declared by assigning = 0 to a virtual function, indicating it has no implementation in the base class:
class Shape {
public:
virtual double area() = 0; // Pure virtual function
virtual ~Shape() = default;
};Pure virtual functions create a contract that derived classes must implement. The base class defines what must be done, not how.
Derived classes must override all pure virtual functions to be instantiable:
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
};If a derived class doesn't implement all pure virtual functions, it also becomes abstract and cannot be instantiated.
Use pure virtual functions when the base class cannot provide a meaningful default implementation.
Try it yourself
#include <iostream>
#include <string>
#include "PaymentMethods.h"
using namespace std;
int main() {
// Read inputs
string ccAccountId;
double feePercentage;
string bankAccountId;
string bankName;
getline(cin, ccAccountId);
cin >> feePercentage;
cin.ignore();
getline(cin, bankAccountId);
getline(cin, bankName);
// TODO: Create an array of PaymentProcessor* pointers with 2 elements
// TODO: Create CreditCardProcessor and BankTransferProcessor objects
// and store them in the array
// TODO: Loop through the array and for each processor:
// 1. Print "Processing with <processorName>:"
// 2. Call processPayment with amount 100.0
// 3. Print a blank line between processors (not after the last one)
// TODO: Clean up dynamically allocated objects
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container