2010-08-07 26 views
16

Estoy trabajando con std::list<std::string> en mi proyecto actual. Pero hay una pérdida de memoria en algún lugar relacionado con esto. Así que he probado el código problemático por separado:Fuga de memoria con std :: cadena al usar std :: list <std::string>

#include <iostream> 
#include <string> 
#include <list> 

class Line { 
public: 
    Line(); 
    ~Line(); 
    std::string* mString; 
}; 

Line::Line() { 
    mString = new std::string("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); 
} 

Line::~Line() { 
    //mString->clear(); // should not be neccessary 
    delete mString; 
} 

int main(int argc, char** argv) 
{ 
    // no memory leak 
    while (1==1) { 
     std::string *test = new std::string("XXXXXXXXXXXXXXXXXXXXXXXX"); 
     delete test; 
    } 

    // LEAK! 
    // This causes a memory overflow, because the string thats added 
    // to the list is not deleted when the list is deleted. 
    while (1==1) { 
     std::list<std::string> *sl = new std::list<std::string>; 
     std::string *s = new std::string("XXXXXXXXXXXXXXXXXXXXXXX"); 
     sl->push_back(*s); 
     //sl->pop_back(); //doesn't delete the string?- just the pointer 
     delete sl; 
    } 

    // LEAK! 
    // Here the string IS deleted, but the memory does still fill up 
    // but slower 
    while (1==1) { 
     std::list<Line> *sl = new std::list<Line>; 
     Line *s = new Line(); 
     sl->push_back(*s); 
     //sl->pop_back(); //does delete the Line-Element 
     sl->clear(); 
     delete sl; 
    } 
    return 0; 

    // this does not cause any noticable memory leak 
    while (1==1) { 
     std::list<int> *sl = new std::list<int>; 
     int i = 0xFFFF; 
     sl->push_back(i); 
     sl->clear(); 
     delete sl; 
    } 
    return 0; 

    // This does not cause any overflow or leak 
    while (1==1) { 
     int *i; 
     i= new int [9999]; 
     delete[] i; 
    } 

} 

¿Por qué mi lista de cadenas causa una pérdida de memoria? ¿No debería eliminarse la lista para llamar a los destructores en cada cadena contenida?

+338

Deja de usar 'new' tanto. No veo razón alguna por la que hayas usado algo nuevo en ningún lado. Puede crear objetos por valor en C++ y es una de las grandes ventajas de usar el lenguaje. No tiene que asignar todo en el montón. Deja de pensar como un programador de Java. – Omnifarious

Respuesta

26

En el primer caso, la clase list no tiene idea de que haya asignado la cadena con new, y no puede eliminarla. En particular, la lista solo contiene una copia de la cadena que pasó.

De forma similar, en el segundo caso, nunca libera el objeto de línea s, y por lo tanto, pierde memoria. La razón por la que se elimina la cadena interna es porque no ha implementado correctamente un constructor de copia. Por lo tanto, si realiza una copia de un objeto Line, ambos harán referencia al mismo puntero de cadena, y si intenta eliminarlos, se verá en problemas.

7

Aquí está su fuga:

while (1==1) { 
    std::list<Line> *sl = new std::list<Line>; 
    Line *s = new Line(); 
    sl->push_back(*s); 
    //sl->pop_back(); //does delete the Line-Element 
    sl->clear(); 
    delete sl; 
} 

STL almacenar elementos colecciones por valor, asignar y liberar espacio para ello. Lo asignado usted tiene que liberar explícitamente. Simplemente agregue delete s al final del ciclo.

Si usted tiene que almacenan punteros, considere almacenar punteros administrados como boost::shared_ptr, o mirar en Boost pointer container library.

En la segunda mirada, no necesita asignar Line en el montón. Sólo cambia a:

sl->push_back(Line()); 

Y, como otros han dicho, asegúrese de que los Line 's miembros puntero se gestionan adecuadamente en el constructor de copia, copia-asignación, y el destructor.

2
std::list<Line> *sl = new std::list<Line>; 
    Line *s = new Line(); 
    sl->push_back(*s); 
    //sl->pop_back(); //does delete the Line-Element 
    sl->clear(); 
    delete sl; 

Olvidó borrar s. Lo has actualizado, tienes que eliminarlo. Mientras está copiando objetos (al rellenarlos en una lista) mientras administra la memoria en su clase Line, también debe proporcionar un constructor de copias y un operador de asignación para su clase Line.

12

Su clase de línea necesita un copiador y un operador de asignación que maneje correctamente el puntero de cadena.

Como alternativa, solo tiene un miembro std::string en lugar de un puntero y deje que la clase string maneje la memoria (para eso es).

2

Otros han abordado específicamente por qué tiene su fuga: eliminar una lista de punteros no elimina los objetos apuntados, y no debería ser un simple puntero si no indica si era la única referencia a ese objeto), pero hay más formas que tener que asegurarse de repetir la lista en la eliminación para eliminar los punteros.


Por lo que este ejemplo muestra theres ninguna razón para utilizar punteros a nada en absoluto, ya que estás usando ellos cuando entran en el ámbito de aplicación y deshacerse de ellos cuando salen del ámbito - simplemente crear todo en el apilar en su lugar y el compilador eliminará todo correctamente al salir de los ámbitos. P.ej.

while (1==1) { 
    std::list<std::string> sl; 
    std::string s = std::string("XXXXXXXXXXXXXXXXXXXXXXX"); 
    sl.push_back(s); 
} 

Si necesita el comportamiento puntero (para no tener que duplicar objetos que están vinculados a por muchas cosas, etc, etc) que debe tomar un vistazo a punteros inteligentes, ya que estos se eliminarán muchos de las trampas, ya que pueden manejar automáticamente el recuento de referencias y la semántica que necesita. (Específicamente take a look at the boost smart pointers)

Hay muchos tipos de puntero inteligente que puede usar según la necesidad específica y la semántica de propiedad para representar.

std :: auto_ptr tiene propiedad estricta: si el puntero se "copia", el original se anula y se transfiere la propiedad, solo hay un auto_ptr válido para el objeto. El objeto señalado se elimina cada vez que el puntero inteligente con propiedad queda fuera del alcance.

También se potencian punteros compartidos y punteros débiles mediante el recuento de referencias para saber cuándo liberar el objeto al que se apunta. Con punteros compartidos, cada copia del puntero aumenta el recuento de referencias, y el objeto apuntado se elimina cuando todos los punteros compartidos quedan fuera del alcance. Un puntero débil apunta a un objeto administrado por un puntero compartido, pero no aumenta el recuento de referencia, si todos los punteros compartidos principales se eliminan intentando desreferenciar, un puntero débil lanzará una excepción fácilmente capturable.

Obviamente, hay mucho más en la gama de punteros inteligentes, pero recomiendo echarles un vistazo como una solución para ayudar con la administración de la memoria.

Cuestiones relacionadas