When Is a Vtable Created in C++

When is VTable in C++ created?

A vtable isn't a C++ concept so if they are used and when they are created if they are used will depend on the implementation.

Typically, vtables are structures created at compile time (because they can be determined at compile time). When objects of a particular type are created at runtime they will have a vptr which will be initialized to point at a static vtable at construction time.

When is a vtable created in C++?

Beyond "vtables are implementation-specific" (which they are), if a vtable is used: there will be unique vtables for each of your classes. Even though B::f and C::f are not declared virtual, because there is a matching signature on a virtual method from a base class (A in your code), B::f and C::f are both implicitly virtual. Because each class has at least one unique virtual method (B::f overrides A::f for B instances and C::f similarly for C instances), you need three vtables.

You generally shouldn't worry about such details. What matters is whether you have virtual dispatch or not. You don't have to use virtual dispatch, by explicitly specifying which function to call, but this is generally only useful when implementing a virtual method (such as to call the base's method). Example:

struct B {
virtual void f() {}
virtual void g() {}
};

struct D : B {
virtual void f() { // would be implicitly virtual even if not declared virtual
B::f();
// do D-specific stuff
}
virtual void g() {}
};

int main() {
{
B b; b.g(); b.B::g(); // both call B::g
}
{
D d;
B& b = d;
b.g(); // calls D::g
b.B::g(); // calls B::g

b.D::g(); // not allowed
d.D::g(); // calls D::g

void (B::*p)() = &B::g;
(b.*p)(); // calls D::g
// calls through a function pointer always use virtual dispatch
// (if the pointed-to function is virtual)
}
return 0;
}

Some concrete rules that may help; but don't quote me on these, I've likely missed some edge cases:

  • If a class has virtual methods or virtual bases, even if inherited, then instances must have a vtable pointer.
  • If a class declares non-inherited virtual methods (such as when it doesn't have a base class), then it must have its own vtable.
  • If a class has a different set of overriding methods than its first base class, then it must have its own vtable, and cannot reuse the base's. (Destructors commonly require this.)
  • If a class has multiple base classes, with the second or later base having virtual methods:

    • If no earlier bases have virtual methods and the Empty Base Optimization was applied to all earlier bases, then treat this base as the first base class.
    • Otherwise, the class must have its own vtable.
  • If a class has any virtual base classes, it must have its own vtable.

Remember that a vtable is similar to a static data member of a class, and instances have only pointers to these.

Also see the comprehensive article C++: Under the Hood (March 1994) by Jan Gray. (Try Google if that link dies.)

Example of reusing a vtable:

struct B {
virtual void f();
};
struct D : B {
// does not override B::f
// does not have other virtuals of its own
void g(); // still might have its own non-virtuals
int n; // and data members
};

In particular, notice B's dtor isn't virtual (and this is likely a mistake in real code), but in this example, D instances will point to the same vtable as B instances.

Does every class have virtual function table in C++

The language specification of C++ does not define what a "vtable" is, or which classes need one.

A particular implementation of C++ in a compiler often uses a vtable to implement virtual methods. If a class has no virtual methods (and no superclasses with virtual methods), then the compiler may omit the vtable. However, keep in mind this is purely a compiler implementation decision, and not required by the standard.

When exactly does the virtual table pointer (in C++) gets set for an object?

This is strictly Implementation dependent.

For Most compilers,

The compiler initializes this->__vptr within each constructor's Member Initializer list.

The idea is to cause each object's v-pointer to point at its class's v-table, and the compiler generates the hidden code for this and adds it to the constructor code. Something like:

Base::Base(...arbitrary params...)
: __vptr(&Base::__vtable[0]) ← supplied by the compiler, hidden from the programmer
{

}

This C++ FAQ explains a gist of what exactly happens.

Implementing basic vtable in C

A basic vtable is nothing more than an ordinary struct containing function pointers, which can be shared between object instances. There are two basic ways one can implement them. One is to make the vtable pointer an ordinary struct member (this is how it works in C++ under the hood):

#include <stdio.h>
#include <stdlib.h>

typedef struct Person Person;
typedef struct Person_VTable Person_VTable;

struct Person {
int id;
char *name;
const Person_VTable *vtable;
};

struct Person_VTable {
void (*print)(Person *self);
};

void print_name(Person *person) {
printf("Hello %s\n", person->name);
}

static const Person_VTable vtable_Person = {
.print = print_name
};

Person *init_person(void) {
Person *person = malloc(sizeof(Person));
person->vtable = &vtable_Person;
return person;
}

int main(void) {
Person *p = init_person();
p->name = "Greg";
p->vtable->print(p);
return 0;
}

Another is to use fat pointers (this is how it’s implemented in Rust):

#include <stdio.h>
#include <stdlib.h>

typedef struct Person Person;
typedef struct Person_VTable Person_VTable;

typedef struct Person_Ptr {
Person *self;
const Person_VTable *vtable;
} Person_Ptr;

struct Person {
int id;
char *name;
const Person_VTable *vtable;
};

struct Person_VTable {
void (*print)(Person_Ptr self);
};

void print_name(Person_Ptr person) {
printf("Hello %s\n", person.self->name);
}

static const Person_VTable vtable_Person = {
.print = print_name
};

Person_Ptr init_person(void) {
Person_Ptr person;
person.self = malloc(sizeof(Person));
person.vtable = &vtable_Person;
return person;
}

int main(void) {
Person_Ptr p = init_person();
p.self->name = "Greg";
p.vtable->print(p);
return 0;
}

In C, the preferred way is the former, but that’s mostly for syntax reasons: passing structs between functions by value doesn’t have a widely-agreed-upon ABI, while passing two separate pointers is rather unwieldy syntactically. The other method is useful when attaching a vtable to an object whose memory layout is not under your control.

In essence, the only advantages of vtables over ordinary function pointer members is that they conserve memory (each instance of the struct only needs to carry one vtable pointer) and protect against memory corruption (the vtables themselves can reside in read-only memory).

What is a vtable in C++

V-tables (or virtual tables) are how most C++ implementations do polymorphism. For each concrete implementation of a class, there is a table of function pointers to all the virtual methods. A pointer to this table (called the virtual table) exists as a data member in all the objects. When one calls a virtual method, we lookup the object's v-table and call the appropriate derived class method.



Related Topics



Leave a reply



Submit