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:
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:
Ptr* AddCompoenent<>()
instead of void AddComponent<>(Ptr*&)
nullptr
when the object they point to is destroyedIs this at all possible?
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.