Lesson 16 • Advanced

    Advanced OOP in C++

    Master virtual tables, abstract classes, multiple inheritance, and compile-time polymorphism with CRTP.

    What You'll Learn

    • How vtables enable runtime polymorphism
    • Abstract classes and pure virtual functions
    • Multiple inheritance and the diamond problem
    • CRTP for static polymorphism

    Virtual Tables (vtables) — How Polymorphism Works

    When you declare a virtual function, the compiler creates a virtual table (vtable) for the class — an array of function pointers. Each object gets a hidden vptr that points to its class's vtable. When you call a virtual function through a base pointer, the CPU looks up the correct function in the vtable at runtime.

    This is why virtual function calls are slightly slower than non-virtual ones — there's an extra pointer dereference. But the flexibility of runtime polymorphism is almost always worth the cost.

    vtable & Virtual Functions

    See polymorphic dispatch and virtual destructors in action

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    class Animal {
    public:
        virtual void speak() const {
            cout << "Animal speaks" << endl;
        }
        virtual void move() const {
            cout << "Animal moves" << endl;
        }
        virtual ~Animal() {
            cout << "Animal destroyed" << endl;
        }
    };
    
    class Dog : public Animal {
    public:
        void speak() const override {
            cout << "Dog barks: Woof!" << endl;
        }
        void move() const override {
            cout << "Dog runs on four legs" << end
    ...

    Abstract Classes

    Build a polymorphic shape hierarchy with pure virtual functions

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <memory>
    using namespace std;
    
    // Pure abstract class (interface)
    class Shape {
    public:
        virtual double area() const = 0;       // Pure virtual
        virtual double perimeter() const = 0;  // Pure virtual
        virtual string name() const = 0;
        virtual ~Shape() = default;
        
        // Non-virtual: shared behaviour
        void describe() const {
            cout << name() << ": area=" << area()
                 << ", perimeter=" << perimeter() << endl;
        }
    }
    ...

    Multiple Inheritance & the Diamond Problem

    C++ allows a class to inherit from multiple bases. The diamond problem occurs when two bases share a common ancestor — should the derived class have one or two copies of the ancestor? Use virtual inheritance to ensure only one copy exists.

    Diamond Inheritance

    Solve the diamond problem with virtual inheritance

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    // The Diamond Problem and virtual inheritance
    class Device {
    protected:
        string serialNumber;
    public:
        Device(string sn) : serialNumber(sn) {
            cout << "Device(" << sn << ") constructed" << endl;
        }
        virtual ~Device() = default;
        string getSerial() const { return serialNumber; }
    };
    
    // virtual inheritance prevents duplicate Device base
    class Printer : virtual public Device {
    public:
        Printer(string sn) : Device(sn) {
            cout
    ...

    CRTP Pattern

    Use the Curiously Recurring Template Pattern for zero-overhead polymorphism

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    // CRTP: Curiously Recurring Template Pattern
    // Static polymorphism — no virtual function overhead
    template <typename Derived>
    class Countable {
        static int count;
    public:
        Countable() { ++count; }
        Countable(const Countable&) { ++count; }
        ~Countable() { --count; }
        
        static int getCount() { return count; }
    };
    
    template <typename Derived>
    int Countable<Derived>::count = 0;
    
    class Enemy : public Countable<Enemy> {
        string type;
    publ
    ...

    Common Mistakes

    ⚠️ Missing virtual destructor: Deleting a derived object through a base pointer without a virtual destructor causes undefined behaviour.

    ⚠️ Forgetting override: Without override, a typo in the function signature creates a new function instead of overriding.

    ⚠️ Slicing: Assigning a derived object to a base variable (not pointer/reference) slices off the derived part.

    Pro Tips

    💡 Always use override: The override keyword catches signature mismatches at compile time.

    💡 Prefer composition: Multiple inheritance adds complexity. Often, composition (has-a) is cleaner than inheritance (is-a).

    💡 Use final: Mark classes or methods final to prevent further overriding — the compiler can devirtualize calls for better performance.

    📋 Quick Reference

    ConceptSyntax
    Pure virtualvirtual void f() = 0;
    Overridevoid f() override;
    Final classclass X final : Base {};
    Virtual inheritanceclass D : virtual public Base
    Virtual destructorvirtual ~Base() = default;

    Lesson Complete!

    You've mastered advanced OOP concepts including vtables, abstract classes, the diamond problem, and CRTP. Next: Templates Deep Dive — variadic templates, SFINAE, and compile-time metaprogramming.

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service