2011-04-30 14 views
8

Nel mio programma C++, creo oggetti in una funzione usando new. Questi oggetti sono inseriti in un set. Quando voglio rimuovere oggetti dal set, utilizzo un iteratore in un ciclo for. Quando rimuovo l'oggetto dall'insieme, devo ancora cancellare l'oggetto per liberare la sua memoria, corretto? Ho provato a usare delete, ma poi ho ricevuto un errore che diceva che il puntatore che veniva liberato non era allocato. Quindi, come può essere fatto?Come eliminare un oggetto in un set

Ecco il codice dove Creo l'oggetto e inserirla nella serie

set <myObject> myobjectlist; 
myObject *myobject = new myObject; 
myobjectlist.insert(*myobject); 

In un'altra funzione, provo a rimuovere un oggetto dal set, e senza la sua memoria:

for (set<myObject>::iterator i = myobjectlist.begin(); i != myobjectlist.end(); i++) 
if (i->myObjectID == myObjectID) 
{ 
    myobjectlist.erase(*i); 
    delete &i; 
    break; 
} 

Questo funziona perfettamente senza la parte 'cancella'. L'ho aggiunto perché pensavo che la memoria dell'oggetto non venisse liberata.

risposta

7

Supponendo che si stia chiamando il metodo , si noti che chiamerà il distruttore dell'oggetto per voi. Dopo averlo erase() il tuo oggetto, è già stato delete d, e quindi il tuo secondo tentativo di chiamare manualmente l'eliminazione avrà esito negativo poiché il puntatore non è più allocato.

Per riferimento, vedere this

+1

In base al suo frammento di codice questa affermazione è fuorviante. Il suo set memorizza l'oggetto in base al valore, quindi ciò che cancella sta distruggendo non è ciò che ha assegnato con nuovo. – hifier

+0

No, perché sta eliminando per riferimento questo non è il caso. Il suo set contiene una copia del valore dell'oggetto originale, che viene quindi trovato. Il valore viene cancellato (e distrutto) dall'insieme e quindi si tenta di eliminare l'iteratore, che non funzionerà, poiché non punta più a un blocco valido. –

+1

Il suo codice non tenta di eliminare l'iteratore, tenta di eliminare _address_ dell'iteratore (che è ancora un indirizzo valido, ma non uno che contiene un puntatore a qualcosa che è stato allocato con nuovo). Ma non è questo il punto, il codice perderà l'oggetto originale. – hifier

2

Sì, è necessario eliminare gli oggetti creati. Tuttavia, ciò che è nel tuo set non è necessariamente quello che hai assegnato. Ad esempio, forse il tuo insieme contiene valori oggetto (piuttosto che puntatori) e l'oggetto allocato è trapelato dopo l'inserimento. Codice postale

Modifica: È stato. Il tuo set non memorizza i puntatori, memorizza le copie degli oggetti che stai allocando. Rimuovere l'eliminazione dal ciclo cancellare e inserire l'oggetto come questo:

set <myObject> myobjectlist; 
myobjectlist.insert(myObject()); 

In alternativa, basta fare il set sia set<myObject*>.

Inoltre, cancella prende un iteratore - non c'è bisogno di derefarlo.

+0

Effettuare tali modifiche causa errori di compilazione nel ciclo. Come dovrebbe essere cambiato? –

+0

Se memorizza copie, allora forse è meglio non usarne di nuove quando si crea l'oggetto? –

+0

In entrambi i casi non puoi cancellare un elemento che hai 'cancella()' d. Il membro di cancellazione chiama il distruttore dell'oggetto e delete tenterà di fare la stessa cosa! –

2

Ecco ciò che si vuole, partendo dal presupposto che è necessario utilizzare nuove per allocare questi oggetti:

set <myObject*> myobjectlist;  
    myObject *myobject = new myObject; 
    myobjectlist.insert(myobject); //insert the pointer, not the object 

    for (set<myObject*>::iterator i = myobjectlist.begin(); i != myobjectlist.end(); i++) { 
    if ((*i)->myObjectID == myObjectID) { 
     myobjectlist.erase(i); 
     delete *i; 
     break; 
    } 
    } 
+0

@sean, la cancellazione non incrementa l'iteratore. Infatti, cancella prende questo parametro in base al valore, quindi non può avere alcun effetto sull'oggetto iteratore. – hifier

+0

Per chiarezza, la cancellazione della chiamata cambierà l'impostazione e quindi invaliderà l'iteratore per un ulteriore utilizzo all'interno del set. Tuttavia, l'iteratore rimane invariato e può ancora essere utilizzato per recuperare il puntatore all'oggetto appena cancellato. – hifier

+0

la mia confusione era basata sull'implementazione vs2010 di erase - set :: erase return void, ma è implementata tramite _Tree :: erase che restituisce un iteratore incrementato –

1

Se avete bisogno di una lista di puntatori, usare un elenco di puntatori intelligenti. Utilizzare un algoritmo standard per trovare l'elemento corretto e cancellarlo dall'elenco.

#include <set> 
#include <boost/shared_ptr.hpp> 
#include <boost/bind.hpp> 

using namespace boost; 

typedef boost::shared_ptr<MyObject> t_object; 
std::set<t_object> myObjectList; 
myObjectList.insert(t_object(new MyObject)); 

std::set<t_object>::iterator item = std::find_if(
    myObjectList.begin(), 
    myObjectList.end(), 
    bind(&MyObject::myObjectID, _1)== myObjectID); 
if(item!=myObjectList.end()) 
    myObjectList.erase(item); 
Problemi correlati