I have a C++ Object class like this:
class Component {};
template <typename T>
concept component = std::is_base_of_v<Component, T>;
class Object
{
std::map<std::type_index, Component*> components;
public:
template<component T>
T* add()
{
if(components.find(typeid(T)) == components.cend())
{
T* value{new T{}};
components[typeid(T)] = static_cast<Component*>(value);
}
}
template<component T, typename... Args>
T* add(Args &&... args)
{
if(components.find(typeid(T)) == components.cend())
{
T* value{new T{std::forward<Args>(args)...}};
components[typeid(T)] = static_cast<Component*>(value);
}
}
};
Component
s that are added to class Object
are delete
d on another function that is not related to my question. AFAIK doing a lot of new
/delete
calls (heap allocations) hurt performance and supposedly there should be like 20/30 (or even more) Objects
s with 3-10 Object::add
on each one. I thought that I could just call T
-s constructor without new
, then to static_cast<Component*>(&value)
, but the Component added on the map is "invalid", meaning all T's members (ex. on a class with some int
members, they are all equal to 0
instead of some custom value passed on its constructor). I am aware that value
goes out of scope and the pointer on the map
becomes a dangling one, but I can't find a way to instantiate T
objects without calling new
or without declaring them as static
. Is there any way to do this?
EDIT: If I declare value
as static
, everything works as expected, so I guess its a lifetime issue related to value
.
I suppose, you think of this as the alternative way of creating your objects
T value{std::forward<Args>(args)...};
components[typeid(T)] = static_cast<Component*>(&value);
This creates a local variable on the stack. Doing the assignment then, stores a pointer to a local variable in the map
.
When you leave method add()
, the local object will be destroyed, and you have a dangling pointer in the map. This, in turn, will bite you eventually.
As long as you want to store pointers, there's no way around new and delete. You can mitigate this a bit with some sort of memory pool.
If you may also store objects instead of pointers in the map, you could create the components in place with std::map::emplace
. When you do this, you must also remove the call to delete
and clean up the objects some other way.