创建模板类型而不新建/删除
Creating template types without new/delete
我有一个像这样的C++ Object类:
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);
}
}
};
添加到class Object
Component
sdelete
与我的问题无关的另一个函数上。AFAIK 执行大量new
/delete
调用(堆分配(会损害性能,据说应该有 20/30(甚至更多(Objects
,每个调用有 3-10 个Object::add
。我以为我可以在没有new
的情况下调用T
-s 构造函数,然后调用static_cast<Component*>(&value)
,但是映射上添加的组件是"无效的",这意味着所有 T 的成员(例如,在具有一些int
成员的类上,它们都等于0
而不是在其构造函数上传递的一些自定义值(。我知道value
超出了范围,map
上的指针变成了悬空的指针,但我找不到一种方法来实例化T
对象而不调用new
或不将它们声明为static
.有什么办法可以做到这一点吗?
编辑:如果我value
声明为static
,一切都按预期工作,所以我想这是一个与value
相关的终身问题。
我想,你认为这是创建对象的另一种方式
T value{std::forward<Args>(args)...};
components[typeid(T)] = static_cast<Component*>(&value);
这会在堆栈上创建一个局部变量。然后,执行赋值,将指向局部变量的指针存储在map
中。
当您离开方法add()
时,本地对象将被销毁,并且您在映射中有一个悬空指针。反过来,这最终会咬你。
只要你想存储指针,就没有办法绕过新建和删除。您可以使用某种内存池来缓解这种情况。
如果还可以在映射中存储对象而不是指针,则可以使用std::map::emplace
就地创建组件。执行此操作时,还必须删除对delete
的调用,并以其他方式清理对象。
在我看来,在证明堆分配确实会损害程序性能之前,试图避免堆分配不是一个好方法。如果是这种情况,您可能也应该摆脱代码中的std::map
。话虽如此,如果您真的想在那里没有new
/delete
调用,则可以这样做,但需要显式枚举Component
类型。像这样的东西可能是你正在寻找的:
#include <array>
#include <variant>
// Note that components no longer have to implement any specific interface, which might actually be useful.
struct Component1 {};
struct Component2 {};
// Component now is a variant enumerating all known component types.
using Component = std::variant<std::monostate, Component1, Component2>;
struct Object {
// Now there is no need for std::map, as we can use variant size
// and indexes to create and access a std::array, which avoids more
// dynamic allocations.
std::array<Component, std::variant_size_v<Component> - 1> components;
bool add (Component component) {
// components elements hold std::monostate by default, and holding std::monostate
// is indicated by returning index() == 0.
if (component.index() > 0 && components[component.index() - 1].index() == 0) {
components[component.index() - 1] = std::move(component);
return true;
}
return false;
}
};
Component
枚举所有已知的组件类型,这允许避免Object
中的动态分配,但会增加内存使用量,因为用于单个Object
的内存大致number_of_component_types * size_of_largest_component
。
虽然其他答案清楚地说明了问题所在,但我想提出一个建议,如何完全解决这个问题。
您在编译时知道 mosz 的映射中可能有哪些类型,因为您知道使用了add
模板的哪个实例。因此,您可以摆脱地图并在编译时完成所有操作。
template<component... Comps>
struct object{
std::tuple<std::optional<Comps>...> components;
template<component comp, class ... args>
void add(Args... &&args) {
std::get<std::optional<comp>>(components).emplace(std::forward<Args>(args)...);
}
}
当然,这会迫使您在创建对象时收集所有可能的对象,但这不是更多信息,您必须拥有更多不切实际的信息。
您可以为add
添加以下重载,以使错误更易于阅读
template<component T>
void add(...) {
static_assert(false, "Please add T to the componentlist of this object");
}
- 创建模板类型而不新建/删除
- C++ 在请求特定字节的新建后删除
- 为什么非放置"新建"和"删除"内置于语言中,而不仅仅是常规函数?
- 来自C#的mingw DLL:为什么我必须覆盖新建/删除?
- C++ - 定义自定义新建和删除运算符时make_shared
- 使用安全零内存新建/删除时出现问题
- 奇怪的内存泄漏由C++中的新建/删除
- CRT 检测到应用程序在堆缓冲区(新建/删除)类结束后写入内存
- 无法覆盖C++中纯抽象类中的运算符删除/新建
- 混合运算符和表达式新建/删除
- 使用运算符重载(新建/删除)实现单例的优缺点
- 忽略全局覆盖的新建/删除
- 基本的新建/删除操作员日志记录
- C++自定义全局新建/删除覆盖系统库
- Realloc 和全球本地新建/删除运算符覆盖
- 使用 malloc/free 来模拟新建/删除
- 新建/删除运算符不匹配错误的严重程度
- 新建/删除导致访问冲突
- 新建/删除操作符重载和基类
- 新建/删除/免费存储和 malloc/免费/堆组合