How C++ invokes virtual functions

Greetings, everyone. This is my first post in the forum and I hope this is the right place for my question.

My curiosity drives me to think what would happen if class A's vptr is replaced with that of class B. Will A actually calling B's virtual functions stored in B's vtable? With these question, I wrote the following code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

class TestA
{
public:
	virtual void f(void)
	{ std::cout << "TestA::f()" << std::endl; }
};

class TestB
{
public:
	virtual void f(void)
	{ std::cout << "TestB::f()" << std::endl; }
};

int main(int argc, char* argv[])
{
	TestA a ;
	int * vptrA = (int *)(&a + 0);

	TestB b;
	int * vptrB = (int *)(&b + 0);

	// replace the vptr of TestA with that of TestB
	*vptrA = *vptrB;

	TestA *pa = &a;

	a.f();   // PRINTS "TestA::f()"
	pa->f(); // PRINTS "TestB::f()"

	return 0;
}


I make the program in Ubuntu 7.10 and g++-4.2.4. I think after the substitution, a.f() and pa->f() would both print "TestB::f()". And now I am wondering why a.f() and pa->f() prints different result. Anyone could help me? Thanks in advance:)
You cannot do anything like this in C++. If it works on X compiler it is merely by chance, and there is no guarantee (or even a likelyhood) that it will work on a different compiler.

This should never be done in code. I suppose if you're just trying to satisfy your curiosity that's fine -- but just remember that you're not learning about C++, you're learning about X compiler for Y platform.
Last edited on
It works somehow all right unlike inherited from abstract ones.
Thanks for Disch's suggestions. Surely this manner is forbidden in code. However, I am still wondering why a.f() and pa->f() show different result. Do you mean the way of invocation of the compiler and platform makes the result different?

Best regards
Since dereferencing the vtable requires a lookup and indirection, the compiler might be optimizing. Because 'a' is an object and not a pointer, the compiler might try to use TestA::f first if it exists, because that's undoubtedly what the vtable says to use (at least, under normal conditions). Then it can call the function direct rather than use the vtable.

But because pa is a pointer, it might not be a TestA object (might be a derived class or something) and so it uses the vtable.
Line 30 the compiler knows for 100% certainty that the object you are calling f() on is an A because the type of "a" is "A". Therefore, as Disch said, the compiler is optimizing the call by calling the function directly instead of through the vtable. In the second case, because it is a pointer, the compiler, in a reasonable context-free language, cannot know that the pointer is really referring to an A, so it has to resort to the vtable lookup.

Again, as Disch mentioned, this is compiler-specific behavior, so results may vary from compiler to compiler.


Ah-ha, Compiler optimization. Thanks all for your answers and I learn a lot from them.
Topic archived. No new replies allowed.