CodeNebula
C++98corebeginner

Virtual Functions

C++98 · The Foundation · Compiler flag: -std=c++98

Explanation

Base class functions overridden by derived classes for dynamic dispatch.

Real-World Scenario

Render different enemy types with a common `attack` method in a game.

Quick Snippet

snippet.cpp
class Enemy { virtual void attack() {} };

Full Example

virtual_functions.cpp
// CodeNebula - C++98 Example: Virtual Functions
// Compile: g++ -std=c++98 -Wall -Wextra -o example virtual_functions.cpp
//
// This example demonstrates:
// - Declaring virtual functions in a base class
// - Overriding in derived classes
// - How virtual dispatch works (vtable concept)
// - Difference between virtual and non-virtual calls
// - Calling the base version from a derived override
// - Cost: virtual functions have a small overhead (vtable pointer)

#include <iostream>
#include <cstring>

// ============================================================
// Base class: Instrument
// ============================================================
class Instrument {
private:
    char name[64];

public:
    Instrument(const char* n) {
        std::strncpy(name, n, 63);
        name[63] = '\0';
    }

    virtual ~Instrument() {}

    const char* getName() const { return name; }

    // --- Virtual functions ---
    virtual void play() const {
        std::cout << "  [Instrument] " << name << " makes a sound" << std::endl;
    }

    virtual void tune() const {
        std::cout << "  [Instrument] Tuning " << name << "..." << std::endl;
    }

    // --- Non-virtual function (static dispatch always) ---
    void description() const {
        std::cout << "  Generic instrument: " << name << std::endl;
    }
};

// ============================================================
// Derived: Piano
// ============================================================
class Piano : public Instrument {
public:
    Piano() : Instrument("Piano") {}

    // Override virtual function
    void play() const {
        std::cout << "  [Piano] Playing elegant piano notes..." << std::endl;
    }

    void tune() const {
        // Can call base version explicitly
        Instrument::tune();
        std::cout << "  [Piano] Adjusting 88 keys..." << std::endl;
    }

    // Hides (not overrides) the non-virtual description()
    void description() const {
        std::cout << "  A grand piano with 88 keys" << std::endl;
    }
};

// ============================================================
// Derived: Guitar
// ============================================================
class Guitar : public Instrument {
private:
    int strings;

public:
    Guitar(int s) : Instrument("Guitar"), strings(s) {}

    void play() const {
        std::cout << "  [Guitar] Strumming " << strings << " strings..." << std::endl;
    }

    void tune() const {
        Instrument::tune();
        std::cout << "  [Guitar] Tuning " << strings << " strings one by one..." << std::endl;
    }

    void description() const {
        std::cout << "  A guitar with " << strings << " strings" << std::endl;
    }
};

// ============================================================
// Derived: Drums
// ============================================================
class Drums : public Instrument {
public:
    Drums() : Instrument("Drums") {}

    void play() const {
        std::cout << "  [Drums] BOOM BAP BOOM BAP!" << std::endl;
    }

    // tune() NOT overridden -- uses Instrument::tune()
};

// ============================================================
// A function demonstrating virtual dispatch
// ============================================================
void perform(const Instrument& inst) {
    std::cout << "  Performing with: " << inst.getName() << std::endl;
    inst.play();         // VIRTUAL dispatch -- calls the derived version
    inst.description();  // NON-VIRTUAL -- always calls Instrument::description()
    std::cout << std::endl;
}

int main() {
    std::cout << "========================================" << std::endl;
    std::cout << "1. VIRTUAL DISPATCH IN ACTION" << std::endl;
    std::cout << "========================================" << std::endl;
    {
        Piano  p;
        Guitar g(6);
        Drums  d;

        // Same function, different behaviour at runtime
        perform(p);
        perform(g);
        perform(d);  // Drums::play() is called; description() is Instrument's
    }

    std::cout << "========================================" << std::endl;
    std::cout << "2. VIRTUAL vs NON-VIRTUAL THROUGH POINTER" << std::endl;
    std::cout << "========================================" << std::endl;
    {
        Piano piano;
        Instrument* ptr = &piano;

        std::cout << "  Through Instrument* pointer:" << std::endl;
        ptr->play();          // Virtual -> Piano::play()
        ptr->description();   // Non-virtual -> Instrument::description()

        std::cout << "\n  Through Piano object directly:" << std::endl;
        piano.play();         // Piano::play()
        piano.description();  // Piano::description() (static dispatch to Piano)
    }

    std::cout << "\n========================================" << std::endl;
    std::cout << "3. CALLING BASE VERSION FROM OVERRIDE" << std::endl;
    std::cout << "========================================" << std::endl;
    {
        Piano p;
        Guitar g(12);

        std::cout << "  Piano::tune() calls Instrument::tune() first:" << std::endl;
        p.tune();

        std::cout << "\n  Guitar::tune() calls Instrument::tune() first:" << std::endl;
        g.tune();
    }

    std::cout << "\n========================================" << std::endl;
    std::cout << "4. DRUMS: INHERITING BASE VIRTUAL FUNCTION" << std::endl;
    std::cout << "========================================" << std::endl;
    {
        Drums d;
        Instrument* ptr = &d;

        std::cout << "  Drums overrides play():" << std::endl;
        ptr->play();    // Drums::play()

        std::cout << "  Drums does NOT override tune():" << std::endl;
        ptr->tune();    // Instrument::tune() (inherited)
    }

    std::cout << "\n========================================" << std::endl;
    std::cout << "5. VTABLE CONCEPT (educational)" << std::endl;
    std::cout << "========================================" << std::endl;
    std::cout << "  When a class has virtual functions, the compiler creates" << std::endl;
    std::cout << "  a 'vtable' -- a table of function pointers for that class." << std::endl;
    std::cout << std::endl;
    std::cout << "  Each object contains a hidden 'vptr' pointing to its" << std::endl;
    std::cout << "  class's vtable. This is how virtual dispatch works:" << std::endl;
    std::cout << std::endl;
    std::cout << "    ptr->play()  -->  vptr->vtable[play_slot]  -->  Piano::play()" << std::endl;
    std::cout << std::endl;
    std::cout << "  Size overhead: one pointer per object (vptr)." << std::endl;
    std::cout << "  Time overhead: one indirection per virtual call." << std::endl;
    std::cout << std::endl;
    {
        std::cout << "  sizeof(Instrument): " << sizeof(Instrument) << " bytes" << std::endl;
        std::cout << "  sizeof(Piano):      " << sizeof(Piano) << " bytes" << std::endl;
        std::cout << "  sizeof(Guitar):     " << sizeof(Guitar) << " bytes" << std::endl;
        std::cout << "  (Sizes include the hidden vptr)" << std::endl;
    }

    return 0;
}

// Expected output (sizes are platform-dependent):
// ========================================
// 1. VIRTUAL DISPATCH IN ACTION
// ========================================
//   Performing with: Piano
//   [Piano] Playing elegant piano notes...
//   Generic instrument: Piano
//
//   Performing with: Guitar
//   [Guitar] Strumming 6 strings...
//   Generic instrument: Guitar
//
//   Performing with: Drums
//   [Drums] BOOM BAP BOOM BAP!
//   Generic instrument: Drums
//
// ========================================
// 2. VIRTUAL vs NON-VIRTUAL THROUGH POINTER
// ========================================
//   Through Instrument* pointer:
//   [Piano] Playing elegant piano notes...
//   Generic instrument: Piano
//
//   Through Piano object directly:
//   [Piano] Playing elegant piano notes...
//   A grand piano with 88 keys
//
// ========================================
// 3. CALLING BASE VERSION FROM OVERRIDE
// ========================================
//   Piano::tune() calls Instrument::tune() first:
//   [Instrument] Tuning Piano...
//   [Piano] Adjusting 88 keys...
//   ...
//
// ========================================
// 4. DRUMS: INHERITING BASE VIRTUAL FUNCTION
// ========================================
//   Drums overrides play():
//   [Drums] BOOM BAP BOOM BAP!
//   Drums does NOT override tune():
//   [Instrument] Tuning Drums...
//
// ========================================
// 5. VTABLE CONCEPT (educational)
// ========================================
//   ...vtable explanation and sizes...
Run command
g++ -std=c++98 -Wall -Wextra -o example virtual_functions.cpp