1

编辑:对于任何阅读此问题以备将来使用的人:该错误与 unique_ptr 无关。正如 JoergB 在他的回答中公正地说的那样,这只是我忘记基类的虚拟析构函数的错误。


在偶尔的运行时崩溃之后,我认为我的代码正遭受严重的内存泄漏炎。我用 Valgrind 运行我的程序,医生似乎同意:字节肯定会丢失。不过,我终其一生都无法弄清楚哪里出了问题。

我设法将泄漏归结为以下三行:

std::unique_ptr<Operator> pointer(new Operator{"left", "right"});
NodeSpace space; // The node space takes ownership over the operator

// When I comment out the following line, Valgrind reports nothing:
space.setNode("key", move(pointer));

在第一行unique_pointer创建了一个类的实例Operator。内部Operator看起来像这样:

class Operator : public Node {
public:
    Operator(std::initializer_list<std::string> input_keys) {
        input_nodes_.reserve(input_keys.size());
        for_each(begin(input_keys), end(input_keys), [this](const string& key) {
            input_nodes_[key] = nullptr;
        });
    }

    // ...

private:
    std::unordered_map<std::string, Node*> input_nodes_;
};

我将unique_ptrr 值引用传递给以下函数:

void NodeSpace::setNode(const std::string& key, std::unique_ptr<Node> node);

因为节点空间接管了传入节点的所有权,所以它采用unique_ptr按值(移动语义)。在内部它将指针存储在一个 中std::map,但即使函数体被注释掉,内存泄漏仍然会发生(这让我相信问题是node函数调用的参数)。

有人远程知道问题出在哪里吗?

旁注:我没有使用shared_ptr,因为节点可以以循环方式相互引用。weak_ptr可能是一种选择,但是通过系统的构建,当其拥有的节点空间不再存在时,节点就不可能存在。

Valgrind 输出:

==83791== 112 (16 direct, 96 indirect) bytes in 1 blocks are definitely lost in loss record 606 of 794
==83791==    at 0x100060ABD: malloc (vg_replace_malloc.c:274)
==83791==    by 0x1000C9147: operator new(unsigned long) (in /usr/lib/libc++.1.dylib)
==83791==    by 0x10000CB0F: std::__1::__hash_table<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*>, std::__1::__unordered_map_hasher<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::__unordered_map_equal<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*> > >::__rehash(unsigned long) (in ./test/mimi)
==83791==    by 0x10000C684: std::__1::__hash_table<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*>, std::__1::__unordered_map_hasher<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::__unordered_map_equal<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, true>, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, mimi::Node*> > >::rehash(unsigned long) (in ./test/mimi)
==83791==    by 0x100005D5A: mimi::Operator::Operator(std::initializer_list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >) (in ./test/mimi)
==83791==    by 0x100005984: mimi::Operator::Operator(std::initializer_list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >) (in ./test/mimi)
==83791==    by 0x10002E940: main (in ./test/mimi)

我也不明白为什么 Valgrind 似乎告诉我泄漏发生在Operator构造函数中,即使在调用时操作符已经被构造NodeSpace::setNode()

4

1 回答 1

5

您没有向我们展示代码的关键部分 - 的声明Node,特别是它的析构函数,的部分NodeSpace,特别是它如何删除Nodes 等。但是从您的评论“节点和运算符都没有任何析构函数和复制/移动构造函数/assignment-operator”,看来这就是问题所在。

Operator如果您通过 aNode *或 a维护 a 的所有权unique_ptr<Node>,即如果您通过 a 删除任何派生对象Node *,则Node 必须具有虚拟析构函数。如果你不声明和定义一个,它不会。

虽然结果行为未定义,但结果通常是派生类成员的析构函数不被调用。这与您的错误消息相匹配。

于 2013-01-27T14:15:10.760 回答