Hello! In our previous lessons, we've explored the intricate mechanics of C++ polymorphism, from the vtable and vptr that enable virtual function calls to the complex memory layouts required for multiple and virtual inheritance. You've seen how the compiler uses tables of function pointers to achieve dynamic dispatch at runtime.
Today, we'll leverage that foundation to learn how to define contracts and interfaces in C++. The learning outcome for this lesson is to define abstract base classes using pure virtual functions to create interfaces. We will see how C++ allows us to specify that a derived class must provide an implementation for certain functions, and we'll investigate the vtable mechanics that enforce this rule.
This concept is the bedrock of many design patterns and is essential for building flexible, decoupled systems—a key skill for the roles you're targeting.
1. Enforcing a Contract: Pure Virtual Functions
So far, when a derived class has overridden a virtual function, it has been optional. A Dog class could choose to override Animal::makeSound(), but it wasn't required to. What if we want to create a base class that serves as a blueprint, forcing all derived classes to implement a specific set of functions?
This is where pure virtual functions come in. A pure virtual function is a virtual function that the base class declares but does not define. This has two immediate and important consequences:
- The class containing the pure virtual function becomes an abstract class.
- Any concrete (non-abstract) class that inherits from it must provide an implementation for that function.
Let's see how this works in practice.
Interfaces in C++ (Pure Virtual Functions)
The Cherno provides an excellent introduction to the concept of pure virtual functions. He explains the syntax and the immediate effect it has: making a class abstract and unenforceable on its children.
Watch the first three minutes of the video (00:00 - 02:46). Pay close attention to: The syntax used to declare a pure virtual function: virtual ... = 0;. The compile error that occurs when you try to instantiate an abstract class. The fact that a derived class also becomes abstract if it fails to implement the pure virtual function it inherits.
As you just saw, the = 0 syntax is a declaration that effectively says, "I am not providing an implementation for this function; it is up to my children to do so." This makes the class an incomplete blueprint, or "abstract," which is why the compiler prevents you from creating an object from it.
2. Using Abstract Classes to Create Interfaces
The most powerful application of abstract classes is to define interfaces. An interface is a class that consists of nothing but pure virtual functions (and a virtual destructor, which we know is crucial!). It defines a set of behaviors—a contract—without committing to any specific implementation. Any class that inherits from this interface and implements its functions can be treated polymorphically through a pointer or reference to the interface.
This is a fundamental technique for decoupling code. You can write functions that operate on an interface type, without needing to know anything about the concrete classes that implement it.
Let's continue with The Cherno's video to see a practical example of creating a Printable interface.
Interfaces in C++ (Pure Virtual Functions)
This segment demonstrates how to build and use a simple interface to create generic code.
Watch the rest of the video (02:46 - 06:28). Focus on how the Printable class, with its single pure virtual get_class_name() method, acts as a contract. Notice how the print() function can accept any object that fulfills this contract, whether it's an Entity, a Player, or a completely unrelated class A.
3. The Internals: What's in the V-Table?
Now for the crucial question: if a pure virtual function has no implementation in the base class, what does its vtable entry point to? It can't be null, as that would lead to a crash if it were ever called. And it can't point to a real function body, because one doesn't exist.
The compiler and C++ runtime have a specific mechanism for this. The vtable slot for a pure virtual function in an abstract class points to a special, globally available handler function. In the Itanium C++ ABI (used by GCC/Clang on Linux), this function is named __cxa_pure_virtual.
If you manage to call a pure virtual function—which should not happen in a well-formed program but can occur if you call it from the constructor or destructor of the abstract base class—the vtable dispatch will lead to __cxa_pure_virtual. This function's job is to terminate the program with an error message, alerting you to the mistake. It's a runtime safety net.
Let's see this in the context of reverse engineering.
Reversing C++ Virtual Functions: Part 1
This article from ALSchwalm's blog analyzes a C++ binary. It's an excellent resource that directly shows how pure virtual functions appear at the assembly and vtable level.
Read the sections following the two decompiled images of the main function. Start from the paragraph beginning "Looking back at the tables..." and continue to the end of the article. Key points to focus on: His identification of the ___cxa_pure_virtual entry in the vtable for the Mammal class. The conclusion that a vtable containing this entry corresponds to an abstract type. The critical statement: "the dynamic type of an object can never be an abstract type."
The osdev.org wiki, a fantastic resource for low-level systems development, even shows what a minimal implementation of this handler looks like. This is exactly the kind of function you'd have to provide yourself if you were writing a C++ kernel from scratch without the standard library.
This wiki page for OS developers explicitly discusses the runtime support needed for C++ features, including pure virtual functions.
Read the short section titled 'Pure virtual functions'. Note the function signature extern "C" void __cxa_pure_virtual(). This confirms its role as a C-linkage function provided by the runtime to handle invalid calls.
So, to summarize the internal mechanism:
- You declare a function
virtual ... = 0;. - The compiler marks the class as abstract.
- In the vtable for this abstract class, the entry for the pure virtual function is filled with the address of
__cxa_pure_virtual. - When a concrete class derives and implements the function, its vtable entry is updated to point to the new implementation, as with any regular virtual function.

Test your understanding!
Consider the following class hierarchy:
class IShape {
public:
virtual double getArea() const = 0;
virtual void draw() const = 0;
virtual ~IShape() = default;
};
class Circle : public IShape {
public:
double getArea() const override { /* ... */ }
// Note: draw() is NOT implemented
};
What happens if you try to compile this code and instantiate a Circle object like this: Circle c;? Why does this happen, both from a C++ language rule perspective and from a vtable implementation perspective?
Show answer
The code will fail to compile. The compiler will issue an error stating that you cannot instantiate an object of type Circle because it is an abstract class.
Language Rule Perspective: The rule is that any class that inherits from an abstract class but does not provide an implementation for all of the inherited pure virtual functions is, itself, an abstract class. Since Circle implemented getArea() but not draw(), it remains abstract.
V-Table Implementation Perspective: The compiler, when constructing the vtable for Circle, would fill the slot for getArea() with a pointer to Circle::getArea(). However, for the draw() slot, it has no implementation from Circle to point to. Its only option is to inherit the entry from IShape. Since IShape::draw() is pure virtual, its vtable entry points to __cxa_pure_virtual. Therefore, the vtable for Circle would still contain a pointer to __cxa_pure_virtual, making it an abstract class that cannot be instantiated.
Conclusion
This lesson marks the end of our module on runtime polymorphism internals. You've now seen the full picture, from basic virtual functions to the complex machinery of virtual inheritance, and finally to how these concepts are used to create abstract classes and interfaces.
Key Takeaways:
- A pure virtual function is declared with
= 0and makes its class abstract. - An abstract class cannot be instantiated directly.
- Derived classes must override all inherited pure virtual functions to become concrete (instantiable).
- A class with only pure virtual functions and a virtual destructor serves as a pure interface, defining a contract for other classes to implement.
- Internally, the vtable slot for a pure virtual function points to a runtime handler (like
__cxa_pure_virtual) that terminates the program if called, providing a safety net against incorrect usage.
In our next module, we will shift gears to another concept that is absolutely fundamental to modern C++: Resource Management and the RAII Idiom. We'll explore how C++ provides powerful tools to automate memory and resource cleanup, moving beyond the manual new and delete calls we've used in our examples and into a safer, more robust style of programming.