PDA

View Full Version : [C++ - VS2008] Curioso problema con stl::vector


cerbert
11-06-2012, 12:59
Premetto che, all'urlo di "basta che funzioni", per quanto mi riguarda il problema che vi esporrò è risolto.

D'altro canto, dal momento che questo simpatico, inspiegabile, comportamento ha comportato la perdita di circa 16 ore uomo (due giornate di lavoro, sì) mi sento di sottoporvelo per pareri e, non si sa mai, esperienza.

Dunque all'interno di una libreria sviluppata internamente e utilizzata nelle tre applicazioni proprietarie dell'azienda per cui lavoro abbiamo questo codice:


std::vector<Property*>::iterator it = m_kPropertyStorage.begin();

while(it != m_kPropertyStorage.end())
{
delete *it;
it++;
}


Non penso ci sia bisogno di tantissimi dettagli:
- Property: classe di dati contenente delle CString
- m_kPropertyStorage: vettore di puntatori a Property allocati nell'heap

Questo codice funzionava perfettamente in debug in tutte le applicazioni ed in release in due applicazioni. Nella terza applicazione, se compilata in release, originava puntualmente inspiegabili crash.

Dopo 16 ore di smarronamento e ripasso dell'onomastica, essendo io un brutale zappatore che ha sempre avuto in odio gli iteratori (il codice originale è di un mio collega) decido, per capire meglio cosa stia succedendo, di riscrivere il codice così:


Property* dummyPtr = NULL;

for(int i=0; i<m_kPropertyStorage.size();i++)
{
dummyPtr = m_kPropertyStorage[i];

if(dummyPtr)
delete dummyPtr;

dummyPtr = NULL;
}


Ovvero nascondendo l'iteratore nell'iterazione ed utilizzando gli operatori ed i metodi di stl::vector che permettono di utilizzarlo come un comune array.

Il codice... FUNZIONA!

Ora, la sfida per i guru è: cosa cambia tra le due implementazioni?
Per che motivo, solo in release e solo in una su tre applicazioni che usano la stessa libreria nello stesso modo (ah, scrivendo e leggendo il vettore in un singolo thread, nel caso ci fosse dubbio) la versione a "aritmetica degli iteratori" (teoricamente più "elegante") dava crash?

Misteri della programmazione.

-MiStO-
11-06-2012, 16:00
il problema, se non vado errato, credo sia nel fatto che l'operazione di delete invalida l'iteratore in esame, nel tuo caso it.
quindi la successiva istruzione it++ ti crea dei problemi, in quanto incrementi un iteratore non valido

tu nel tuo fix hai evitato il problema maneggiando direttamente l'array, quindi evitando di avere iteratori non validi :D

nb: se non ricordo male, un operazione del tipo
delete *it++;
dovrebbe funzionare in quanto le operazioni fatte sono: incremento it, ritorno il temporaneo con il valore precedente, elimino l'oggetto puntato dal temporaneo mantenendo integro it, che punta gia' al successivo...
ma sarebbe da testare in quanto vado a memoria :stordita:

british
12-06-2012, 15:13
il problema, se non vado errato, credo sia nel fatto che l'operazione di delete invalida l'iteratore in esame, nel tuo caso it.
quindi la successiva istruzione it++ ti crea dei problemi, in quanto incrementi un iteratore non valido


Sei sicuro? mi risulta che siano le operazioni di erase a invalidare gli iteratori.
Cerbert applica la delete all'oggetto puntato dal puntatore contenuto nell'iteratore - ma non altera nè l'iteratore nè il suo contenuto, nè la struttura del contenitore STL.


ciao!

-MiStO-
12-06-2012, 17:28
Sei sicuro? mi risulta che siano le operazioni di erase a invalidare gli iteratori.
Cerbert applica la delete all'oggetto puntato dal puntatore contenuto nell'iteratore - ma non altera nè l'iteratore nè il suo contenuto, nè la struttura del contenitore STL.

ciao!
mmhh il dubbio mi sale in effetti...:stordita:

!fazz
12-06-2012, 18:13
bah secondo me è un problema dovuto alle ottimizzazioni del compilatore, cerbert se compili senza ottimizzazioni hai un funzionamento regolare?

cerbert
13-06-2012, 10:05
Sì, si tratta sicuramente di un problema dovuto all'ottimizzazione in quanto, come detto, in debug l'errore non si presenta.

Infatti una risposta alla mia domanda probabilmente la troverei andando a guardare come viene sviluppato l'assembler in un caso e nell'altro...

british
13-06-2012, 14:18
bah secondo me è un problema dovuto alle ottimizzazioni del compilatore, cerbert se compili senza ottimizzazioni hai un funzionamento regolare?

Quale sospetti che sia il meccanismo del problema?

ciao!

tomminno
14-06-2012, 08:46
Ma dato che funziona su 2 software su 3 e apparentemente non ci sono bug (è codice abbastanza usuale) non è che il compilatore è VS6? :) O eventualmente provato ad usare un Visual Studio più recente? (Parli di CString quindi penso sia un software MFC).
Il crash, salvo mostruosi problemi nell'implementazione della libreria standard sottostante, dovrebbe essere quasi esclusivamente dovuto ad un'accidentale double delete.
Sarebbe stato interessante provare a gestire le SEH e vedere di preciso dove sta l'errore