Статический экземпляр класса неправильно обрабатывает удаление ресурса при выходе из программы

Я обнаружил это случайно сегодня, когда работал над своим проектом.
По сути, в моем проекте у меня есть что-то похожее ниже для обработки ресурсов.

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.

Спасибо

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 освобождения. Что произойдет, если вы попробуете тот же тест?

2

Другие решения

Вы забыли реализовать [глубокий] конструктор копирования и связанный с ним оператор присваивания.

Что происходит, когда вы создаете копию экземпляра Resource? Первая копия в конечном итоге уничтожается, а затем вторая копия в конечном итоге уничтожается … и уничтожение каждой копии приводит к deleteИон тех же указателей. Нехорошо.

Короче говоря, вы забыли реализовать правило трех. Посмотрите это в глоссарии вашей книги на C ++, так как он обязательно будет там.

0

По вопросам рекламы [email protected]