0
#include <iostream>

using std::cout;
using std::endl;

class ObjectA {
public:
    ObjectA(int x):value_(x) {}
    virtual void funcA()
    {cout << "ObjectA funcA" << endl;}
    virtual void funcA1()
    {cout << "ObjectA funcA1" << endl;}
private:
    int value_;
};

void printVirtualTable(ObjectA* objA)
{
    typedef void (*funcPtr)();

    funcPtr* vptr = (funcPtr*)(*((uint64_t*)objA));

    while (*vptr) {
        fprintf(stdout, "%p ", *vptr);
        vptr++;
    }
}

int main()
{
    ObjectA* objA = new ObjectA(19);
    printVirtualTable(objA);
    return 0;
}

ObjectA has 2 virtual methods, so I think the size of the vtable is 2, but printVirtualTable shows there are 3 pointers in the vtable, the first and second pointers are funcA and funcA1, but what is the third one?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 4
    Is it specified anywhere in the standard that the vtable is 'null terminated' so to speak? Also I get different results depending on if i compile with clang or gcc which leads me to think your method of determining vtable size / printing just isnt valid. (And even on clang when i print it the third pointer is very different from the first two leading me to think its unrelated) – Borgleader Jul 06 '21 at 02:13
  • 1
    That is a pair of reinterpret_casts, both of which I'm 99% sure are undefined behavior. This code could print any number and be conformant to the standard. It could also delete your harddrive and still be conformant to the standard. – Silvio Mayolo Jul 06 '21 at 02:16
  • 2
    @Borgleader there is no `vtable` in standart. Standart doesn't mandate that abstract classes need to be implemented via a `vtable`. – ALX23z Jul 06 '21 at 02:16
  • @ALX23z Alright that means then that the answer is no and checking for nullptr at end of vtable is not a valid/guaranteed way of printing the vtable until the end. – Borgleader Jul 06 '21 at 02:17
  • thanks,vtable is not null terminated, so my code is wrong – Yanpeng Chen Jul 06 '21 at 02:33
  • @YanpengChen the compiler knows how many virtual methods are in a given class, and what their indexes are within that class's vtable (if there is one). Calling a virtual method at runtime is just a function call using the appropriate vtable index. So there is no need for the vtable to be null-terminated. – Remy Lebeau Jul 06 '21 at 06:01
  • There's no "t" in "standard" either, @ALX23z. ;) – Ulrich Eckhardt Jul 06 '21 at 06:03
  • Does this answer your question? [Virtual Table layout in memory?](https://stackoverflow.com/questions/1342126/virtual-table-layout-in-memory) – prehistoricpenguin Jul 06 '21 at 06:14
  • 1
    @UlrichEckhardt: *"standard"* without *'t'* is *"sandard"*... ;) – Jarod42 Jul 06 '21 at 08:39
  • The standard doesn't actually require classes with virtual functions to even have a vtable at all. In any event, if the compiler does use vtables as part of your class, the value `sizeof(ObjectA) - sizeof(int)` (more generally, size of the class type minus the sum of the sizes of all its members) is an *upper bound* for the size of the vtable. AS others have said, your code has undefined behaviour, so it is not guaranteed to give any output in particular. – Peter Jul 06 '21 at 10:10

1 Answers1

0

I think you need to change the evaluation condition from while(vptr) to while(*vptr). vptr itself won't be null.

Also if you actually call those functions, you would see at the third call program will crash.

This does not mean that you can depend on this mechanism. It just to answer to your question - there is nothing in third pointer.

      void printVirtualTable(ObjectA* objA)
  {
     typedef void(*funcPtr)();

     funcPtr* vptr = (funcPtr*)(*((unsigned int*)objA));

     if (*vptr) {
        (*vptr)();
        fprintf(stdout, "adddress vtable: %p direct address: %p\n", vptr, &objA);
     }
     vptr++;
     if (*vptr) {
        (*vptr)();
        fprintf(stdout, "adddress vtable: %p direct address: %p\n", vptr, &ObjectA::funcA);
     }
     vptr++;
     if (*vptr) {
        (*vptr)();
        fprintf(stdout, "adddress vtable: %p direct address: %p\n", vptr, &ObjectA::funcA1);
     }
  }