Click to See Complete Forum and Search --> : vtable question: why its own method is called rather than the parent's one?


haiyeong
May 30th, 2008, 08:17 PM
class B derives from class A,
A has two virtual methods: f1( ) and f2( ). In A::f1( ), it call f2( ).
B override both virtual methods. In B::f1( ), it call A::f1( ). The question is: why B::f2( ) is called rather than A::f2( ).

Thanks!



#include <stdio.h>

class A
{
public: virtual void f1()
{
f2();
}

public: virtual void f2()
{
printf("A.f2()\n");
}
};

class B : A
{
public: virtual void f1()
{
A::f1();
}
public: virtual void f2()
{
printf("B.f2()\n");
}
};

int main(int argc, char** argv)
{
B * b = new B();
b->f1();
delete b;
}


The output is:
>> B.f2()

spoon!
May 30th, 2008, 09:34 PM
It looks reasonable. What did you expect it to do? The method "f2" is virtual, so it calls the "f2" method of the actual type of the object, which is B.

Lindley
May 30th, 2008, 09:37 PM
Since you're using a B* anyway, of course it's going to call B's method. If you were using an A* you'd get the same behavior, but it would at least make sense to be surprised in that case.

I suspect you've got a backwards notion of how inheritance works......children can override parents, not the other way around.

haiyeong
May 30th, 2008, 10:42 PM
what I was thinking is how the execution pointer been moved back from A::f1( ) back to B.

vtable for A:
0: pointer to A::f1
4: pointer to A::f2

vtable for B:
0: pointer to B::f1
4: pointer to B::f2

The execution process would be:
1. call b.f1( ) -- execution pointer is set to the first item in B's vtable
2. call A::f1( ) -- execution pointer is set to the first item in A's vtable
3. call f2( ) --
here is where question comes. Since the execution pointer is still pointing to the code segment belonging to A::f1( ), how could it change to B::f2?

Thanks.

Lindley
May 30th, 2008, 11:10 PM
I don't know the specifics of the vtable, but if you've got a B* pointer to a B object, I don't see why A's methods would get involved at all.

haiyeong
May 30th, 2008, 11:26 PM
I guess it can be explained by "this" pointer. In A::f1( ), the f2( ) is called actually in the form of "this->f2( )". Therefore, when we call b->f1( ), the "this" pointer is pointing to B object, which cause "this->f2( )" jump to B's f2( ).

As Lindley indicated, the interesting thing happened if we change the main function a little bit:


int main (...)
{
B * b = new B();
((A*)b)->f1( );
delete b;
}


Any ideas about the output from this code?

Lindley
May 30th, 2008, 11:45 PM
It should be the same, since the actual object is a B, the method called will be B's method where one exists. A method from A will only be called if it is non-virtual or if B does not define an overriding function.

As to what I said before:
I don't know the specifics of the vtable, but if you've got a B* pointer to a B object, I don't see why A's methods would get involved at all.

There is an exception to that: The constructor of A will be called in the process of calling B's constructor. Also, since you didn't declare A's destructor virtual, which destructor gets called (both or just A's) will depend on what kind of pointer you cast to for the delete call.

Paul McKenzie
May 31st, 2008, 12:04 AM
I guess it can be explained by "this" pointer. In A::f1( ), the f2( ) is called actually in the form of "this->f2( )". Therefore, when we call b->f1( ), the "this" pointer is pointing to B object, which cause "this->f2( )" jump to B's f2( ).The f2() is a virtual function. The B class has the virtual function implemented. Regardless of which class you're in, if you have a B object, and call a virtual function that is implemented in B, you better get B's implementation called. If this didn't happen, the whole point of using virtual functions would be lost.

The time things don't work the way I've described is where somewhere in the chain of function calls, the instance of B is lost, and you wind up with an A object. This can occur when you overload a function with references or pointers to A and B. See this article:

http://en.wikipedia.org/wiki/Double_dispatch

The other time things do not work is if the virtual mechanism is attempted to be used during object construction. You cannot call a virtual function in a constructor, and have it automatically call the derived version's implementation. In this scenario, it will always call the base class version of the function.
As Lindley indicated, the interesting thing happened if we change the main function a little bit:

int main (...)
{
B * b = new B();
((A*)b)->f1( );
delete b;
}

Any ideas about the output from this code?Virtualness does not disappear because you casted up. You are still going to call B's methods. If you want to call A's methods, you have to explicitly do it using "A::" (unless you have a scenario as described in the double dispatch description)

What is more strange is that you cannot call A methods at all if A has a public method and it wasn't overriden in B, and you are using a B object. Do you know why? (I know why, but asking you anyway :) )

Example:

#include <stdio.h>

class A
{
public: virtual void f1()
{
f2();
}

public: virtual void f2()
{
printf("A.f2()\n");
}

public: virtual void f3()
{
printf("A.f3()\n");
}
};

class B : A
{
public: virtual void f1()
{
A::f1();
}
public: virtual void f2()
{
printf("B.f2()\n");
}
};

int main(int argc, char** argv)
{
B b;
b.f3(); // error, Cannot call f3().
}

Regards,

Paul McKenzie

Graham
May 31st, 2008, 06:21 AM
what I was thinking is how the execution pointer been moved back from A::f1( ) back to B.

vtable for A:
0: pointer to A::f1
4: pointer to A::f2

vtable for B:
0: pointer to B::f1
4: pointer to B::f2

The execution process would be:
1. call b.f1( ) -- execution pointer is set to the first item in B's vtable
2. call A::f1( ) -- execution pointer is set to the first item in A's vtable
This is where your thinking has gone wrong. The object is a B, it only has a B vtable - when the code in A::f1 is executed, the vtable is still B's, not A's. Only when the object is of type A will you get an A vtable (e.g. during construction/destruction when it's in A's constructor or destructor, if you create an object of type A, or if you slice a B object by passing it to a function that takes an a argument by value).

haiyeong
May 31st, 2008, 04:53 PM
Thanks guys. I know that B::f2( ) should be called otherwise polymorphism means nothing. I guess I was puzzled by the virtual function on the execution level or compiler level, how this dynamic call can be implemented when compiling or executing code. I may find a textbook on compiler design to recall those knowledge.

BTW, http://en.wikipedia.org/wiki/Double_dispatch is an interesting article. Thanks again.

haiyeong
May 31st, 2008, 05:01 PM
Hi Paul,

I tried the f3( ) method, it works in my compiler: g++ (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu7). why you say " b.f3(); // error, Cannot call f3()."?

quote:
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

int main(int argc, char** argv)
{
B b;
b.f3(); // error, Cannot call f3().
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

souldog
May 31st, 2008, 05:15 PM
if you got it to compile then you changed it.

Paul put something subtle in B definition

Paul McKenzie
May 31st, 2008, 05:25 PM
Hi Paul,

I tried the f3( ) method, it works in my compiler: g++ (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu7). why you say " b.f3(); // error, Cannot call f3()."?Did you try the *exact* code I posted? Don't change anything. Just copy and paste it into your compiler's editor. As a matter of fact, I got the A and B classes from the first code you posted.

If you still do not get an error, your compiler is broken.

Regards,

Paul McKenzie

haiyeong
May 31st, 2008, 06:01 PM
Oh, I see. The trick is "public B : A" rather than "public B : public A". The default is "private" in C++ derivation, therefore method f3( ) in B is a private member. I tried it again through copy&paste this time, and there IS an error.

souldog
May 31st, 2008, 07:13 PM
Maybe of interest

http://www.parashift.com/c++-faq-lite/private-inheritance.html