2

我今天在做我的项目时偶然发现了这一点。基本上,在我的项目中,我有类似于下面的资源处理。

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 中!

为了进一步证明这仅与程序死亡时静态实例死亡有关,我删除了静态实例。在我的程序启动时,我只是Resource在堆栈上分配了一个实例,而不是静态实例。main()在此更改之后,问题也消失了。

现在,我想知道为什么会这样?这和操作系统有关系吗?

我的猜测是,操作系统可能会在程序终止时尝试释放所有内容,而我的析构函数恰好在清理过程中启动,而不是之前。

这里有问题的操作系统是 Linux (Ubuntu 12.10)。编译器是 gcc 4.7.2。

谢谢

4

2 回答 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 个释放。如果你尝试相同的测试用例会发生什么?

于 2012-11-01T01:27:22.550 回答
0

您忘记实现 [deep] 复制构造函数和关联的赋值运算符。

当您创建 的实例的副本时会发生什么Resource?第一个副本最终被破坏,然后第二个副本最终被破坏......每个副本的破坏都会导致delete相同指针的离子。不好。

简而言之,您忘记执行三的规则。在您的 C++ 书籍的词汇表中查找它,因为它肯定会在那里。

于 2012-11-01T01:32:58.687 回答