c++dynamic-bindingrun-time-polymorphism

Virtual function calling a non-virtual function and vice versa


I have a class A as a base class of class B.

I have called the non-virtual function, abc(), within my virtual function, xyz(), as mentioned below.

Due to run-time polymorphism, B:xyz is called – I understand this.

However, I don't understand, why was it followed by B:abc and not A:abc, as abc is a non-virtual function.

Please note: I have come across the following question: Virtual function calling a non-virtual function. It mentions that calling abc() within the virtual function is equivalent to this->abc(), hence the output. However, I am not sure I understand this part.

Because, when I do the opposite (i.e. a non-virtual function calling a virtual function), that time correct run time polymorphism is displayed. What happens to the this pointer then?

//Virtual function calling non-virtual 
class A
{
  public:
  void abc()
  {
    cout<<"A:abc"<<endl;
  }

  virtual void xyz()
  {
    cout<<"A:xyz"<<endl;
    abc();
  }
};


class B: public A
{
  public:
  void abc()
  {
    cout<<"B:abc"<<endl;
  }

  void xyz()
  {
    cout<<"B:xyz"<<endl;
    abc();
  }
};

int main() {

  A *obj3 = new B;
  obj3->xyz();\
  return 0;
}
Output
B:xyz
B:abc
//Non-virtual calling virtual function
#include <iostream>
using namespace std;

class A
{
  public:

  void abc()
  {
    cout<<"A:abc"<<endl;
    xyz();
  }

  virtual void xyz()
  {
    cout<<"A:xyz"<<endl;
  }
};

class B: public A
{
  public:

  void abc()
  {
    cout<<"B:abc"<<endl;
    xyz();
  }

  void xyz()
  {
    cout<<"B:xyz"<<endl;
  }
};

int main() {

  A *obj3 = new B;
  obj3->abc(); 
  return 0;
}
Output
A:abc
B:xyz

Solution

  • Calls to your non-virtual abc function are resolved, effectively, at compile time: so, when that is called from within another member function of class B, the class B version of the function is called, and is passed a pointer (this) to the object from which it is being called; similarly, if called from within a class A function, then the class A definition will be used. That is to say, to the compiler, non-virtual functions associate with a class, rather than any particular instance of the class.

    However, your virtual xyz function is handled differently by the compiler; in this case, a reference or pointer to the function is added to the class definition (this is generally added into what is known as a vtable, although the details are implementation-specific); when any objects of your class(es) are created, they include a copy of that function pointer and/or vtable. When the compiler sees code to call such a virtual function, it translates that into a call via the appropriate function pointer; so, the function 'travels with' the actual object: whether the function is called from the derived class or base class (in your code) is irrelevant – the function called is the one belonging to the object (instance) from which it is invoked.

    In summary: calls to non-virtual functions are resolved at compile time, whereas calls to virtual functions are (conceptually) resolved at run time.

    To see creation of this "vtable" in action, try compiling and running the following code:

    #include <iostream>
    
    class A {
    public:
        int i;
        ~A() = default;
        void foo() { std::cout << i << std::endl; }
    };
    
    class B {
    public:
        int i;
        virtual ~B() = default;
        virtual void foo() { std::cout << i << std::endl; }
    };
    
    int main()
    {
        std::cout << sizeof(A) << std::endl;
        std::cout << sizeof(B) << std::endl;
        return 0;
    }
    

    The only difference between the two classes is that one has virtual functions and the other doesn't – yet that causes a significant difference in the size of class objects: the size of the vtable (with, possibly, some 'padding' for optimal alingment of data)! (On my 64-bit Windows, using MSVC, I get sizes of 4 and 16, but the actual values will vary between compilers and platforms.)