Where is my virtual function???

M

Maxim Rogozhin

HI, All!
I've created the following class hierarchy in MS Visual Studio:

class B {
};

class D : public B {
public:
virtual void g();
};

void D::g() {}

and created my object:
D* p = new D;

So far so good. I can see an "B" entry and __vfptr (const
D::'vftable') entry under 'p' entry in "Locals" window, and I can see
an [0x0] entry for function g() under __vfptr.

But if I add a virtual function f() in class B:
class B {
public:
virtual void f();
};

void B::f() {}

class D : public B {
public:
virtual void g();
virtual void f();
};

void D::g() {}
void D::f() {}

then virtual function g() disappears from __vftbl. Moreover - __vfptr
entry has moved under the "B" entry in "Locals" windows (it was under
'p' entry formerly).

So my question is where has my function g() got?? And why __vfptr
moves from 'p' to 'B' ?

Thanks in advance!!!
 
S

SG

[...] MS Visual Studio [...]
[...] __vfptr (const D::'vftable') [...]
[...] __vfptr moves from 'p' to 'B' [...]

You are not supposed to care about these implementation details. They
are not part of the C++ standard.

Cheers!
SG
 
M

Maxim Rogozhin

[...] MS Visual Studio [...]
[...] __vfptr (const D::'vftable') [...]
[...] __vfptr moves from 'p' to 'B' [...]

You are not supposed to care about these implementation details. They
are not part of the C++ standard.

Cheers!
SG

OK. But VS shows me vtable and its entries. I want to figure out why
function g() is missing.
 
F

Francesco S. Carta

[...] MS Visual Studio [...]
[...] __vfptr (const D::'vftable') [...]
[...] __vfptr moves from 'p' to 'B' [...]

You are not supposed to care about these implementation details. They
are not part of the C++ standard.

Cheers!
SG

OK. But VS shows me vtable and its entries. I want to figure out why
function g() is missing.

I'm not 100% sure if this very case falls under the implementer
freedoms, but surely a compiler can drop parts of code if it can prove
that those parts are never ever used during runtime - i.e. just
optimizing the executable size.

If you really feel you want to investigate the issue, you might try
creating different tests, some where you define and use some functions,
some other where you define but /don't/ use them.

In any case, this is an off-topic issue for this NG, strictly speaking -
you would find more ad-hoc information in a MS group/forum, but you
could also generalize the issue if you are interested in the mechanism
as one of the C++ features and continue the discussion here.
 
M

Maxim Rogozhin

Why do you care?

I learn COM. COM technology is based on binary layout of vtbl. I just
wanted to experiment with my custom interfaces and classes to make
sure that things go as described in COM and C++ books)
 
Ö

Öö Tiib

I learn COM. COM technology is based on binary layout of vtbl. I just
wanted to experiment with my custom interfaces and classes to make
sure that things go as described in COM and C++ books)

You mean DCOM? That port 135 worm-hole. The various registered objects
fill registry with various crap and turn it slow. It has been
deprecated in favor of .NET framework ... so no surprize if MS
announces 5 years later that "Oh sorry Maxim, all you studied was
worthless junk so it will be removed from next Windows Budget-Extra-
Ultima .NET-64#."
 
R

ralph

I learn COM. COM technology is based on binary layout of vtbl. I just
wanted to experiment with my custom interfaces and classes to make
sure that things go as described in COM and C++ books)

Then here are two major points about COM you should keep in mind. Both
are well known, but often under-appreciated.

1) "COM" is a protocol. "OLE" is its implementation.
While common conventions have come about for implementing (coding) COM
and Microsoft's implementation has become a "standard" on its own (ie,
OLE 2), as long as COM protocol is obeyed (a COM participate can
consume or source a COM object) a coder/compiler/libraries are
relatively free on how it goes about its implementation.

2) There is no implementation inheritance in COM. (Only interface
inheritance is supported). Thus no matter what mechanical construction
is going on under the covers - COM sees a single result. While a
compiler might be able to mine or resolve elements of a particular
construct, COM is limited to what it first sees - a resultant
interface.

-ralph
 
P

Paul Bibbings

Maxim Rogozhin said:
HI, All!
I've created the following class hierarchy in MS Visual Studio:

class B {
};

class D : public B {
public:
virtual void g();
};

void D::g() {}

and created my object:
D* p = new D;

So far so good. I can see an "B" entry and __vfptr (const
D::'vftable') entry under 'p' entry in "Locals" window, and I can see
an [0x0] entry for function g() under __vfptr.

But if I add a virtual function f() in class B:
class B {
public:
virtual void f();
};

void B::f() {}

class D : public B {
public:
virtual void g();
virtual void f();
};

void D::g() {}
void D::f() {}

then virtual function g() disappears from __vftbl. Moreover - __vfptr
entry has moved under the "B" entry in "Locals" windows (it was under
'p' entry formerly).

So my question is where has my function g() got??

I can only guess at what is happening here, but the following feels like
an at least possible way of looking at it.

In your first version, g() is declared a virtual member of D with no
counterpart in B. I am guessing that the presence of the virtual
keyword in some way requires a vtable to be generated, even though it is
not `used' by your code. D::g() is then added to the vtable because
*something* must be but, as it is not used in a way that /requires/ it to
be virtual, it /could/ be left out as an optimisation if there
were something else to replace it, so to speak. (Remember, D::g() does
not override anything and is not overriden itself, virtually or otherwise.)

In the second version, there is indeed `something else' - your pairing
of B::f() and D::f() for which, now, the virtual specifier has meaning
(or rather, /effect/). D::g() can now be dropped from the vtable as the
proposed optimization. D::f(), however, cannot be, since it overrides
B::f() and *could* be called virtually.

To `ressurect' your D::g(), merely add the virtual member B::g() to B
and you will see two entries in your vtable.
And why __vfptr moves from 'p' to 'B' ?

p is of type D*. Since, in your first example, there are no virtual
members declared in B, D is the `highest' (or `lowest', if you visualize
hierarchies from the bottom up) class that requires the vtable. In your
second example, however, the requirement of a vtable is shifted up (or
down) owing to the presence of a virtually-declared member in B, hence
VS's representation of the vtable shifts likewise to interpose B in the
visual tree. Well... something like that. :)

Regards

Paul Bibbings
 
M

Maxim Rogozhin

I can only guess at what is happening here, but the following feels like
an at least possible way of looking at it.

In your first version, g() is declared a virtual member of D with no
counterpart in B.  I am guessing that the presence of the virtual
keyword in some way requires a vtable to be generated, even though it is
not `used' by your code.  D::g() is then added to the vtable because
*something* must be but, as it is not used in a way that /requires/ it to
be virtual, it /could/ be left out as an optimisation if there
were something else to replace it, so to speak.  (Remember, D::g() does
not override anything and is not overriden itself, virtually or otherwise..)

In the second version, there is indeed `something else' - your pairing
of B::f() and D::f() for which, now, the virtual specifier has meaning
(or rather, /effect/).  D::g() can now be dropped from the vtable as the
proposed optimization.  D::f(), however, cannot be, since it overrides
B::f() and *could* be called virtually.

To `ressurect' your D::g(), merely add the virtual member B::g() to B
and you will see two entries in your vtable.


p is of type D*.  Since, in your first example, there are no virtual
members declared in B, D is the `highest' (or `lowest', if you visualize
hierarchies from the bottom up) class that requires the vtable.  In your
second example, however, the requirement of a vtable is shifted up (or
down) owing to the presence of a virtually-declared member in B, hence
VS's representation of the vtable shifts likewise to interpose B in the
visual tree.  Well... something like that. :)

Regards

Paul Bibbings

Paul, thank you for your detailed answer!

But I have investigated assembler code generated for p->g() call and
it coincides with assembler code generated for p->f() call. On the
other hand for non-virtual function h() compiler has generated
completely different code:
p->f();
0041159D mov eax,dword ptr [p]
004115A0 mov edx,dword ptr [eax]
004115A2 mov esi,esp
004115A4 mov ecx,dword ptr [p]
004115A7 mov eax,dword ptr [edx]
004115A9 call eax
004115AB cmp esi,esp
004115AD call @ILT+365(__RTC_CheckEsp) (411172h)
p->g();
004115B2 mov eax,dword ptr [p]
004115B5 mov edx,dword ptr [eax]
004115B7 mov esi,esp
004115B9 mov ecx,dword ptr [p]
004115BC mov eax,dword ptr [edx+4] // different offset
004115BF call eax
004115C1 cmp esi,esp
004115C3 call @ILT+365(__RTC_CheckEsp) (411172h)
p->h();
004115C8 mov ecx,dword ptr [p]
004115CB call D::h (4110C3h)

Looks like there's an entry for virtual function g() in vtbl of class
D, but VS doesn't shows it ... for some unknown reason...

Though I'm guessing about this reason - in the second example vtbl
entry is located under 'B' entry, i.e. we see part of vtbl related to
class B. But why VS doesn't show remaining part under entry for
'p' (for class D) then???
 
J

James Kanze

I've created the following class hierarchy in MS Visual Studio:
class B {
};
class D : public B {
public:
virtual void g();
};
void D::g() {}
and created my object:
D* p = new D;
So far so good. I can see an "B" entry and __vfptr (const
D::'vftable') entry under 'p' entry in "Locals" window, and
I can see an [0x0] entry for function g() under __vfptr.
But if I add a virtual function f() in class B:
class B {
public:
virtual void f();
};
void B::f() {}
class D : public B {
public:
virtual void g();
virtual void f();
};
void D::g() {}
void D::f() {}
then virtual function g() disappears from __vftbl. Moreover
- __vfptr entry has moved under the "B" entry in "Locals"
windows (it was under 'p' entry formerly).

How do you determine this? If it is with the debugger, it may
be just an artifact of the way the debugger displays
information. However...
So my question is where has my function g() got?? And why
__vfptr moves from 'p' to 'B' ?

This is all very implementation dependent, but in general, in
all of the implementations I know, your class D will only have
one vptr. (Multiple inheritance will introduce more.) In the
same way the compiler puts the data in B in front of the data
added by D, it will put the entries for B in the vtable in front
of those for D. The debugger is probably just displaying those
which are valid for both B and D as for B, and those only valid
for D as for D.
 
S

Stuart Redmann

Maxim said:
But I have investigated assembler code generated for p->g() call and
it coincides with assembler code generated for p->f() call.

What? You even marked the place where these two calls differ from each
other.
On the
other hand for non-virtual function h() compiler has generated
completely different code:
p->f();
0041159D mov eax,dword ptr [p]
004115A0 mov edx,dword ptr [eax]
004115A2 mov esi,esp
004115A4 mov ecx,dword ptr [p]
004115A7 mov eax,dword ptr [edx]
004115A9 call eax
004115AB cmp esi,esp
004115AD call @ILT+365(__RTC_CheckEsp) (411172h)
p->g();
004115B2 mov eax,dword ptr [p]
004115B5 mov edx,dword ptr [eax]
004115B7 mov esi,esp
004115B9 mov ecx,dword ptr [p]
004115BC mov eax,dword ptr [edx+4] // different offset
004115BF call eax
004115C1 cmp esi,esp
004115C3 call @ILT+365(__RTC_CheckEsp) (411172h)
p->h();
004115C8 mov ecx,dword ptr [p]
004115CB call D::h (4110C3h)

Looks like there's an entry for virtual function g() in vtbl of class
D, but VS doesn't shows it ... for some unknown reason...

Yup. VC shows the vtable of the derived class if the base class has no
vtable. Else it will show the vtable of the base class (note the name
of the vtable: B::__vfptr 0x004157cc const D::`vftable', this means
that VC recognizes that it displays the partial vtable of D). This is
probably by design, and if not, this will probably be classified as
"by design" as soon as you complain about it ;-)
Though I'm guessing about this reason - in the second example vtbl
entry is located under 'B' entry, i.e. we see part of vtbl related to
class B. But why VS doesn't show remaining part under entry for
'p' (for class D) then???

Who knows what the people at MS are thinking.

Regards,
Stuart
 
M

Maxim Rogozhin

What? You even marked the place where these two calls differ from each
other.

Mmm... almost coinsides :)

Who knows what the people at MS are thinking.

I have also investigated windbg output (dt -v p) and got the analogous
result - only B-class related part of vtbl is shown. There definitely
must be a reason for such behaviour of MS debuggers!

I have also posted a question here
http://social.msdn.microsoft.com/Forums/en/vsdebug/thread/23f0a79b-1ca9-467a-9c28-70be30a4ea5a
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top