Intermediate 12 min

Pure Virtual Functions

Create abstract interfaces using pure virtual functions that derived classes must implement

Learn how to create interfaces in C++ using pure virtual functions that enforce implementation contracts in derived classes.

A Simple Example

#include <iostream>
#include <string>
#include <vector>

class Animal {
protected:
    std::string name;
    int age;

public:
    Animal(std::string name, int age) : name{name}, age{age} {
    }

    virtual void makeSound() const = 0;

    virtual void move() const = 0;

    void displayInfo() const {
        std::cout << name << " (" << age << " years old)" << "\n";
    }

    virtual ~Animal() {
        std::cout << "Animal destructor: " << name << "\n";
    }
};

class Dog : public Animal {
public:
    Dog(std::string name, int age) : Animal{name, age} {
    }

    void makeSound() const override {
        std::cout << name << " says: Woof! Woof!" << "\n";
    }

    void move() const override {
        std::cout << name << " is running on four legs" << "\n";
    }

    ~Dog() override {
        std::cout << "Dog destructor" << "\n";
    }
};

class Bird : public Animal {
public:
    Bird(std::string name, int age) : Animal{name, age} {
    }

    void makeSound() const override {
        std::cout << name << " says: Tweet! Tweet!" << "\n";
    }

    void move() const override {
        std::cout << name << " is flying through the air" << "\n";
    }

    ~Bird() override {
        std::cout << "Bird destructor" << "\n";
    }
};

void interactWithAnimal(const Animal& animal) {
    animal.displayInfo();
    animal.makeSound();
    animal.move();
    std::cout << "\n";
}

int main() {
    // Animal a{"Generic", 1};  // ERROR: Cannot instantiate abstract class!

    Dog dog{"Buddy", 3};
    Bird bird{"Tweety", 1};

    interactWithAnimal(dog);
    interactWithAnimal(bird);

    std::vector<Animal*> zoo;
    zoo.push_back(new Dog{"Max", 5});
    zoo.push_back(new Bird{"Polly", 2});
    zoo.push_back(new Dog{"Rex", 4});

    std::cout << "Zoo animals:" << "\n";
    for (const Animal* animal : zoo) {
        animal->makeSound();
        animal->move();
        std::cout << "\n";
    }

    for (Animal* animal : zoo) {
        delete animal;
    }

    return 0;
}

Breaking It Down

Pure Virtual Function Syntax: = 0

  • Syntax: virtual void functionName() = 0; - note the = 0 at the end
  • What it means: "I declare this function exists but provide no implementation"
  • Effect: Makes the class abstract - cannot create objects of it
  • Remember: The = 0 is not assignment, it's special syntax for pure virtual

Abstract Classes Cannot Be Instantiated

  • Having even one pure virtual function makes a class abstract
  • Cannot create objects: Animal a; is a compiler error
  • Can create pointers/references: Animal* ptr or Animal& ref are valid
  • Remember: Abstract classes are interfaces/blueprints, not concrete objects

Derived Classes Must Implement

  • Derived classes must implement ALL pure virtual functions to become concrete
  • If a derived class doesn't implement them, it's also abstract
  • Use override keyword to catch typos and ensure you're overriding correctly
  • Remember: Pure virtual functions enforce a contract at compile time

Polymorphism Through Interfaces

  • Pure virtual functions enable polymorphism - call through base reference/pointer
  • Example: interactWithAnimal(dog) works with any Animal derivative
  • The correct derived implementation is called at runtime
  • Remember: Code to interfaces, not concrete implementations

Why This Matters

  • Pure virtual functions are contracts - they say "I define WHAT needs to be done, but not HOW." This is how you design interfaces in C++.
  • Every GUI framework, plugin system, and extensible architecture relies on this. You're defining behavior that must exist without dictating implementation details.
  • It's the difference between saying "all vehicles must move" versus "all vehicles must use a V8 engine." Pure virtual functions let you code to interfaces, not implementations - one of the most powerful design principles in software engineering.

Critical Insight

Pure virtual functions force derived classes to make a decision - they can't just inherit and ignore the interface. This catches bugs at compile time! If you add a new pure virtual function to a base class, every derived class that doesn't implement it will fail to compile. This is a feature, not a bug - it ensures all classes conform to the interface:

class Plugin {
public:
    virtual void initialize() = 0;
    virtual void execute() = 0;
    virtual void cleanup() = 0;
};

class MyPlugin : public Plugin {
    // If you forget to implement any of these,
    // the code won't compile!
    // This prevents runtime errors from missing functionality
};

Best Practices

Always use override keyword: Helps catch typos and ensures you're actually overriding a virtual function.

Make destructors virtual in abstract classes: Use virtual ~ClassName() to ensure proper cleanup.

Keep interfaces focused: Pure virtual functions should represent cohesive, related functionality.

Document contracts: Explain what implementing classes must do, not just the function signature.

Common Mistakes

Trying to instantiate abstract class: You can't create objects of classes with pure virtual functions.

Forgetting to implement all pure virtual functions: If a derived class doesn't implement all pure virtual functions, it's also abstract.

Not making destructor virtual: In abstract classes, destructors should always be virtual.

Const correctness: If base declares void foo() const = 0;, override must also be const.

Debug Challenge

This code tries to instantiate an abstract class. Click the highlighted line to fix it:

1 class Shape {
2 public:
3 virtual double area() const = 0;
4 };
5
6 int main() {
7 Shape s;
8 return 0;
9 }

Quick Quiz

  1. What makes a function pure virtual?
The `= 0` syntax
The `pure` keyword
The `abstract` keyword
  1. Can you create an object of a class with pure virtual functions?
No, the class is abstract
Yes, always
Only in main()
  1. What must derived classes do with pure virtual functions?
Implement all of them to become concrete classes
Ignore them
Delete them

Practice Playground

Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.

Lesson Progress

  • Fix This Code
  • Quick Quiz
  • Practice Playground - run once