Search code examples
c++overridingvirtual-functionsredefinition

What is the purpose of virtual member functions?


What is the difference member between function overriding and virtual functions in C++?

Virtual member functions can be overridden in derived classes. Redefining a function in a derived class is called function overriding.

Why do we actually have virtual functions?


Solution

  • ABSTRACT

    In this paper, we discuss virtual functions in C++. Part zero explains how virtual functions are declared and overridden. Part one attempts (and perhaps fails) to explain how virtual functions are implemented. Part two is a sample program that uses the example classes defined in parts zero and one. Part three is the classic animal example given in every virtual function - polymorphism tutorial.

    PART ZERO

    A method of a class is said to be virtual if and only if its declared to be so.

    class my_base
    {
    public:
                void non_virtual_test() { cout << 4 << endl; } // non-virtual
        virtual void virtual_test()     { cout << 5 << endl; } // virtual
    };
    

    (Of course, I am assuming the programmer did not previously do anything like #define virtual.)

    A class that redeclares and re-implements a non-virtual method of one of its bases is said to overload that method. A class that redeclares and re-implements a virtual method of one of its bases is said to override that method.

    class my_derived : public my_base
    {
    public:
        void non_virtual_test() { cout << 6 << endl; } // overloaded
        void virtual_test()     { cout << 7 << endl; } // overriden
    };
    

    PART ONE

    When the compiler detects a class has virtual methods, it automatically adds a virtual method table (also known as vtable) to the class' memory layout. The result is similar to what would have been generated from compiling this code:

    class my_base
    {
    //<vtable>
    // The vtable is actually a bunch of member function pointers
    protected:
        void (my_base::*virtual_test_ptr)();
    //</vtable>
    
    // The actual implementation of the virtual function
    // is hidden from the rest of the program.
    private:
        void virtual_test_impl() { cout << 5 << endl; }
    
    // Initializing the real_virtual_test pointer in the vtable.
    public:
        my_base() : virtual_test_ptr(&my_base::virtual_test_impl) {}
    
    public:
        void non_virtual_test() { cout << 4 << endl; }
        // The interface of the virtual function is a wrapper
        // around the member function pointer.
        inline void virtual_test() { *virtual_test_ptr(); }
    };
    

    When the compiler detects a class has overridden a virtual method, it replaces its associated entry in the vtable. The result is similar to what would have been generated from compiling this code:

    class my_derived : public my_base
    {
    // The actual implementation of the virtual function
    // is hidden from the rest of the program.
    private:
        void virtual_test_impl() { cout << 7 << endl; }
    
    // Initializing the real_virtual_test pointer in the vtable.
    public:
        my_derived() : virtual_test_ptr(&my_derived::virtual_test_impl) {}
    
    public:
        void non_virtual_test() { cout << 6 << endl; }
    };
    

    PART TWO

    Now that it's clear that virtual functions are implemented using vtables, which are nothing but a bunch of function pointers, it should be clear what this code does:

    #include <iostream>
    
    using namespace std;
    
    class my_base
    {
        public:
                void non_virtual_test() { cout << 4 << endl; }
        virtual void virtual_test()     { cout << 5 << endl; }
    };
    
    class my_derived : public my_base
    {
    public:
        void non_virtual_test() { cout << 6 << endl; }
        void virtual_test()     { cout << 7 << endl; }
    }
    
    int main()
    {
        my_base* base_obj = new my_derived();
    
        // This outputs 4, since my_base::non_virtual_test() gets called,
        // not my_derived::non_virtual_test().
        base_obj->non_virtual_test();
    
        // This outputs 7, since the vtable pointer points to
        // my_derived::virtual_test(), not to my_base::virtual_test().
        base_obj->virtual_test();
    
        // We shall not forget
        // there was an object that was pointed by base_obj
        // who happily lived in the heap
        // until we killed it.
        delete base_obj;
    
        return 0;
    }
    

    PART THREE

    Since no virtual function example is complete without an example with animals...

    #include <iostream>
    
    using namespace std;
    
    class animal
    {
    public:
        virtual void say_something()
        { cout << "I don't know what to say." << endl
               << "Let's assume I can growl." << endl; }
    
        /* A more sophisticated version would use pure virtual functions:
         *
         * virtual void say_something() = 0;
         */
    };
    
    class dog : public animal
    {
    public:
        void say_something() { cout << "Barf, barf..." << endl; }
    };
    
    class cat : public animal
    {
    public:
        void say_something() { cout << "Meow, meow..." << endl; }
    };
    
    int main()
    {
        animal *a1 = new dog();
        animal *a2 = new cat();
        a1->say_something();
        a2->say_something();
    }