c++ - 将多态 unique_ptr 作为参数传递时发生内存泄漏

标签 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 右值引用传递给以下函数:

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() 时构造好了 被调用。

最佳答案

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

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

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

关于c++ - 将多态 unique_ptr 作为参数传递时发生内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14544788/

相关文章:

C++11 构造函数和 operator= 的统一初始化

c++ - vmware中的线程编程, 'process scheduling'没有发生

c# - 旋转图像显示“内存不足”异常

c++ - 混合运行时/编译时条件下的标签调度

c - 内存泄漏与否 - 链表

android - 由于 Release模式下的内存问题导致应用程序崩溃

c++ - android NDK 提示 std::nearbyint 不是 std 的成员

c++ - 最小 "extern"使用测试用例的未定义​​引用问题

c++ - Qt C++ 隔离键盘

c++ - C++构造函数中char的默认值