views:

171

answers:

3

Basically, I have a pure virtual class Base, and a concrete class Derived which inherits from Base. I then allocate a piece of memory and treat it as an array of Derived via a simple cast. Then, I populate the array using =. Finally, I loop through the array, trying to call the virtual method GetIndex that is declared in Base and defined in Derived.

The problem is that I end up getting an access violation exception trying to read the pointer to the vtable for Base (in Visual Studio debugging, this is shown as __vfptr, and it is always 0xbaadf00d).

Following is a simple example of the problem I am encountering:

#include "stdafx.h"
#include "windows.h"

struct Base
{
    virtual int GetIndex() const = 0;
};

struct Derived : public Base
{
    int index;

    Derived()
    {
        static int test = 0;
        index = test++;
    }

    int GetIndex() const
    {
        return index;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    int count = 4;
    // Also fails with malloc
    Derived* pDerived = (Derived*)HeapAlloc(GetProcessHeap(), 0, sizeof(Derived) * count);

    for (int i = 0; i < count; i++)
    {
        Derived t;
        pDerived[i] = t;
    }

    // Should print 0 1 2 3
    for (int i = 0; i < count; i++)
    {
        Base& lc = pDerived[i];
        printf("%d\n", lc.GetIndex()); // FAIL!
    }
    return 0;
}

This behavior only occurs when allocating the memory via HeapAlloc or malloc; if new[] is used, it works fine. (Also, the cstor is called 4 times previously, so the output is 4 5 6 7.)

+9  A: 

If you allocate memory without new you always need to call the constructor manually with placement new and the destructor with x->~Derived();

KennyTM
A: 

I think inside the first for loop you are creating a object without new. Which means that the context of this object is your for loop. This variable no longer exists when you quit the for loop.

Andres
+1  A: 

If you want to use an allocator besides C++'s default, you should define your own operator new rather than try to remember to call the constructor every time.

void *operator new[]( size_t block_size, HANDLE heap ) {
    return HeapAlloc( heap, 0, block_size );
}

Derived *pDerived = new( GetProcessHeap() ) Derived[ count ];

Details depend on whether you want it to be the default way of allocating Derived and whether it really needs parameters.

You still need to be careful if free() can't free the memory you got. Then the default delete won't work, and you should either create Derived::operator delete or write your own function which calls object->~Derived().

Potatoswatter