今天我在处理项目时偶然发现了这一点。 基本上,在我的项目中,我有类似于下面的资源处理。
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。
由于
答案 0 :(得分: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次释放。如果您尝试相同的测试用例会发生什么?
答案 1 :(得分:0)
您忘记实现[深层]复制构造函数和关联的赋值运算符。
创建Resource
实例的副本时会发生什么?第一个副本最终被破坏,然后第二个副本最终被破坏......每个副本的破坏导致相同指针的delete
离子。不好。
简而言之,您忘记实施三条规则。在你的C ++书籍词汇表中查阅,因为它肯定会在那里。