Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ virtual function call without pointer or reference

As far as I know, virtual function call usually requires pointer or reference. So I am very surprised by the following codes.

#include <iostream>
using namespace std;
class B{
public:
  void runB(){        call();  }
  virtual void call(){ cout<<"B\n"; };
};

class D: public B{
public:
  void runD(){       runB();   }
  void call(){       cout<<"D\n";  }
};

int main(){
 D d;
 d.runD();
}

The output is

D

Could someone please comment why this virtual function call works? Thanks。

like image 507
chao Avatar asked Aug 13 '14 01:08

chao


People also ask

Can I call a virtual function with object?

A virtual function is a member function that you expect to be redefined in derived classes. When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class's version of the function.

Why is a pointer required for virtual function?

The pointer points to the location of the correct code for that member function. So if one virtual function was inherited and not changed, then its table entry points to the definition for that function that was given in the parent class (or other ancestor class if need be).

Do virtual functions need to be overriden?

¶ Δ A pure virtual function is a function that must be overridden in a derived class and need not be defined. A virtual function is declared to be “pure” using the curious =0 syntax.

Can a virtual function call a non virtual function?

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


3 Answers

The difference between virtual to not virtual is:

not virtual - always goes by the caller object/reference/pointer type.

virtual - reference/pointer - goes by the created object type.

virtual - object - goes by the caller.

for example:

class A{
public:
    virtual void f(){
        cout <<"A\n";
    }
};
class B: public A{
public:
    virtual void f(){
        cout <<"B\n";
    }
};


B b;
A a,*pa=&b;
a.f(); //A: caller type = created type - same for not virtual
b.f(); //B: caller type = created type - same for not virtual
((A)b).f(); //A: object goes by the caller type - same for not virtual
pa->f(); // B: pointer goes by the created type - it would be A if it was not virtual!!
like image 90
SHR Avatar answered Oct 17 '22 03:10

SHR


Within a member function, any references to other member functions or variables are implicitly resolved via the this pointer. So in the definition of runB(), the call() really means this->call(). The virtual function call is performed using the current object's virtual table.

like image 13
Wyzard Avatar answered Oct 17 '22 01:10

Wyzard


Firstly, virtual function call does not require a pointer or a reference. As far as the language is concerned, any call to virtual function is a virtual call, unless you explicitly suppress the virtual dispatch mechanism by using a qualified function name. For example, these

d.D::call(); // calls `D::call()` directly
d.B::call(); // calls `B::call()` directly

are calls which were explicitly forced to be non-virtual. However, this

d.call(); // calls `D::call()` virtually

is a virtual call. In this case it is immediately obvious to the compiler that the target function is D::call(), so the compiler normally optimizes this virtual call into a regular direct call. Yet, conceptually, d.call() is still a virtual call.

Secondly, the call to call() made inside B::runB() is made through a pointer. The pointer is present there implicitly. Writing call() inside B::runB() is just a shorthand for (*this).call(). this is a pointer. So that call is made through a pointer.

Thirdly, the key property of virtual call is that the target function is chosen in accordance with the dynamic type of the object used in the call. In your case, even when you are inside B::runB() the dynamic type of the object *this is D. Which is why it calls D::call(), as it should.

Fourthly, what you really need a pointer or a reference for is to observe the actual polymorphism. Polymorphism proper occurs when static type of the object expression used in the call is different from its dynamic type. For that you do indeed need a pointer or a reference. And that is exactly what you observe in that (*this).call() call inside B::runB(). Even though the static type of *this is B, its dynamic type is D and the call is dispatched to D::call().

like image 8
AnT Avatar answered Oct 17 '22 02:10

AnT