2010-07-31 6 views
5

Sto cercando di mappare alcuni struct ad alcuni altri casi, come questo:casi struct memorizzazione in uno std :: map

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

Poi uso in questo modo:

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

class LogicComponent : public Component<UnitInfos> {}; 

Il problema è che quando in seguito recuperare i dati in seguito, in questo modo:

comp.instance[id]; 

ho un nuovo oggetto breand con proprietà inizializzate a valori di default.

C'è qualcosa di intrinsecamente sbagliato in questo pezzo di codice, o sto tralasciando informazioni sul problema?


Come da @aaa suggerimento, modificare il codice per

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

ma quando accedo che

UnitInfos &info = logic_c.instances[id]; 

il valore di info.x è ancora 0. Tutti gli indicatori?


Il problema era come ho memorizzato il riferimento a LogicComponent in un'altra classe. utilizzando LogicComponent logic_c; anziché LogicComponent& logic_c;. Ora funziona, ma sto memorizzando i puntatori nella mappa (invece del suggerimento di @ aaa). È una cattiva idea?

+1

Ci _is_ qualcosa di intrinsecamente sbagliato con questo pezzo di codice: non creare il 'T' dinamicamente. Non c'è motivo per farlo, e come è scritto ora, si perde quell'oggetto. Per quanto riguarda il problema che stai vedendo, dovrai pubblicare l'implementazione dell'operatore minore di per "EntityID" e come stai chiamando "operator []' sulla mappa (cioè come crei l'id che passi a " operatore [] '?). –

+0

EntityID è appena definito come 'typedef unsigned long EntityID;'. – sharvey

risposta

3

Chiarire le operazioni che si desidera eseguire su LogicComponent. Supponendo che si sta cercando di ottenere qualcosa di simile:

Passaggio 1: Aggiungere una nuova voce alla mappa:

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

Fase 2: Inizializzare informazioni:

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

Fase 3: Get le informazioni di nuovo oggetto:

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

Poi, un paio di commenti al codice sono in ordine:

L'oggetto informazioni restituito da comp.add è una COPIA dell'oggetto aggiunto alla mappa. Modificandolo, non stai modificando ciò che è nella mappa.

La soluzione più semplice consiste nel creare una mappa di puntatori dell'oggetto anziché dell'oggetto stesso.

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

Inoltre, utilizzare un metodo di accesso per ottenere il valore mappato, invece di esporre l'oggetto mappa come pubblico. Rendi la variabile delle istanze protetta e aggiungi un metodo pubblico get().

Edit: Questo codice funziona bene per me:

#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

Quello che hai descritto è esattamente ciò che sto cercando di ottenere. Passare a ciò che hai descritto è qualcosa che ho provato, ma quando chiami info-> x dopo l'ultima riga, ottengo un EXC_BAD_ACCESS, con informazioni che puntano a 0x0. – sharvey

+0

Sono d'accordo, questo codice funziona.Quando provo ad accedervi da un'altra parte del codice (la funzione render passata a 'glutDisplayFunc' e' glutIdleFunct', i puntatori puntano ancora a 0x0 – sharvey

+0

Stai accedendo allo stesso oggetto LogicComp? – carlsborg

4

potrebbe essere che

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

dovrebbe essere

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

Ho provato a cambiare il codice, ma mi dà ancora lo stesso problema. Dovrei recuperare l'istanza in questo modo: 'UnitInfos info = logic_c.instances [id];'? – sharvey

+0

@sharvey no, 'Informazioni UnitInfos' è una nuova istanza,' UnitInfos & info 'è un riferimento all'istanza esistente. se vieni da Java, potresti dover leggere questo: http://www.parashift.com/c++-faq-lite/references.html – Anycorn

+0

Vengo da uno sfondo java-ish. La cosa strana è che quando accedo subito dopo averlo cambiato, il valore è corretto; ma quando lo riavrò più avanti, il valore di x è ancora 0. Ill aggiorno la domanda. Grazie. – sharvey

2

Suona come si è definito l'operatore di indicizzazione come:

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

O qualcosa del genere.

Il probabile effetto inatteso di questo è che inserirà automaticamente istanza predefinita-costruito T nella mappa e poi restituirlo per le voci non exising. Questo è fatto in std::map quindi la sintassi di assegnazione naturale come instances[10] = t; funziona.

Il punto chiave qui è constness. Definire esattamente come sopra, tranne ritorno per valore e con un attributo const:

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

In questo modo si otterrà un'eccezione quando si tenta il recupero da parte di non-esistente chiave.Meglio ancora, proprio typedef piace soffietto e da fare con esso:

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

Altri già detto che non è necessario per allocare dinamicamente un oggetto - si memorizza una copia nel contenitore in ogni caso - e che perdita di memoria quando lo fai.

+0

Grazie, ora capisco perché il C++ faq-lite dice che probabilmente non hai bisogno del puntatore la maggior parte del tempo in C++. Non pensavo che si sarebbe applicato anche a qualcosa di simile. Sto accedendo all'istanza attraverso il sottocampo delle istanze, quindi non l'operatore [] viene chiamato sul modello Componente. Potrei sbagliarmi, ovviamente. Implementerò assolutamente l'operatore [] come suggerito, perché è molto più bello. Grazie. – sharvey