传递多态unique_ptr作为参数时内存泄漏

时间:2013-01-27 05:45:03

标签: c++ memory-leaks c++11 polymorphism

编辑:对于阅读此问题以供将来使用的任何人:错误无论如何都与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_ptr r值引用传递给以下函数:

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()时已经构造了运算符。

1 个答案:

答案 0 :(得分:5)

您没有向我们展示代码的关键部分 - Node的声明,特别是其析构函数,NodeSpace的部分,特别是它如何删除Node s等等。但是从您的评论&#34; Node和Operator都没有任何析构函数和复制/移动构造函数/赋值运算符&#34;,似乎这就是问题所在。

如果您通过OperatorNode *保留unique_ptr<Node>的所有权,即通过Node *删除任何衍生对象,Node < strong>必须拥有虚拟析构函数。如果你没有声明并定义一个,那就没有了。

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