-1

I'm using VS 2013 and trying to see how vptr and vftable are working at object level. So I have the following classes:

#include<iostream>
using namespace std;

class baseClass
{
public:
    void nonVirtualFunc() {}
    virtual void virtualNonOverriddenFunc() {}
    virtual void virtualOverriddenFunc() {}
};

class derivedClass : public baseClass
{
public:
    virtual void virtualOverriddenFunc() {}
    virtual void derivedClassOnlyVirtualFunc() { cout << "derivedClass" << endl; }
};


int main(int argc, char** argv) {

    derivedClass derivedClassObj2;
    cout << "Size of derivedClassObj: " << sizeof(derivedClassObj2) << endl;

    return 0;
}

And this is what I see when debugging: enter image description here

Theoretically there should be TWO vptrs. One for the vftable of baseClass and one for derivedClass to track the newly added derivedClassOnlyVirtualFunc().

But as you see, there is only one vptr/vftable. But the mechanism works fine.

I thought there is second vptr that I cannot see in the watch window, so I printed out the size of the object. It is 4 bytes, indicating that only one pointer is present.

So how is this working with the newly added virtual function?

According to this there should be two vptrs.

EDIT: I checked the memory contents of vftable as Serge suggested and there indeed are three entries. enter image description here For some reason it is not showing up in the debugger.

Cheers.

Community
  • 1
  • 1
madu
  • 5,232
  • 14
  • 56
  • 96
  • No, because when you create a derived object, the entries in the vtable are replaced with the addresses of derived's virtual functions (if derived overrided them). This is done during construction of the object which explains why you shouldnt call virtual functions in constructors/destructors. – Borgleader Dec 01 '14 at 15:42
  • Why should there be two in the object? One is sufficient, the derived classes vtable is based on the base-classes, adding additional entries for more virtuals and replacing others to point to the new implementation. – Deduplicator Dec 01 '14 at 15:44
  • @Borgleader: Calling virtual functions in ctors and dtors is fine. – Deduplicator Dec 01 '14 at 15:44
  • 1
    @Deduplicator ... assuming you understand exactly which overriders get called, which not everyone does. – Angew is no longer proud of SO Dec 01 '14 at 15:48
  • 1
    It's simple, the one of the type whos ctor / dtor is just executing. (Beware of the ctor-init-list there though.) – Deduplicator Dec 01 '14 at 15:49
  • But there is a NEW virtual function added in derivedClass. Where is the entry for that virtual? That is my confusion. – madu Dec 01 '14 at 15:49
  • 1
    @madu Could be the debugger playing tricks on you, showing the "base" part of the vtable only. The entire vtable is certain to have an entry for the new function as well, appended to it. – Angew is no longer proud of SO Dec 01 '14 at 15:51
  • @Angew Yes thats what I thought. But when I printout the size of the object, it is 4 bytes. This is same as when there is only one vptr. – madu Dec 01 '14 at 15:52
  • 2
    @Griwes: There is virtual dispatch, and pretending otherwise is a potentially fatal error. The class-hierarchy is just capped below the target most-derived type. – Deduplicator Dec 01 '14 at 16:02
  • @Deduplicator any idea how I can view the complete vftable? – madu Dec 01 '14 at 16:10
  • 1
    @madu There *is* only one "vptr" but your debugger is only displaying the two entries from the table for the base class. From the top answer to the question you linked: "in reality compilers merge different vtables into a single vtable". – molbdnilo Dec 01 '14 at 16:11
  • 1
    @madu Yes, there is only one vptr. Pointing to a vtable of 3 elements. Maybe the debugger is confused somehow and only showing you the first 2 elements. – Angew is no longer proud of SO Dec 01 '14 at 16:11
  • @Deduplicator, yeah, I overstated the thing. There is virtual dispatch, but unless your use case is absurdly insane (like the one [here](http://en.cppreference.com/w/cpp/language/virtual), for example), the virtual dispatch behaves as if it didn't really exist (as long as your use case is not absurdly insane, the compiler can see exactly which functions you are calling statically). (Leaving absurdly insane cases out of an explanation is a good thing.) – Griwes Dec 01 '14 at 16:14
  • As for the question itself: I can't see how this can be potentially fitting [tag:c++]; the language has no notion of vtables or vptrs. – Griwes Dec 01 '14 at 16:15
  • The debugger shows only two entries because you are looking at the `baseClass` subobject, and the `baseClass` vtable has only two entries. – Raymond Chen Dec 02 '14 at 04:04
  • @RaymondChen any idea how I can look at the new virtual function entry? Thanks. – madu Dec 02 '14 at 04:16
  • 1
    Use the memory window. The debugger is not going to help you much because this is internal detail that you should not be relying on. – Raymond Chen Dec 02 '14 at 04:27
  • @RaymondChen Thank you. Yes I finally found it in memory window as suggested by Serge. I wish it would have been more obvious. – madu Dec 02 '14 at 04:30

1 Answers1

1

The vtable implementation is compiler dependant. The size of the object (4 bytes) show that the vtable is not replicated in the object since 4 bytes is just one pointer. My understanding is :

  • there is one and only one vtable(*) per class (not per object)
  • each object has a pointer to its vtable (the one of its actual class)
  • as _vfptr is an attribute of the ancestor class, the debugger shows it under the ancestor class, and as such only shows the methods defined in that class

But for sure the real _vtable contains entry for the other virtual methods ... after the entries displayed by the debugger !

(*) Things become harder when you think about the internal organization of the _vfptr array. In fact, it can be seen as containing copies of all the vtables of ancestor classes. Here, the 2 first entries of derivedClass correspond to the vtable of baseClass. But if you open a Memory window, and examine what is at the _vfptr address (0x00d9ba68 in your example) you should see a third entry before a null entry (at least that is what my MSVC Express 2008 shows). This third entry correspond to the function derivedClassOnlyVirtualFunc but is not shown by debugger as I said above.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • 1. Even the *existance* of a vtable (if any) is an implementation-detail. 2. Take a look at `std::iostream`. Per my count, it has 3 vtables, because each instance has 3 vtable pointers. (On some common implementations.) 3. Neither basic types, nor classes with neither virtual bases nor members have a vtable and vtable-pointer. – Deduplicator Dec 01 '14 at 20:10
  • @Serge Thank you. I did look at the memory contents and there seems to be no third entry. I have updated the original question with the memory screenshot. – madu Dec 02 '14 at 00:07
  • @Serge You are right. There is an entry. I made a mistake in the code. Thank you for this. It's strange why it wouldn't show up. – madu Dec 02 '14 at 02:49