2010-07-31 9 views
5

Estoy intentando trazar algunas estructuras a algunos otros casos, como este:almacenamiento de instancias struct en un std :: mapa

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; 
    }; 
}; 

Entonces lo uso como esto:

struct UnitInfos { 
    int owner_id; 
    int health; 
    float x, y; 
}; 

class LogicComponent : public Component<UnitInfos> {}; 

El problema es que cuando más tarde recuperar datos más adelante, así:

comp.instance[id]; 

consigo un nuevo objeto Breand con propiedades inicializadas a valores por defecto.

¿Hay algo intrínsecamente incorrecto con este fragmento de código, o estoy omitiendo información sobre el problema?


según la sugerencia @aaa, puedo cambiar el código para

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

pero cuando puedo acceder a él

UnitInfos &info = logic_c.instances[id]; 

el valor de info.x sigue siendo 0. Cualquier punteros?


El problema era cómo Almacené la referencia a LogicComponent en otra clase. utilizando LogicComponent logic_c; en lugar de LogicComponent& logic_c;. Ahora funciona, pero estoy almacenando punteros en el mapa (en lugar de la sugerencia de @aaa). ¿Es una mala idea?

+1

Hay _es_ algo inherentemente malo con este pedazo de código: no crear el 't' dinámicamente. No hay ninguna razón para hacerlo, y tal como está escrito ahora, filtra ese objeto. En cuanto al problema que está viendo, tendrá que publicar la implementación del operador menor para 'EntityID' y cómo está llamando' operator [] 'en el mapa (es decir, ¿cómo se crea el ID que pasa a' operador [] '?). –

+0

EntityID simplemente se define como 'typedef unsigned long EntityID;'. – sharvey

Respuesta

3

Aclare las operaciones que desea realizar en LogicComponent. Suponiendo que usted está tratando de lograr algo como esto:

Paso 1: Agregar una nueva entrada en el mapa:

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

Paso 2: Iniciar el Info:

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

Paso 3: Obtener la información objeto nuevo:

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

Luego, unos comentarios de código están en orden:

El objeto de información devuelto por comp.add es una COPIA del objeto que ha agregado al mapa. Al modificarlo, no está modificando lo que está en el mapa.

La solución más simple es crear un mapa de punteros al objeto en lugar del objeto en sí.

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]; 

Además, utilice un método de acceso para obtener el valor asignado, en lugar de exponer el objeto del mapa como público. Haga que las instancias estén protegidas y agregue un método get público().

Editar: Este código funciona bien para mí:

#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; 
} 
+0

Lo que describiste es exactamente lo que trato de lograr. Cambiar a lo que ha descrito es algo que intenté, pero cuando llamo a info-> x después de su última línea, obtengo un EXC_BAD_ACCESS, con información apuntando a 0x0. – sharvey

+0

Estoy de acuerdo, este código funciona.Cuando intento acceder desde otra parte del código (la función de renderización pasa a 'glutDisplayFunc' y' glutIdleFunct', los punteros aún apuntan a 0x0. – sharvey

+0

¿Está accediendo al mismo objeto LogicComp? – carlsborg

4

podría ser que

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

debería ser

T& add(EntityID id) { 
    instances[id] = T(); 
    return instances[id]; 
}; 
+0

Intenté cambiar el código, pero todavía me da el mismo problema. ¿Debería recuperar la instancia de esta manera: 'UnitInfos info = logic_c.instances [id];'? – sharvey

+0

@sharvey no, 'UnitInfos info' es una instancia nueva,' UnitInfos & info 'es una referencia a la instancia existente. si vienes de Java, es posible que tengas que leer esto: http://www.parashift.com/c++faq-lite/references.html – Anycorn

+0

Vengo de un fondo java-ish. Lo extraño es que cuando accedo justo después de cambiarlo, el valor es correcto; pero cuando lo vuelvo a obtener más tarde, el valor de x sigue siendo 0. Actualizaré la pregunta. Gracias. – sharvey

2

Suena como que ha definido el operador de indexación como:

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

O algo por el estilo.

El posible efecto inesperado de esto es que insertará automáticamente la instancia construida por defecto de T en el mapa y luego lo devolverá para las entradas no existentes. Esto se hace en std::map por lo que funciona la sintaxis de asignación natural como instances[10] = t;.

El punto clave aquí es constness. Definir exactamente como antes, excepto por el valor de regresar y con un atributo const:

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

De esta manera obtendrá una excepción cuando intenta recuperar por la clave no existe.Mejor aún, tal como se typedef abajo y acabar de una vez:

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

Otros ya mencionado de que no es necesario asignar dinámicamente un objeto - se almacena una copia en el contenedor de todos modos - y que usted pérdida de memoria cuando haces eso

+0

Gracias, ahora entiendo por qué el C++ faq-lite dice que probablemente no necesite el puntero la mayor parte del tiempo en C++. No pensé que también se aplicaría a algo como esto. Estoy accediendo a la instancia a través del subcampo instancias, por lo tanto, no se llama al operador [] en la plantilla Componente. Podría estar equivocado, por supuesto. Implementaré absolutamente el operador [] como sugirió, porque es mucho más agradable. Gracias. – sharvey

Cuestiones relacionadas