mutiju
mutiju

Reputation: 55

Initialisation of objects with/without vtable

Say I have a pool that allocates some buffer.

int size = 10;

T* buffer = (T*) new char[size * sizeof(T)];

If I now want to assign some data to the buffer, i do the following.

buffer[0] = data;

My question is now what is the difference in initialization of objects that have vtable and those that don't.

From what I can see, I can without a problem assign classes to this buffer, and as long as I don't call any virtual functions, function calls work just fine. e.g.

class A{
    void function(){}
};

A a;
buffer[0] = a;
a.function(); // works

But:

class B{
    void function(){}
    virtual void virtual_function(){}
};

B b;
buffer[0] = b;
b.function(); // does work
b.virtual_function() // does not work.

Why does non-virtual function work?

Is it because the function is statically declared due to it being a normal class function and therefore is being copied when we do the assignment?

But then it doesn't make sense that I need to call the constructor on the buffer I created in case I need to make sure the virtual function works as well. new (buffer[0]) T(); in order to call the constructor on the object created.

Both examples first create the appropriate size of the buffer then do a assignment, view this as a pool where I pre-allocate memory depending on the amount of objects I want to fit in the pool.

Maybe I just looked at this to long and confused my self :)

Upvotes: 3

Views: 1375

Answers (3)

Serge Ballesta
Serge Ballesta

Reputation: 149075

The problem is not specially with virtual functions but more generally with inheritance. As buffer is an array of A, when you write :

B b;
buffer[0] = b;

you first construct a B object (first line), and later construct an A object using its copy constructor initialized with b (second line).

So when you later call buffer[0].virtual_function() you actually apply the virtual function to an A object, not to aB one.

By the way, a direct call to b.virtual_function() should still correctly call the B version since it is applied to a real B object :

B b;
buffer[0] = b;
b.virtual_function(); // calls B version

If you do not need to take a copy of the object, you could use an array of pointers.

Upvotes: 0

WhozCraig
WhozCraig

Reputation: 66234

Your non-virtual functions "work" (a relative term) because they need no vtable lookup. Under the hood is implementation-dependent, but consider what is needed to execute a non-virtual member.

You need a function pointer, and a this. The latter is obvious, but where does the fn-ptr come from? its just a plain function call (expecting a this, then any supplied arguments). There is no polymorphic potential here. No vtable lookup required means the compiler can (and often does) simply take the address of what we think is an object, push it, push any supplied args, and invoke the member function as a plain-old-call. The compiler knows which function to call, and needs no vtable-intermediary.

It is not uncommon for this to cause headaches when invoking non-static, non-virtual member function on illicit pointers. If the function is virtual, you'll generally (if you're fortunate) blow up on the call. If the function is non-virtual, you'll generally (if you're fortunate) blow up somewhere in the body of the function as it tries to access member data that isn't there (including a vtable-directed execution if your non-virtual calls a virtual).

To demonstrate this, consider this (obviously UB) example. Try it.

#include <iostream>

class NullClass
{
public:
    void call_me()
    {
        std::cout << static_cast<void*>(this) << '\n';
        std::cout << "How did I get *here* ???" << '\n';
    }
};

int main()
{
    NullClass *noObject = NULL;
    noObject->call_me();
}

Output (OSX 10.10.1 x64, clang 3.5)

0x0
How did I get *here* ???

The bottom line is no vtable is bound to the object when you allocate raw memory and assign a pointer via a cast as you are. If you want to do this, you need to construct the object via placement-new. And in so doing, do not forget you must also destroy the object (which has nothing to do with the memory it occupies, as you're managing that separately) by calling its destructor manually.

Finally, the assignment you're invoking does not copy the vtable. Frankly there is no reason to. The vtable of a properly constructed object is already properly built, and referenced by the vtable pointer for a given object instance. Said-pointer does not participate in object copying, which has its own set of mandated requirements from the language standard.

Upvotes: 2

Gennady Proskurin
Gennady Proskurin

Reputation: 168

new char[...]

This does not construct object T (does not calls constructor). Virtual table is created during construction.

Upvotes: 1

Related Questions