2010-09-19 12 views
12

Ci sono state alcune domande su questo problema in precedenza; la mia comprensione è che chiamando std::vector::erase invaliderà solo gli iteratori che si trovano in una posizione dopo l'elemento cancellato. Tuttavia, dopo aver cancellato un elemento, l'iteratore in quella posizione è ancora valido (a patto, ovviamente, che non punti a end() dopo la cancellazione)?std :: invalidazione iteratore vettoriale

La mia comprensione di come un vettore sarebbe implementato sembra suggerire che l'iteratore sia utilizzabile in modo definitivo, ma non sono del tutto sicuro che possa portare a un comportamento indefinito.

Come esempio di ciò di cui sto parlando, il seguente codice rimuove tutti gli interi dispari da un vettore. Questo codice causa un comportamento indefinito?

typedef std::vector<int> vectype; 
vectype vec; 

for (int i = 0; i < 100; ++i) vec.push_back(i); 

vectype::iterator it = vec.begin(); 
while (it != vec.end()) { 
    if (*it % 2 == 1) vec.erase(it); 
    else ++it; 
} 

Il codice funziona correttamente sulla mia macchina, ma questo non mi convince che sia valido.

risposta

31

dopo la cancellazione di un elemento, è l'iteratore in quella posizione ancora valido

No; tutti gli iteratori a/dopo l'iteratore/i passati a erase vengono invalidati.

Tuttavia, erase restituisce un nuovo iteratore che punta all'elemento immediatamente dopo l'elemento (s) che sono stati cancellati (o alla fine se non esiste tale elemento). È possibile utilizzare questo iteratore per riprendere l'iterazione.


Si noti che questo particolare metodo di rimozione di elementi dispari è del tutto inefficiente: ogni volta che si rimuovere un elemento, tutti gli elementi dopo che deve essere spostato di una posizione verso nel vettore sinistra (questo è O (n)). È possibile eseguire questa operazione in modo molto più efficiente utilizzando erase-remove idiom (O (n)). È possibile creare un is_odd predicato:

bool is_odd(int x) { return (x % 2) == 1; } 

allora questo può essere passato a remove_if:

vec.erase(std::remove_if(vec.begin(), vec.end(), is_odd), vec.end()); 
+1

Perché si passa 'x' per riferimento const anziché da valore? – fredoverflow

+0

@Fred: nessun motivo particolare; Grazie per la segnalazione. –

+0

@James Ma allora come funziona sopra il codice fornito in questione funziona poiché la cancellazione annullerà gli iteratori? – Kapil

0

Oppure:

class CIsOdd 
{ 
public: 
    bool operator()(const int& x) { return (x % 2) == 1; } 
}; 

vec.erase(std::remove_if(vec.begin(), vec.end(), CIsOdd()), vec.end()); 
+1

Perché si passa 'x' per riferimento const anziché per valore? – fredoverflow

+2

Domanda: perché le persone spesso preferiscono l'approccio qui (funtore con solo operatore() sovraccarico, cioè nessuno stato/dati) rispetto a una semplice funzione indipendente come nella risposta di James McNellis sopra? Capisco che funzioneranno entrambi, ma di solito prendo lo stesso approccio di James, a me sembra più semplice e più ovvio. A un certo punto, comincio a chiedermi "cosa mi sfugge?" Se è solo una preferenza personale, va bene. – Dan

+0

@FredOverflow, x può essere una strucure, @Dan, ... e operator() un membro di quella struttura – ssianky