Я обнаружил это случайно сегодня, когда работал над своим проектом.
По сути, в моем проекте у меня есть что-то похожее ниже для обработки ресурсов.
class Resource {
public:
static Resource instance_;
~Resource () {
std::for_each(res_.begin(), res_.end(), [&] (Stuff *stuff) {
delete stuff;
});
}
private:
std::set<Stuff*> res_;
};
При повторном запуске valgrind при выходе из программы я вижу некоторые действительно загадочные ошибки.
Что-то вроде этого:
by 0x40DD42: std::_Rb_tree<unsigned int, std::pair<unsigned int const, std::pair<Vertex*, unsigned int> >,
std::_Select1st<std::pair<unsigned int const, std::pair<Vertex*, unsigned int> > >, std::less<unsigned int>,
std::allocator<std::pair<unsigned int const, std::pair<Vertex*, unsigned int> > > >::equal_range(unsigned int const&)
\\ a lot, lot more to come...
Читая все это, кажется, указывает на то, что деструктор Resource
освобождает уже освобожденную область памяти.
Но мой деструктор определенно справляется с ситуацией правильно. Чтобы доказать это, я переместил код удаления из деструктора в другую функцию-член.
Итак, как то так:
class Resource {
public:
static Resource instance_;
~Resource () { /* does nothing */ }
void clear () {
std::for_each(res_.begin(), res_.end(), [&] (Stuff *stuff) {
delete stuff;
});
}
// ... more
};
Затем я просто вызываю clear () для статического экземпляра до выхода из программы.
Теперь ошибка больше не появляется в valgrind!
Чтобы доказать, что это связано только с тем, когда статический экземпляр умирает, когда умирает программа, я удалил статический экземпляр.
Вместо статического instance_ я просто выделил экземпляр Resource
в стеке main()
когда моя программа запускается.
После этого изменения проблема также уходит.
Теперь мне интересно, почему это происходит?
Это как-то связано с операционной системой?
Я предполагаю, что ОС, вероятно, пытается освободить все, когда программа умирает, и мой деструктор случайно включается во время очистки, а не раньше.
Здесь речь идет об ОС Linux (Ubuntu 12.10).
Компилятор gcc 4.7.2.
Спасибо
Я использую Xubuntu 12.10, моя версия gcc соответствует вашей:
$ gcc --version
gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
С помощью этого теста:
#include <set>
#include <algorithm>
class Thing {};
class Test {
public:
static Test test;
std::set<Thing*> things;
~Test() {
std::for_each(things.begin(), things.end(),
[&](Thing* thing){
delete thing;
});
}
};
Test Test::test;
int main() {
Test::test.things.insert(new Thing());
Test::test.things.insert(new Thing());
return 0;
}
А также valgrind
сообщает все хорошо, 4 выделения, 4 освобождения. Что произойдет, если вы попробуете тот же тест?
Вы забыли реализовать [глубокий] конструктор копирования и связанный с ним оператор присваивания.
Что происходит, когда вы создаете копию экземпляра Resource
? Первая копия в конечном итоге уничтожается, а затем вторая копия в конечном итоге уничтожается … и уничтожение каждой копии приводит к delete
Ион тех же указателей. Нехорошо.
Короче говоря, вы забыли реализовать правило трех. Посмотрите это в глоссарии вашей книги на C ++, так как он обязательно будет там.