Search code examples
c++pointersgame-engine

Managing pointers to destroyed objects in a game engine in an elegant way


I'm currently building a game engine, and have run into a bit of a problem. I have found a couple of solution, but it's quite as elegant as I would like them to be.

Here's the issue:

  1. Instantiate Object A in the engine
  2. Get a pointer to Object A - ObjectA*
  3. Destroy Object A
  4. ObjectA* becomes a dangling pointer

Using shared_ptr is a no-go since it can lock objects, which we don't want. There's nothing wrong with Object A being destroyed, I just need a way to check whether it has.

I have two solutions for this issue:

The first is simply to return a surrogate object whenever you want a reference to an object. This surrogate object could have an implicit conversion to a raw pointer, for cases where we know for certain the object is valid. In cases where we keep the reference around for longer, we would use the surrogate object, which is basically just a pointer to a pointer. When the object is destroyed we simply set its pointer to nullptr, which the surrogate would then be able to check for

The second solution is to not return pointers at all. Instead, whenever we want a reference to an object, we pass along the pointer we want it assigned to as a parameter. The engine will then keep said pointer in memory and manually set it to nullptr when the object is destroyed.

Here are the requirements for my preferred elegant solution:

  1. Only uses raw pointers for references
  2. Said raw pointers are returned from function calls (i.e. Ptr* AddCompoenent<>() instead of void AddComponent<>(Ptr*&)
  3. Pointers will become nullptr when the object they point to is destroyed

Is this at all possible?


Solution

  • If you want to return simple pointers, there is no way of collecting all pointers to the object when you deallocate it and setting them to nullptr if you just use normal C++ objects.

    There is of course a solution, it's not new, and it's called garbage collection. This is exactly the algorithm you'd need to do it; it analyzes the stack and heap and is able to collect all pointers to your object. Just instead of keeping the object if it finds pointers to it, you want it to set the pointers to nullptr.

    Now there are a couple of prerequisites to garbage collection. One is that you must be able to recognize all pointer values on the stack and heap at runtime, so that you can be sure that some data is actually a pointer to your object and not just some integer or other value that happens to hold a value that looks like a pointer to your object. If you still want to have simple pointer types in your code, you need support from your compiler to have that information at runtime – and C++ does not provide it. This is why garbage collection is typically defined for a whole language.

    Moreover, the performance implications of using this algorithm are horrendous: Every time you deallocate an object, you would need to analyze all stack and heap. This would mean it runs far more often than a normal gc-based language runs it, and look at their performance losses due to it.