2010-12-29 13 views
13

Ho una mappa come questaÈ sicuro ottenere un oggetto in std :: map per riferimento?

map<int,object> objmap; 
object& obj = objmap.find(num)->second; 
object& obj2 = objmap[num]; 

Qualunque modifiche che fare nell'oggetto deve essere riflesso sulla mappa. La cosa simile non può essere fatta in un vettore poiché cambia posizione degli oggetti quando vuole più spazio. È sicuro farlo in una std :: map? ed è consigliabile? La seconda versione restituisce un errore in quanto il mio oggetto non ha un costruttore vuoto. Se dichiari un costruttore vuoto che non fa nulla, le due linee funzioneranno allo stesso modo?

risposta

18

Fintanto che l'oggetto in questione non viene rimosso dalla mappa, allora sì è sicuro. Una volta inseriti in una mappa, gli oggetti non si spostano anche se vengono aggiunti o rimossi altri elementi.

object& obj = objmap.find(num)->second; 

Questo è potenzialmente pericoloso se non si è sicuri che un elemento con chiave num esiste realmente nella mappa. Se non si è sicuri, è possibile utilizzare il sovraccarico di insert che restituisce un iterator e un bool che indica se è stato inserito un nuovo elemento o se un elemento con la chiave specificata era già presente nella mappa.

E.g.

object& obj = objmap.insert(std::make_pair(num, object(arg1, arg2, argN))).first->second; 
+1

Sai dove e come trovare tali informazioni nei documenti? In realtà non sapevo nemmeno come cercare questa risposta: qual è il modo corretto per dire "gli oggetti non si muovono"? Immagino che le specifiche debbano essere definite esattamente perché è una differenza così importante tra un vettore e una mappa. – Flynsee

11

Questo è sicuro finché l'elemento non viene rimosso dalla mappa.

Tuttavia, la seconda linea non è abbastanza sicuro:

object& obj = objmap.find(num)->second; 

Se non ci sono elementi con chiave num nella mappa, find tornerà objmap.end(). Questa possibilità dovrebbe essere testato prima dereferencing l'iteratore restituito:

const std::map<int, object>::iterator it = objmap.find(num); 
if (it != objmap.end()) 
{ 
    object& obj = it->second; 
    /* ... */ 
} 

Ora, se l'obiettivo non è realmente trovare ma in realtà per inserire, chiamando operator[] è una possibilità (anche se, come già notato, richiede il valore per fornire un costruttore senza parametri). Ma dovete capire che queste sono due cose molto diverse:

  • findsolo accerti: se la chiave non viene trovato, nulla viene inserito e l'iteratore end viene restituito
  • operator[]sempre rendimenti un riferimento a un valore nella mappa: era la chiave assente, si verifica un inserimento (per un valore predefinito costruito: quindi il fabbisogno costruttore)
1

Se si desidera apportare modifiche a l'oggetto nella mappa deve essere riflesso, è probabile che tu voglia cambiare la tua mappa per memorizzare i puntatori agli oggetti invece degli oggetti stessi. I riferimenti possono funzionare fintanto che non si fa nulla all'oggetto tra il riferimento che lo invalida.

Ad esempio, il seguente codice sarebbe rompere utilizzando i riferimenti:

object& obj = objmap.find(num)->second; 
objmap.erase(objmap.find(num)); // should check for objmap.end() - left out for simplicity 
obj.DoSomething(); // this object has been destroyed, so the reference is invalid 
4

Se la tua domanda è se il std::map invalida suoi iteratori nelle sue funzioni mutare allora la risposta è negativa. Le garanzie standard std::map non invalidano i suoi iteratori.

0

Fare ciò che si sta facendo è un modo comune per implementare il caching.

Se l'articolo è già presente, l'operatore [] restituisce l'articolo. Se non c'è, creerà un "blank" con il costruttore predefinito e potrai scriverlo per un uso successivo.

Ovviamente si usa comunemente shared_ptr come valore_type che avrà un valore "vuoto" quando viene creato. In questo caso è necessario ottenere shared_ptr come riferimento in modo da poter richiamare reset() su di esso.

Come in qualsiasi raccolta/caching, ecc. È necessario prestare attenzione ai problemi relativi alla sicurezza dei thread se vengono eseguiti in un'applicazione multi-thread.

Quello che non sono sicuro di voler sapere è se è possibile memorizzare il riferimento da qualche parte (o un puntatore ad esso) e aspettarsi che sia valido in seguito quando altri elementi vengono aggiunti alla mappa, e sì, sarà .

Problemi correlati