Утечка памяти при использовании std::string при использовании std::list<std::string>
Я работаю с 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;
}
}
Почему мой список строк вызывает утечку памяти? Разве удаление списка не должно вызывать деструкторы для каждой содержащейся строки?
5 ответов
В первом случае list
класс понятия не имеет, что вы выделили строку new
и не может удалить его. В частности, список содержит только копию строки, которую вы передали.
Аналогично, во втором случае вы никогда не освобождаете линейный объект s
и, таким образом, вы теряете память. Причина, по которой внутренняя строка удаляется, заключается в том, что вы неправильно реализовали конструктор копирования. Таким образом, если вы делаете копию Line
объект, оба они будут ссылаться на один и тот же строковый указатель, и если вы попытаетесь удалить их обоих, у вас возникнут проблемы.
Вашему классу Line нужен экземпляр-ctor и оператор присваивания, которые правильно работают с указателем строки.
В качестве альтернативы, просто иметь std::string
член, а не указатель и пусть string
класс обрабатывает память (вот для чего он).
Вот ваша утечка:
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 хранят элементы по значению, выделяя и освобождая для них место. То, что вы выделили, вы должны опубликовать явно. Просто добавь delete s
до конца цикла.
Если вам нужно хранить указатели, рассмотрите возможность хранения управляемых указателей, таких как boost::shared_ptr
или посмотрите в библиотеку контейнера указателя Boost.
На второй взгляд, вам не нужно выделять Line
в кучу вообще. Просто измените его на:
sl->push_back(Line());
И, как отмечали другие, убедитесь, что Line
Члены указателя должным образом управляются в конструкторе копирования, назначении копирования и деструкторе.
Другие обращались конкретно к тому, почему у вас утечка - удаление списка указателей не удаляет объекты, на которые указывают, и не должно быть простым указателем, который не указывает, была ли это единственная ссылка на этот объект), но есть и другие способов, чем убедиться, что вы итерируете список при удалении, чтобы удалить указатели.
Поскольку приведенный здесь пример показывает, что нет никакой причины вообще использовать указатели на что-либо, поскольку вы используете их, когда они входят в область действия, и отбрасывают их, когда они выходят из области действия, - вместо этого просто создайте все в стеке, и компилятор будет работать правильно. распоряжаться всем при выходе из прицелов. Например.
while (1==1) {
std::list<std::string> sl;
std::string s = std::string("XXXXXXXXXXXXXXXXXXXXXXX");
sl.push_back(s);
}
Если вам нужно поведение указателя (чтобы избежать дублирования объектов, с которыми связаны многие вещи и т. Д. И т. Д.), Вам следует взглянуть на интеллектуальные указатели, поскольку они устранят многие подводные камни, поскольку они могут автоматически обрабатывать ссылку подсчет и семантика вам нужны. (В частности, посмотрите на умные указатели повышения)
Существует много типов интеллектуальных указателей, которые вы можете использовать в зависимости от конкретной потребности и семантики владения для представления.
Std::auto_ptr имеет строгое право собственности - если указатель "скопирован", оригинал обнуляется и право собственности передается - всегда будет только один действительный auto_ptr для объекта. Указанный объект удаляется всякий раз, когда умный указатель с владельцем выходит из области видимости.
Theres также усиливают общие указатели и слабые указатели, используя подсчет ссылок, чтобы знать, когда освобождать объект, на который указывают. При использовании общих указателей каждая копия указателя увеличивает количество ссылок, а объект, на который указывает указатель, удаляется всякий раз, когда все общие указатели выходят за пределы области видимости. Слабый указатель указывает на объект, управляемый общим указателем, но не увеличивает счетчик ссылок, если все родительские общие указатели будут удалены, при попытке разыменования слабый указатель вызовет легко перехватываемое исключение.
Очевидно, что диапазон интеллектуальных указателей намного больше, но я настоятельно рекомендую взглянуть на них как на решение, которое поможет вам управлять памятью.
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;
Вы забыли удалить s
, Вы новенький, вы должны удалить его. Поскольку вы копируете объекты (помещая их в список) при управлении памятью в своем классе Line, вы также должны предоставить конструктор копирования и оператор присваивания для вашего класса Line.