Menu
Coddy logo textTech

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 icon

Challenge

Easy

Let'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 abstract PaymentProcessor class that serves as the blueprint for all payment methods. This class should have:
    • A protected std::string accountId member
    • 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 a std::string
    • A virtual destructor
  • PaymentMethods.h: Implement two concrete payment processors that inherit from PaymentProcessor:

    CreditCardProcessor:

    • A private double feePercentage member (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 bankName member
    • 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"
  • main.cpp: Read four inputs (each on a separate line):
    1. Credit card account ID
    2. Credit card fee percentage (double)
    3. Bank account ID
    4. Bank name

    Create both payment processors and store them in an array of PaymentProcessor* pointers. Process a payment of 100.0 through 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 $100

Because 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;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming