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

Sti*_*ert 1 c++ polymorphism memory-leaks c++11

编辑:读取此问题以供将来使用的任何人:错误与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));
Run Code Online (Sandbox Code Playgroud)

在第一行unique_pointer中创建a,持有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_;
};
Run Code Online (Sandbox Code Playgroud)

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

void NodeSpace::setNode(const std::string& key, std::unique_ptr<Node> node);
Run Code Online (Sandbox Code Playgroud)

因为节点空间接管传入节点的所有权,所以它需要一个unique_ptrby值(移动语义).在内部它将指针存储在一个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)
Run Code Online (Sandbox Code Playgroud)

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

Joe*_*rgB 5

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

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

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