5

What is the size of virtual pointer(VPTR) for a virtual table in C++? Also this is not a homework question...just a question that came to my mind while I was reading a C++ book.

Shog9
  • 156,901
  • 35
  • 231
  • 235
Light_handle
  • 3,947
  • 7
  • 29
  • 25
  • It's obviously not homework. Lecturers don't usually ask these kind of questions, at least not at undergraduate level. – isekaijin Oct 21 '09 at 23:19
  • What pointer are you talking about? VPTR is not exactly a standard nomenclature. Are you interested in a pointer *to* `vtbl` stored in each object? Or are you interested in pointers that compose the `vtbl` *itself*. – AnT stands with Russia Oct 22 '09 at 00:52
  • 1
    @Leon: Or at any level, as it is an implementation details that is not consitent across compilers. Also not not all compilers use V-Tables to implement virtual functions (it just happens to be the easiest technique). – Martin York Oct 22 '09 at 00:52
  • @Andrey T: I am talking about the pointer to vtbl stored in each object. – Light_handle Oct 22 '09 at 02:53

6 Answers6

7

An excellent article related to this topic is Member Function Pointers and the Fastest Possible C++ Delegates. This article delves deeply into the implementation of member function pointers for many different compilers. This article talks about all the nuances of vtable pointers particularly in light of multiple (and virtual) inheritance.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
3

Note that in order to gracefully handle multiple inheritance, there can be more than one VPTR in an object, but in general each is likely to be a simple architecture dependent pointer.

Try running something like this to see how your compiler lays things out:

#include <iostream>
using namespace std;

struct Base
{
    int B;
    virtual ~Base() {} 
};

struct Base2
{
    int B2;
    virtual ~Base2() {} 
};

struct Derived : public Base, public Base2
{
    int D;
};

int main(int argc, char* argv[])
{
    cout << "Base:" << sizeof (Base) << endl;
    cout << "Base2:" << sizeof (Base2) << endl;
    cout << "Derived:" << sizeof (Derived) << endl;

    Derived *d = new Derived();
    cout << d << endl;
    cout << static_cast<Base*>(d) << endl;
    cout << &(d->B) << endl;
    cout << static_cast<Base2*>(d) << endl;
    cout << &(d->B2) << endl;
    cout << &(d->D) << endl;
    delete d;
    return 0;
}

On my 32-bit compiler, this give 8 bytes for both Base classes, and 20 bytes for the Derived class (and double those values when compiled for 64 bits):

4 bytes Derived/Base VPTR
4 bytes int B
4 bytes Derived/Base2 VPTR
4 bytes int B2
4 bytes int D

You can see how by looking at the first 8 bytes, you can treat a Derived as a Base, and how by looking at the second 8 bytes, you can treat it as a Base2.

Eclipse
  • 44,851
  • 20
  • 112
  • 171
2

That depends on your implementation, but it's easy to find out. For this program

#include <iostream>

struct virtual_base {
    int data;
    virtual_base() {}
    virtual ~virtual_base() {}
};

struct non_virtual_base {
    int data;
    non_virtual_base() {}
    ~non_virtual_base() {}
};

int main() {
    std::cout << sizeof( virtual_base ) - sizeof( non_virtual_base ) << '\n';
    return 0;
}

mine (VC 2008) will print 4, so the cost of polymorphism is, in this case, 4 byte.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • Well actually the *size overhead* is 4 bytes; obviously this is also the pointer size (hard to imagine any shorter size), but it could also be "pointer size" + "alignment padding" (e.g. a 16-bit pointer + 16 bit padding). In general I'd say it's the "size of a code pointer" (some implementations may use different sized for code pointers and data pointers). – U. Windl Jun 02 '23 at 10:29
1

Probably the same size as a normal pointer... generally 4 bytes on 32-bit machines. But this would be compiler-dependent, some compilers may do things differently.

Artelius
  • 48,337
  • 13
  • 89
  • 105
1

Most likely the size of any other pointer. Try something like this to find out for your compiler and machine:

#include <iostream>
struct base {
    base() {}
    virtual ~base() {}
};
int main( int argc, char **argv ) {
    std::cout << sizeof( base ) << std::endl;
}
Boojum
  • 6,592
  • 1
  • 30
  • 34
1

The pointers in the virtual function table are generally the same size as regular pointers in the system. Typically a virtual function table is calculated for every type, and each object instance will contain a pointer to its type's table, so instances of objects containing virtual functions will use sizeof(void *) bytes more per instance than ones that don't. Types deriving from multiple base types must be castable to any base type so may contain multiple pointers to the base types' virtual function tables as necessary. All of this is compiler dependent of course.

moonshadow
  • 86,889
  • 7
  • 82
  • 122