2
\$\begingroup\$

I'm currently building my first 2D game engine and have reached a point where some large-scale tests have shown that some form of custom memory management would be beneficial (object pools).

So I created an object pool but got stuck on the problem of calling the constructor and destructor of the objects allocated from that pool.

So I read this: http://www.scs.stanford.edu/~dm/home/papers/c++-new.html

This seems to be a common problem when using custom allocators and there seems to be no nice solution to it within C++. I've also read on some other place that one should avoid "new" like the plague when creating a game engine. So, if that's the case, should I avoid constructors and destructors altogether? Should I fall back to a Init/Shutdown mechanism for my objects?

Appreciate any advice.

EDIT: Besides the correct answer below, one solution to the problem (also mentioned by mikosz) that also allows the use of non-default constructors can be found here: http://anki3d.org/cpp-allocators-for-games/

\$\endgroup\$
3
\$\begingroup\$

New and delete are only an issue when used excessively (10,000+ times per frame).

This usually means an engine architectural redesign is in order. Allocation pools rarely fix this.

But if you must:

You can use the placement new to call the constructor on an allocated pool object and then call the destructor explicitly when the object gets released back into the pool.

Create a pool:

class MyPool {
public:

  ...

protected:
  char *pool;
  size_t pool_size;

  // some way to keep track of free indices here
}

MyPool::MyPool() {
  pool_size = 100;
  pool = new char[sizeof(MyClass) * pool_size];
}

Construct an object and return it:

MyClass *MyPool::MyAlloc() {
  size_t index = FigureOutWhichIndexIsAvailableAndMarkAsUsed();

  // call constructor using placement new
  new( ((MyClass *)pool) + index ) MyClass();

  // return pointer to constructed object
  return ((MyClass *)pool) + index;
}

Use object in pool...

Destroy object:

void MyPool::MyFree(MyClass *pool_object) {

  //figure out index of object
  size_t index = pool_object - (MyClass *)pool;

  assert(index < pool_size); // check for error (pointer outside of pool range)

  // call destructor on object explicitly
  pool_object->~MyClass();

  MarkIndexAsFreeSoItCanBeReusedLater(index);
}

Then delete entire pool storage when your container gets deleted:

MyPool::~MyPool(){
  assert(EveryObjectIsFree());

  delete[] pool;
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Placement-new returns the pointer you gave it, so you can return that directly. Also, the real implementation should take care of alignment. \$\endgroup\$ – Quentin Jul 20 '17 at 7:26
  • \$\begingroup\$ I'm aware of placement new. That's what I currently have in my pool. Problem is, how do you deal wit non-default constructors? The link I posted tries to deal with it. But as the author recognizes, it's not a nice solution. \$\endgroup\$ – Björn M.P. Jul 20 '17 at 9:02
  • 1
    \$\begingroup\$ If I understand your problem correctly, you could make MyAlloc a variadic template and perfectly-forward its args to the constructor. \$\endgroup\$ – mikosz Jul 20 '17 at 12:09
  • \$\begingroup\$ @Quentin Wrong, you do not need to take care of alignment as per C++ standard (3.7.3.1/2): The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function). If it isn't it's a bug in the C++ library implementation. \$\endgroup\$ – Stephane Hockenhull Jul 20 '17 at 15:48
  • \$\begingroup\$ A placement new returns its argument. If you give it a misaligned pointer, you get back the same misaligned pointer and trigger UB. See this SO Q&A. \$\endgroup\$ – Quentin Jul 20 '17 at 16:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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