首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >创建模板类型而不需要新/删除

创建模板类型而不需要新/删除
EN

Stack Overflow用户
提问于 2020-06-09 21:25:15
回答 3查看 119关注 0票数 0

我有一个像这样的C++对象类:

代码语言:javascript
复制
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中的class Object是与我的问题无关的另一个函数的deleted。AFAIK执行大量的new/delete调用(堆分配)会损害性能,应该有20/30 (甚至更多)的Objects,每个Objects都有3-10 Object::add。我想我只需要调用T-s构造函数而不需要new,然后调用static_cast<Component*>(&value),但是在映射中添加的组件是“无效”的,这意味着所有T的成员(例如)。在具有某些int成员的类中,它们都等于0,而不是传递给构造函数的一些自定义值)。我知道value超出了作用域,map上的指针变成了一个悬空的指针,但我无法找到一种方法来实例化T对象,而不调用new或将它们声明为static。有办法这样做吗?

编辑:如果我声明valuestatic,那么一切都按预期工作,所以我想这是一个与value相关的终身问题。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-06-09 21:33:21

我想,您认为这是创建对象的另一种方式。

代码语言:javascript
复制
T value{std::forward<Args>(args)...};
components[typeid(T)] = static_cast<Component*>(&value);

这将在堆栈上创建一个局部变量。然后,在执行分配时,在map中存储指向局部变量的指针。

当您离开方法add()时,本地对象将被销毁,并且在映射中有一个悬空指针。这反过来又会咬你一口。

只要您想要存储指针,就没有办法绕过new和delete。您可以使用某种内存池来稍微减轻这一点。

如果还可以在映射中存储对象而不是指针,则可以使用std::map::emplace创建适当的组件。执行此操作时,还必须删除对delete的调用,并以其他方式清理对象。

票数 1
EN

Stack Overflow用户

发布于 2020-06-10 09:20:30

在我看来,在证明堆分配确实损害了程序的性能之前,试图避免堆分配并不是一种好方法。如果是这样的话,您可能也应该在代码中去掉std::map。尽管如此,如果您确实希望在那里没有new/delete调用,则可以这样做,但需要对Component类型进行显式枚举。像这样的东西可能就是你要找的东西:

代码语言:javascript
复制
#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

票数 1
EN

Stack Overflow用户

发布于 2020-06-10 13:05:37

其他的答案说明了问题是什么,我想提出一个建议,你怎样才能把这个问题彻底解决。

在编译时,您知道在mosz的映射中有哪些可能的类型,因为您知道在哪里使用add模板的实例化。因此,您可以摆脱映射,并在编译时完成所有操作。

代码语言:javascript
复制
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添加以下重载,以使错误更易于阅读

代码语言:javascript
复制
template<component T>
void add(...) {
    static_assert(false, "Please add T to the componentlist of this object");
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62292126

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档