-1

I have a doubt about C++ virtual table recently.

Why does C++ use virtual table?

=>Because C++ compiler does not know the actual function address

--->Why?

=>Because C++ compiler does not know the exact type(Cat? Dog? Animal?) of the object the pointer "panimal" points to

---Why? Is that any way compiler can figure out the object type?

=>Yes, I think the compiler can make it via tracking object type.

Let's consider the sources where an object pointer gets its value. 2 sources indeed.

  1. another pointer
  2. address of class instance Where does "another pointer" get its value? Eventually, there's a pointer that gets its value from "class instance".

So, via tracking the assignment thread backwards to the original source object

  => the compiler is able to figure out the exact type of a pointer.

  =>the compiler knows the address of the exact function being called

  =>no virtual table is needed.

Object type tracking saves both virtual table memery and virtual table pointer of each class instances.

Where does object type tracking not work?

Library Linking.

If a library function returns a base-class pointer, there's no way for the compiler to track back to the original source object. The compiler can probably adapt to library code and none-library code. For library classes that are exported out, use virtual table. For other classes, just track theire object type to save memory.

I am not sure whether there's some error in above statements, please kindly point it out if any. Thanks in advance~

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Guocheng
  • 543
  • 3
  • 15

3 Answers3

1

In some cases, yes, the compiler can figure out the type a pointer points to at compile time. It is quite easy to construct a case where it cannot though.

int x;
cin >> x;
Animal* p;
if (x == 10)
    p = new Cat();
else
    p = new Dog();

If the compiler can, in all cases, prove the type of an object, it is free to eliminate virtual tables from its generated code, as per the as-if rule.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
0

the compiler is able to figure out the exact type of a pointer.

yes, but how do you want it to call the right function at runtime? The compiler knows, but c++ has no virtual machine to tell it the type of the object being passed at runtime, ergo, the need for a vtable for virtual functions of inherited types. Would you rather the compiler creates code for all the different code paths that lead to the execution of each virtual function so it calls the right function at runtime? That would lead to much much bigger binaries, if at all possible.

Jules G.M.
  • 3,624
  • 1
  • 21
  • 35
  • As the compiler knows the exact type, then it knows the address(or symbol) of the exact function. it can generates codes like "call function_cat_spark".(suppose Cat is subclass of animal) – Guocheng Nov 20 '14 at 07:54
  • how do you suggest the compiler implements that in machine code, expressing yourself in assembly? the compiler is not there at runtime. – Jules G.M. Nov 20 '14 at 07:55
  • there would need to be a copy of each function for each possible type combination, or a switch using a map with the function pointers associated to each pointers. but that would be exactly like using a vtable. – Jules G.M. Nov 20 '14 at 07:56
  • 1
    @Guocheng the compiler already does that when possible. With LTO and LTCG it's going to happen for every call that is virtual that is not used in a polymorphic sense. Any actual (potentially) polymorphic call cannot be replaced as such, so your proposal will not fix anything. – dascandy Nov 20 '14 at 08:04
0

In this example it becomes clear that, whatever the static code analysis the compiler could take, the actual method that gets called at ptrA->f(); can only be known at runtime.

#include <sys/time.h>
#include <iostream>
#include <stdlib.h>

struct A {
    virtual int f()
    {
        std::cout<<"class A\n";
    }
};

struct B: public A {
    int f()
    {
        std::cout<<"class B\n";
    }
};

int main()
{
    A objA;
    B objB;
    A* ptrA;

    timeval tv;
    gettimeofday(&tv, NULL);

    unsigned int seed = (unsigned int)tv.tv_sec;
    int randVal = rand_r(&seed);
    if( randVal < RAND_MAX/2)
    {
        ptrA=&objA;
    }
    else
    {
        ptrA=&objB;
    }

    ptrA->f();
    return 0;
}`
  • 1
    This answer is essentially a copy (with fewer explanation) of the accepted answer (from over 2 years ago) - why did you feel the need to add this? – UnholySheep Feb 01 '17 at 20:45