代码之家  ›  专栏  ›  技术社区  ›  sharvey

在std::map中存储结构实例

  •  5
  • sharvey  · 技术社区  · 16 年前

    我正在尝试将一些结构映射到其他一些实例,如:

    template <typename T>
    class Component {
    public:
    
        typedef std::map<EntityID, T> instances_map;
    
        instances_map instances;
    
        Component() {};
    
        T add(EntityID id) {
            T* t = new T();
            instances[id] = *t;
            return *t;
        };  
    };
    

    然后我这样使用它:

    struct UnitInfos {
        int owner_id;
        int health;
        float x, y;
    };
    
    class LogicComponent : public Component<UnitInfos> {};
    

    问题是,当它稍后检索数据时,如下所示:

    comp.instance[id];
    

    我得到一个breand新对象,其属性在默认值下初始化。

    这段代码本身是否有问题,或者我是否遗漏了有关问题的信息?


    根据@aaa建议,我将代码更改为

    typedef std::map<EntityID, T> instances_map;
    instances_map instances;
    T& add(EntityID id) {
        instances[id] = T();
        return instances[id];
    };
    

    但当我进入的时候

    UnitInfos &info = logic_c.instances[id];
    

    info.x的值仍然是0。有什么指针吗?


    问题是我如何将对LogicComponent的引用存储在另一个类中。使用 LogicComponent logic_c; 而不是 LogicComponent& logic_c; . 它现在可以工作了,但我在地图中存储指针(而不是@aaa的建议)。这是个坏主意吗?

    3 回复  |  直到 16 年前
        1
  •  3
  •   carlsborg    16 年前

    澄清要对LogicComponent执行的操作。假设你试图达到这样的目标:

    步骤1:向地图添加新条目:

    LogicComponent comp; 
    EntityID id = 99;
    UnitInfos info = comp.add(id);
    

    步骤2:初始化信息:

    info.x = 10.0;
    info.y = 11.0
    // etc
    

    第3步:再次获取信息对象:

    UnitInfos info2 = comp.instances[id]; // this is uninitialized.
    

    然后,按顺序排列一些代码注释:

    comp.add返回的info对象是您添加到地图中的对象的副本。通过修改它,您不会修改地图中的内容。

    最简单的解决方法是创建指向对象的指针映射,而不是对象本身。

    typedef std::map<EntityID, T*> pinstances_map;
    
    T * add(EntityID id) {
        T* t = new T();
        instances[id] = t;
        return t;
    };  
    
    // initialize as 
    UnitInfo *info = comp.add(id);
    info->x = 10.0;
    info->y = 11.0;
    
    // retrieve as 
    UnitInfos *info = comp.instances[id];
    

    另外,一定要使用访问器方法来获取映射值,而不是公开映射对象。使实例变量受保护,并添加一个public get()方法。

    编辑:此代码对我很好:

    #include <map>
    #include <iostream>
    using namespace std;
    
    template<typename T>
    class Component
    {
    public:
            typedef map<long, T*> pinstances_map;
            pinstances_map instances;
    
            T * add(long id)
            {
                    T *t = new T();
                    instances[id] = t;
                    return t;
            }
    };
    
    struct UnitInfo 
    {
            float x, y;
    };
    
    class LogicComponent: public Component<UnitInfo> {};
    
    int main()
    {
            LogicComponent comp;
            UnitInfo *info = comp.add(99);
            info->x = 10.0;
            info->y = 11.0;
    
            UnitInfo *info2 = comp.instances[99];
            cout << info2->x << " " << info2->y;
    
            return 0;
    }
    
        2
  •  4
  •   Anycorn    16 年前

    可能是

    T add(EntityID id) {
        T* t = new T();
        instances[id] = *t;
        return *t;  // return value and map instance are not the same anymore
    };  
    

    应该是

    T& add(EntityID id) {
        instances[id] = T();
        return instances[id];
    };  
    
        3
  •  2
  •   Nikolai Fetissov    16 年前

    听起来您将索引运算符定义为:

    template <typename T>
    T& Component::operator[]( EntityID id )
    {
        return instances[id];
    }
    

    或者类似的。

    可能的意外效果是,它将自动插入的默认构造实例 T 在地图上,然后把它还给我 对于不存在的条目 . 这是在 std::map 所以自然赋值语法就像 instances[10] = t; 作品。

    关键是 恒常性 . 完全按照上述定义,除了 按值返回 和一个 const 属性:

    template <typename T>
    T Component::operator[]( EntityID id ) const
    {
        return instances[id];
    }
    

    这样,当您尝试使用不存在的密钥检索时,您将得到一个异常。更好,只是 typedef 它就像下面这样,并且可以用它来完成:

    typedef std::map<EntityID,UnitInfos> EntityUnitMap;
    

    其他人已经提到,您不需要动态地分配一个对象—您无论如何都要在容器中存储一个副本—并且在这样做时会泄漏内存。