Std :: list :: remove metodo call destroyer di ogni elemento rimosso?

std::list lst; //.... Node * node = /* get from somewhere pointer on my node */; lst.remove(node); 

Std :: list :: remove metodo call destroyer (e memoria libera) di ogni elemento rimosso? Se sì, come posso evitarlo?

Sì, rimuovere un Foo* da un contenitore distrugge il Foo* , ma non rilascia il Foo . Distruggere un puntatore raw è sempre un no-op. Non può essere diversamente! Permettetemi di darvi diversi motivi per cui.

Classe di archiviazione

Cancellare un puntatore ha senso solo se il pointee è stato effettivamente assegnato dynamicmente, ma come potrebbe il runtime sapere se questo è il caso in cui la variabile puntatore viene distrutta? I puntatori possono anche puntare a variabili statiche e automatiche e l’eliminazione di uno di questi produce un comportamento non definito .

 { Foo x; Foo* p = &x; Foo* q = new Foo; // Has *q been allocated dynamically? // (The answer is YES, but the runtime doesn't know that.) // Has *p been allocated dynamically? // (The answer is NO, but the runtime doesn't know that.) } 

Puntatori penzolanti

Non c’è modo di capire se il pointee sia già stato rilasciato in passato. Eliminando lo stesso puntatore due volte si ottiene un comportamento indefinito . (Diventa un puntatore pendente dopo la prima eliminazione.)

 { Foo* p = new Foo; Foo* q = p; // Has *q already been released? // (The answer is NO, but the runtime doesn't know that.) // (...suppose that pointees WOULD be automatically released...) // Has *p already been released? // (The answer WOULD now be YES, but the runtime doesn't know that.) } 

Puntatori non inizializzati

È anche imansible rilevare se una variabile puntatore è stata inizializzata del tutto. Indovina cosa succede quando provi ad eliminare un puntatore del genere? Ancora una volta, la risposta è un comportamento indefinito .

  { Foo* p; // Has p been properly initialized? // (The answer is NO, but the runtime doesn't know that.) } 

Matrici dinamiche

Il sistema di tipi non distingue tra un puntatore a un singolo object ( Foo* ) e un puntatore al primo elemento di una matrice di oggetti (anche Foo* ). Quando una variabile puntatore viene distrutta, il runtime non è in grado di capire se rilasciare il pointee tramite delete o via delete[] . Il rilascio tramite la forma errata richiama un comportamento indefinito .

 { Foo* p = new Foo; Foo* q = new Foo[100]; // What should I do, delete q or delete[] q? // (The answer is delete[] q, but the runtime doesn't know that.) // What should I do, delete p or delete[] p? // (The answer is delete p, but the runtime doesn't know that.) } 

Sommario

Dato che il runtime non può fare nulla di sensato con il punto d’impatto, distruggere una variabile puntatore è sempre un no-op. Non fare nulla è decisamente meglio che causare un comportamento indefinito a causa di un’ipotesi non informata 🙂

Consigli

Invece di puntatori grezzi, considera l’utilizzo di puntatori intelligenti come il tipo di valore del tuo contenitore, poiché si assumono la responsabilità di rilasciare il pointee quando non è più necessario. A seconda delle esigenze, utilizzare std::shared_ptr o std::unique_ptr . Se il tuo compilatore non supporta ancora C ++ 0x, usa boost::shared_ptr .

Mai , ripeto, non utilizzo MAI mai std::auto_ptr come tipo di valore di un contenitore.

Chiama il distruttore di ciascuno degli elementi list , ma non è un object Node . È un Node* .

Quindi non cancella i puntatori del Node .

Ha senso?

Chiama il distruttore dei dati nella lista. Ciò significa che std::list::remove chiamerà il distruttore di T (che è necessario quando T è qualcosa come std::vector ).

Nel tuo caso, chiamerebbe il distruttore di Node* , che è un no-op. Non chiama il distruttore del node .

Sì, anche se in questo caso, il nodo * non ha distruttore. Tuttavia, a seconda dei suoi interni, i vari valori del nodo * vengono eliminati o distrutti dalle regole di scoping. Se Nodo * è un tipo non fondamentale, verrà chiamato un distruttore.

Il distruttore è chiamato sul nodo? No, ma ‘Nodo’ non è il tipo di elemento nella lista.

Per quanto riguarda la tua altra domanda, non puoi. Il contenitore dell’elenco standard (di fatto TUTTI i contenitori standard) adotta la proprietà del loro contenuto e lo ripulisce. Se non vuoi che ciò accada, i contenitori standard non sono una buona scelta.

Dato che stai mettendo i puntatori in una std::list , i distruttori non vengono chiamati sugli oggetti Node puntati.

Se si desidera memorizzare gli oggetti allocati nell’heap nei contenitori STL e averli distrutti dopo la rimozione, avvolgerli in un puntatore intelligente come boost::shared_ptr

Il modo migliore per capire è testare ogni modulo e osservare i risultati. Per usare abilmente gli oggetti contenitore con i propri oggetti personalizzati è necessario avere una buona comprensione del comportamento.

In breve, per il tipo Node* non viene chiamato il decostruttore né viene invocato delete / free; tuttavia, per il tipo Node il decostruttore verrà richiamato mentre l’esame di delete / free è un dettaglio di implementazione dell’elenco. Significato, dipende se l’implementazione della lista ha usato new / malloc.

Nel caso di un unique_ptr , il decostruttore viene invocato e la chiamata di delete / free avverrà poiché è necessario assegnargli qualcosa che è stato assegnato da new .

 #include  #include  #include  using namespace std; void* operator new(size_t size) { cout < < "new operator with size " << size << endl; return malloc(size); } void operator delete(void *ptr) { cout << "delete operator for " << ptr << endl; free(ptr); } class Apple { public: int id; Apple() : id(0) { cout << "apple " << this << ":" << this->id < < " constructed" << endl; } Apple(int id) : id(id) { cout << "apple " << this << ":" << this->id < < " constructed" << endl; } ~Apple() { cout << "apple " << this << ":" << this->id < < " deconstructed" << endl; } bool operator==(const Apple &right) { return this->id == right.id; } static void* operator new(size_t size) { cout < < "new was called for Apple" << endl; return malloc(size); } static void operator delete(void *ptr) { cout << "delete was called for Apple" << endl; free(ptr); } /* The compiler generates one of these and simply assignments member variable. Think memcpy. It can be disabled by uncommenting the below requiring the usage of std::move or one can be implemented. */ //Apple& operator=(const Apple &from) = delete; }; int main() { list a = list(); /* deconstructor not called */ /* memory not released using delete */ cout < < "test 1" << endl; a.push_back(new Apple()); a.pop_back(); /* deconstructor not called */ /* memory not released using delete */ cout << "test 2" << endl; Apple *b = new Apple(); a.push_back(b); a.remove(b); cout << "list size is now " << a.size() << endl; list c = list(); cout < < "test 3" << endl; c.push_back(Apple(1)); /* deconstructed after copy by value (memcpy like) */ c.push_back(Apple(2)); /* deconstructed after copy by value (memcpy like) */ /* the list implementation will call new... but not call constructor when Apple(2) is pushed; however, delete will be called; since it was copied by value in the last push_back call double deconstructor on object with same data */ c.pop_back(); Apple z(10); /* will remove nothing */ c.remove(z); cout << "test 4" << endl; /* Apple(5) will never deconstruct. It was literally overwritten by Apple(1). */ /* Think memcpy... but not exactly. */ z = Apple(1); /* will remove by matching using the operator== of Apple or default operator== */ c.remove(z); cout << "test 5" << endl; list> d = list>(); d.push_back(unique_ptr(new Apple())); d.pop_back(); /* z deconstructs */ return 0; } 

Presta molta attenzione agli indirizzi di memoria. Puoi dire quali puntano nello stack e quali puntano nell’heap in base agli intervalli.