3

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
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Sumaiya A
  • 137
  • 1
  • 14
  • 1
    Please clarify *However I dont understand, why was it followed by B:abc and not A:abc as abc is a non-virtual function* What do you mean my *followed by B:abc*? Far as I can tell `B::abc` is never invoked. – user4581301 Apr 12 '21 at 21:09
  • @user4581301 check the output of first case – Sumaiya A Apr 12 '21 at 21:15

1 Answers1

4

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.)

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • So even when a virtual function is called from within a member function, the ‘actual object’ is used and not ‘this’? – Sumaiya A Apr 12 '21 at 21:21
  • 1
    The `this` pointer is still used (it is an 'invisible' parameter to any non-static member function calls); in fact, it is even more important, because that (i.e. this) points to the object that has the vtable. – Adrian Mole Apr 12 '21 at 21:22
  • Code 2: non virtual function of class A is called i.e. abc(). From within this member function of class A, a virtual function xyz is called. So within the member function of class A, if vptr is used to resolve then wouldn’t this->vptr of class A be used? How did it rightly called derived class function. – Sumaiya A Apr 12 '21 at 21:28
  • 1
    @SumaiyaA Read my answer again: the vtable is taken from ***each object at runtime*** and the `this` pointer in your Code 2 points to a `B` object (because it was created with `new B`). That is polymorphism working: the object (and its pointer) carries its virtual function with it, *wherever* it goes! – Adrian Mole Apr 12 '21 at 21:31
  • *"whereas calls to virtual functions are (and can only be) resolved at run time"*. Optimizer might *"de-virtualize"* the code, following the as-if rule. From OP code, we "know" that dynamic type of `obj3` is `B`. – Jarod42 Apr 13 '21 at 09:07
  • @Jarod42 Fair point - edited to be less assertive about run-time resolution. – Adrian Mole Apr 13 '21 at 10:52