1
\$\begingroup\$

I want to create a custom allocator that stores objects to the heap sequentially. The allocator itself is very simple. It just allocates a big chunk of memory and moves an offset as Allocate() calls happen.

The problem comes when allocating memory for custom types, especially classes with virtual functions.

SomeClassWithVirtualFunctions * address = array_allocator->Allocate(sizeof(SomeClassWithVirtualFunctions));
SomeClassWithVirtualFunctions->Init(...);

It throws a seg fault when the virtual function is called, since as i am given to understand, stuff that are initialized through the constructor (vtable for example) are now ignored.

However, if i do the following,

SomeClassWithVirtualFunctions * the_new_object = new SomeClassWithVirtualFunctions();
char * memory = array_allocator->Allocate(sizeof(*the_new_object));
memcpy(memory, the_new_object, sizeof(*the_new_object));
SomeClassWithVirtualFunctions->Init();

then the code works fine. I believe though that the above is a dangerous work-around trick.

The intend is to have a base class with virtual functions like Step() for example, that game classes like Player, Grass, Wall override to have unique behavior.

What is the best way to implement this? Do i use the custom allocator to store only primitive class members stuff, and classes are allocated normally?

Will overriding the new operator for SomeClassWithVirtualFunctions work?

void * operator new(size_t size) {
    char * address = array_allocator->Allocate(size);

    return address;
}
\$\endgroup\$
  • 2
    \$\begingroup\$ This one seems to me to fall foul of the "would a game developer give me a better answer?" test - the answer to that being "no". What you want is called placement new - see e.g. stackoverflow.com/questions/222557/… \$\endgroup\$ – Maximus Minimus Jan 16 '18 at 13:51
3
\$\begingroup\$

Take a look at std::allocator_traits that will allow you to use your allocator and construct objects in allocated space.

If you don't want to use that you can use placement new: new(the_new_object) SomeClassWithVirtualFunctions(...) keep in mind that you will need to use the_new_object->~SomeClassWithVirtualFunctions() to deconstruct it before you can return it to the allocator for reuse.

You can also pass other parameters to the new overload

void * operator new(size_t size, array_allocator_t array_allocator) {
    char * address = array_allocator->Allocate(size);

    return address;
}

call with new(array_allocator) SomeClassWithVirtualFunctions(...)

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.