Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p><strong>ABSTRACT</strong></p> <p>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.</p> <p><strong>PART ZERO</strong></p> <p>A method of a class is said to be <strong>virtual</strong> if and only if its declared to be so.</p> <pre><code>class my_base { public: void non_virtual_test() { cout &lt;&lt; 4 &lt;&lt; endl; } // non-virtual virtual void virtual_test() { cout &lt;&lt; 5 &lt;&lt; endl; } // virtual }; </code></pre> <p>(Of course, I am assuming the programmer did not previously do anything like <code>#define virtual</code>.)</p> <p>A class that redeclares and re-implements a non-virtual method of one of its bases is said to <strong>overload</strong> that method. A class that redeclares and re-implements a virtual method of one of its bases is said to <strong>override</strong> that method.</p> <pre><code>class my_derived : public my_base { public: void non_virtual_test() { cout &lt;&lt; 6 &lt;&lt; endl; } // overloaded void virtual_test() { cout &lt;&lt; 7 &lt;&lt; endl; } // overriden }; </code></pre> <p><strong>PART ONE</strong></p> <p>When the compiler detects a class has virtual methods, it automatically adds a <strong>virtual method table</strong> (also known as <strong>vtable</strong>) to the class' memory layout. The result is similar to what would have been generated from compiling this code:</p> <pre><code>class my_base { //&lt;vtable&gt; // The vtable is actually a bunch of member function pointers protected: void (my_base::*virtual_test_ptr)(); //&lt;/vtable&gt; // The actual implementation of the virtual function // is hidden from the rest of the program. private: void virtual_test_impl() { cout &lt;&lt; 5 &lt;&lt; endl; } // Initializing the real_virtual_test pointer in the vtable. public: my_base() : virtual_test_ptr(&amp;my_base::virtual_test_impl) {} public: void non_virtual_test() { cout &lt;&lt; 4 &lt;&lt; endl; } // The interface of the virtual function is a wrapper // around the member function pointer. inline void virtual_test() { *virtual_test_ptr(); } }; </code></pre> <p>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:</p> <pre><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 &lt;&lt; 7 &lt;&lt; endl; } // Initializing the real_virtual_test pointer in the vtable. public: my_derived() : virtual_test_ptr(&amp;my_derived::virtual_test_impl) {} public: void non_virtual_test() { cout &lt;&lt; 6 &lt;&lt; endl; } }; </code></pre> <p><strong>PART TWO</strong></p> <p>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:</p> <pre><code>#include &lt;iostream&gt; using namespace std; class my_base { public: void non_virtual_test() { cout &lt;&lt; 4 &lt;&lt; endl; } virtual void virtual_test() { cout &lt;&lt; 5 &lt;&lt; endl; } }; class my_derived : public my_base { public: void non_virtual_test() { cout &lt;&lt; 6 &lt;&lt; endl; } void virtual_test() { cout &lt;&lt; 7 &lt;&lt; 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-&gt;non_virtual_test(); // This outputs 7, since the vtable pointer points to // my_derived::virtual_test(), not to my_base::virtual_test(). base_obj-&gt;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; } </code></pre> <p><strong>PART THREE</strong></p> <p>Since no virtual function example is complete without an example with animals...</p> <pre><code>#include &lt;iostream&gt; using namespace std; class animal { public: virtual void say_something() { cout &lt;&lt; "I don't know what to say." &lt;&lt; endl &lt;&lt; "Let's assume I can growl." &lt;&lt; endl; } /* A more sophisticated version would use pure virtual functions: * * virtual void say_something() = 0; */ }; class dog : public animal { public: void say_something() { cout &lt;&lt; "Barf, barf..." &lt;&lt; endl; } }; class cat : public animal { public: void say_something() { cout &lt;&lt; "Meow, meow..." &lt;&lt; endl; } }; int main() { animal *a1 = new dog(); animal *a2 = new cat(); a1-&gt;say_something(); a2-&gt;say_something(); } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload