thelittlegumnut
thelittlegumnut

Reputation: 307

Calling a virtual function from within an inherited function?

I've tried to map it out in my head, but honestly I have no idea what's really going on here.
What exactly is happening when I add and remove the virtual keyword from the below example?

#include <iostream>
#include <string>

class A {
public:
    A() { me = "From A"; }
    void caller() { func(); }
    virtual void func() { std::cout << me << std::endl; } // THIS LINE!
private:
    std::string me;
};

class B : public A {
public:
    B() { me = "From B"; }
    void func() { std::cout << me << std::endl; }
private:
    std::string me;
};

int main() {
    A a;
    a.caller();
    B b;
    b.caller();
    return 0;
}

With the virtual keyword, it prints "From A", then "From B".
Without the virtual keyword, it prints "From A", then "From A".

So far, this is the only time I've found a use for virtual functions without pointers being involved. I thought that if the virtual keyword was removed, the compiler would do the standard thing which is to overload the inherited function and end up printing "From A", and "From B" anyway.

I think this is deeper than just the VTable, and that it's more about the way it behaves in particular circumstances. Does B even have a VTable?

Upvotes: 0

Views: 112

Answers (3)

Christophe
Christophe

Reputation: 73542

In your example, you won't see the difference:

  • With the virtual function, the compiler will generate a call via the VTable and at runtime, each objects will call the right function for their real class.

  • With the non virtual function, the compiler determines at compile time the right function to call, based on the objects defined class.

Now try the following, to see the virtual function in action:

A  *pa = &b;       // pointer to an A: valid as b is a B wich is also an A.  
pa -> caller();    // guess what will be called if virtual or not. 

No need for pointer to experimenting with virtual functions. You can observe the same effect with references as well:

A& ra = b;       // create a reference to an A, but could as well be a parameter passed by reference.
ra.caller(); 

Virtual functions are useful for polymorphism. The idea is that you work with a general object of a class, but you don't know at compile time, if at runtime the object will really be of this class, or if it will not be a more specialiszed object (inheriting from the class).

Upvotes: 0

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145429

The call

func()

is equivalent to

this->func()

so there is a pointer involved.

Still, there's no need to involve pointers to understand the behavior.

Even a direct call of e.g. b.func() has to work as if it's a virtual call, when func is virtual in the statically known type. The compiler can optimize it based on knowing the most derived type of b. But that's a different kind of consideration (optimizations can do just about anything).

Upvotes: 1

ach
ach

Reputation: 2373

Apart from the issue of virtual dispatch, what may bring extra confusion, is that you have two mes, one declared in A and another declared in B. These are two distinct objects.

An object of type B has two data members of type std::string; one on its own, and one incorporated into the subobject of type A. The latter one, though, is not immediately available in the methods of type B because its name is eclipsed by the new me introduced in this class (though you may use a qualified name, A::me to refer to it).

Therefore, even though the bodies of A::func and B::func seem identical, the identifier me used in both of them refers to different members.

Upvotes: 0

Related Questions