2010-06-06 15 views
6

Ho trovato un fatto molto pregiudizievole sulle mappe di stl. Per qualche motivo, non posso ottenere che gli oggetti vengano inseriti nella mappa per essere costruiti/distrutti una sola volta.come ottenere la mappa di stl per costruire/distruggere l'oggetto inserito solo una volta

Esempio:

struct MyObject{ 
    MyObject(){ 
     cout << "constructor" << endl; 
    } 
    ~MyObject(){ 
     cout << "destructor" << endl; 
    } 
}; 
int main() { 
    std::map<int, MyObject> myObjectsMap; 
    myObjectsMap[0] = MyObject(); 
    return 0; 
} 

rendimenti:

constructor 
destructor 
destructor 
constructor 
destructor 

se faccio:

typedef std::pair<int, MyObject> MyObjectPair; 
myObjectsMap.insert(MyObjectPair(0,MyObject())); 

restituisce:

constructor 
destructor 
destructor 
destructor 

Sto inserendo oggetti responsabili della propria allocazione di memoria, quindi quando vengono distrutti si puliscono da soli, essendo distrutti più volte mi stanno creando dei problemi.

+1

apparentemente, 'std :: map ' non offre un'interfaccia non di copia per la creazione di nuove voci. Consiglierei i puntatori condivisi qui - 'std :: map >' –

risposta

1

Questo è il modo in cui lo map e gli altri contenitori funzionano, non è possibile aggirarlo. Ecco perché, ad esempio, non è possibile utilizzare std::auto_ptr in una raccolta.

+0

Penso che passerò a un vettore e manterrò i puntatori nella mappa. –

6

Ti suggerisco di aggiungere un costruttore di copia - questo è quello che viene utilizzato per le costruzioni "mancanti", credo.

Codice:

#include <iostream> 
#include <map> 

using namespace std; 

struct MyObject{ 
    MyObject(){ 
     cout << "no-arg constructor" << endl; 
    } 
    MyObject(const MyObject&) { 
    cout << "const copy constructor" << endl; 
    } 
    ~MyObject(){ 
     cout << "destructor" << endl; 
    } 
}; 
int main() { 
    std::map<int, MyObject> myObjectsMap; 
    myObjectsMap[0] = MyObject(); 
    return 0; 
} 

uscita:

no-arg constructor 
const copy constructor 
const copy constructor 
destructor 
destructor 
no-arg constructor 
destructor 
destructor 
4

std::map è permesso di fare tutte le copie degli oggetti come vuole. Questa è l'implementazione definita e non hai alcun controllo su questo. A proposito, le costruzioni "mancanti" che si notano potrebbero essere chiamate il copy-constructor, che non hai definito.

Ciò che si può fare, tuttavia, è utilizzare un peso mosca, quindi la costruzione di un oggetto recupera infatti un oggetto esistente da un pool di oggetti preesistenti e la distruzione di un oggetto non fa nulla. Il pool non libera mai i suoi oggetti, ma mantiene sempre una maniglia su tutti loro. In questo modo, l'utilizzo della memoria è ampio dall'inizio alla fine, ma non cambia molto nel corso della vita del programma.

2

Per poter essere utilizzato in un contenitore standard gli oggetti devono essere copiabili e assegnabili. Se i tuoi oggetti non sono conformi a questo potresti avere dei problemi.

Detto questo, se (come il codice di esempio indica) basta un oggetto predefinito costruito inserito nella mappa è possibile utilizzare solo operatore [] per il suo effetto collaterale:

// Insert default constructed MyObject at key 0 
myObjectsMap[0]; 

Modifica

Non sono abbastanza chiaro dalla tua domanda, ma se non sei chiaro sul numero di oggetti costruiti e ritieni che vi sia una mancata corrispondenza tra costruttore e distruttore, nota che il compilatore fornirà un costruttore di copia che non registra su std::cout poiché non fornisci uno dichiarato dall'utente.

+0

Speravo di poter avere solo una costruzione e una distruzione, come se stessi usando un vettore. vector v; v.push_back (MyObject()); // produce solo una costruzione e distruzione. –

+0

Hai provato cosa 'myObjectsMap [0];' effettivamente? –

+0

Beh, non proprio, stavo davvero usando il metodo insert, ha meno overhead ma produce ancora più copie di quanto mi aspettassi. Ad ogni modo penso che rimarrò con il vettore e userò la mappa solo per i puntatori. Il post sopra era abbastanza chiaro di cosa sta succedendo. Buono a sapersi. –

2

Quando si dice myObjectsMap [0], si chiama il costruttore predefinito per MyObject. Questo perché non c'è ancora nulla in [0] e tu hai appena effettuato l'accesso. It's in the manual.

Quando si preme MyObject(); stai creando un'istanza MyObject temporanea utilizzando il costruttore predefinito.

Poiché è stato consentito al compilatore di definire il costruttore di copie, vengono visualizzati più distruttori dei messaggi del costruttore. (Ci può essere solo un distruttore, ma molti costruttori, a differenza della costruzione di una casa.) Se si obietta non dovrebbe mai essere copiato in questo modo, è probabile che si desideri dichiarare un costruttore di copia privato e un operatore di assegnazione copia.

si sta chiamando il costruttore di default e il costruttore di copia due volte ogni con questo codice:

myObjectsMap[0] = MyObject(); 

Quando si esegue questa operazione:

myObjectsMap.insert(MyObjectPair(0,MyObject())); 

si chiama il costruttore di default una volta e il copia costruttore 3 volte.

È consigliabile utilizzare i puntatori come valori di mappa anziché gli oggetti stessi, in particolare suggerisco di guardare shared_ptr.

note: tests were done using GCC 3.4.5 on a Windows NT 5.1 machine. 
Problemi correlati