2010-05-20 9 views
5

Bueno, soy muy nuevo en Valgrind y en los perfiladores de pérdida de memoria en general. Y debo decir que da un poco de miedo cuando empiezas a usarlos porque no puedes dejar de preguntarte cuántas fugas podrías haber dejado sin resolver antes.¿Está loco Valgrind o se trata de una pérdida de memoria de iterador de mapa estándar genuino?

Hasta el punto, como no tengo experiencia en el programador de C++, me gustaría comprobar si esto es sin duda una pérdida de memoria o es que Valgrind está haciendo un falso positivo?

typedef std::vector<int> Vector; 
typedef std::vector<Vector> VectorVector; 
typedef std::map<std::string, Vector*> MapVector; 
typedef std::pair<std::string, Vector*> PairVector; 
typedef std::map<std::string, Vector*>::iterator IteratorVector; 

VectorVector vv; 
MapVector m1; 
MapVector m2; 

vv.push_back(Vector()); 
m1.insert(PairVector("one", &vv.back())); 

vv.push_back(Vector()); 
m2.insert(PairVector("two", &vv.back())); 

IteratorVector i = m1.find("one"); 
i->second->push_back(10); 
m2.insert(PairVector("one", i->second)); 

m2.clear(); 
m1.clear(); 
vv.clear(); 

¿Por qué es eso? ¿No debería el comando claro llamar al destructor de cada objeto y cada vector?

Ahora después de hacer algunas pruebas I conocer diferentes soluciones para la fuga:

1) Eliminando:

i->second->push_back(10); 

2) Adición de:

delete i->second; 

3) Eliminación de la segunda

vv.push_back(Vector()); 
m2.insert(PairVector("two", &vv.back())); 

Usando la solución 2) hace que Valgring imprima: 10 allocs, 11 libres ¿Está bien?

Como no estoy utilizando nuevos, ¿por qué debería eliminar?

¡Gracias por cualquier ayuda!

+0

No utilice las comillas de bloques para formatear el código, use el icono 101010 (o Ctrl + K). –

+0

Editado para formato de código. – Gorpik

+1

El uso de typedefs me ha dejado el código incomprensible. –

Respuesta

1

comportamiento Has indefinido aquí:

m1.insert(PairVector("one", &vv.back())); 

vv.push_back(Vector()); 

Insertar invalida iteradores y referencias que apuntan en el vector, lo que significa también el puntero se almacena en el mapa es básicamente apunta a un agujero negro después de la inserción.

hace Valgring imprimir: 10 allocs, 11 frees ¿Está bien?

Es extraño, ¿no imprime también algo sobre los dobleces libres?

Para la solución, sugeriría utilizar un contenedor diferente de vector (por ejemplo, list o deque, cuyas funciones mutantes invalidan los iteradores, pero no las referencias). O puede almacenar punteros (preferiblemente inteligentes, pero podrían ser ordinarios) a los datos en el vector para que la dirección de los datos reales sea estable.

+0

Dice algo acerca de una eliminación no válida aunque no sé cómo deshacerme de las llamadas de depuración, así que solo pude ver los errores. Voy a utilizar Valgrind a partir de ahora, así que espero estar un poco más cómodo con él en los próximos días ... –

+2

No es un comportamiento indefinido (todavía) en las líneas que citó. Es perfectamente legal tener un puntero no válido dando vueltas, * siempre y cuando no lo desreferencie *. Solo queda indefinido en la línea 'i-> second-> push_back (10)', donde el puntero del vector es realmente desreferenciado. – jalf

0

Estás haciendo cosas peligrosas con los vectores aquí. Está conservando punteros a vectores que pueden volverse inválidos durante la ejecución del programa.

std::vector<>::push_back() puede invalidar cualquier iterador o referencias al std::vector<> si ya estaba lleno. Dado que std::vector<> garantiza que su contenido se almacenará de forma contigua (para que pueda usarlo en lugar de una matriz), cuando necesite más memoria, deberá copiarse en un bloque de memoria diferente y el original no será válido.

Esto significa que todas las llamadas a push_back() en su código (a excepción del primero) conducen a un comportamiento indefinido, por lo que todo puede estar sucediendo aquí.

+0

@Gorpik, gracias por los consejos. Pero dime algo, la razón por la que tengo mapas y vectores es que con vectores garantizo que mis objetos están bien alineados en la memoria y que los mapas se usan para encontrar objetos con nombres específicos. Obliviosamente, esto no fue pensado para usar con enteros, sino con objetos de juego grandes ... ¿Tiene sentido para ti si tengo cuidado con los punteros estancados? –

+0

Qué minuto comprendo ahora, el vector puede mover la memoria de mis objetos a diferentes lugares al ejecutarla al crecer, y otras cosas, haciendo que mis mapas sean inútiles. Así que creo que estoy mejor usando una matriz simple para esto. –

+0

@Alberto Toglia: No realmente. Si sabes con certeza el tamaño del vector, puedes especificarlo en la construcción y nunca se moverá. Si no lo haces, bueno, la matriz irá por la borda en lugar de crecer y tendrás fandango en el núcleo. – Gorpik

2

Básicamente esta línea está causando el problema:

i->second->push_back(10); 

Esto se debe a i-> segunda pudo ser válida cuando lo hizo:

vv.push_back(Vector()); 

La segunda vez.

No es necesario llamar a clear. Cuando el objeto vv se sale del alcance, destruirá correctamente todos los objetos. Además, todos los mapas no poseen vectores, por lo que sus destructores no afectan a los vectores a los que apuntan. Por lo tanto, su uso de clear no es necesario.

Si desea mantener la misma solución general, cree una lista de vectores para su objeto vv. Luego, la inserción en la lista no afectará a los miembros ya existentes y sus mapas funcionarán correctamente.

std::list<Vector> vv; // insertion into this will not invalidate any other members. 
         // Thus any pointers to members you have will not become invalidated. 

Personalmente creo que estás complicando las cosas.
Creo que puede lograr los mismos resultados simplificando enormemente esto.
Si los vectores no son referenciados por múltiples elementos de mapa, simplemente coloque el vector en el mapa.

std::map<std::string, std::vector<int> > m1; 

m1["one"].push_back(10); 
m1["two"].push_back(20); 
Cuestiones relacionadas