Valgrind 报告嵌套 shared_ptrs 的 =operator 读取错误

Posted

技术标签:

【中文标题】Valgrind 报告嵌套 shared_ptrs 的 =operator 读取错误【英文标题】:Valgrind report read error on =operator with nested shared_ptrs 【发布时间】:2019-12-18 21:24:20 【问题描述】:

我有这个简单的代码

#include<memory>

class SLLNode
        public:
                SLLNode(const int& d)
                        data_ = d;
                
                std::shared_ptr<SLLNode> next_;
                int data_;
;

int main()

        std::shared_ptr<SLLNode> head(new SLLNode(1));
        head->next_.reset(new SLLNode(2));
        *head = *(head->next_);
        return 0;

Valgrind 抱怨我的读取无效。

==17312== Invalid read of size 4
==17312==    at 0x108EF3: SLLNode::operator=(SLLNode const&) (test_shared_ptr.cpp:3)
==17312==    by 0x108DA3: main (test_shared_ptr.cpp:16)
==17312==  Address 0x5b7dd50 is 16 bytes inside a block of size 24 free'd
==17312==    at 0x4C3123B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17312==    by 0x109511: std::_Sp_counted_ptr<SLLNode*, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:376)
==17312==    by 0x1090DF: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:154)
==17312==    by 0x109061: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::operator=(std::__shared_count<(__gnu_cxx::_Lock_policy)2> const&) (shared_ptr_base.h:703)
==17312==    by 0x108E9A: std::__shared_ptr<SLLNode, (__gnu_cxx::_Lock_policy)2>::operator=(std::__shared_ptr<SLLNode, (__gnu_cxx::_Lock_policy)2> const&) (shared_ptr_base.h:1034)
==17312==    by 0x108EC4: std::shared_ptr<SLLNode>::operator=(std::shared_ptr<SLLNode> const&) (shared_ptr.h:93)
==17312==    by 0x108EEE: SLLNode::operator=(SLLNode const&) (test_shared_ptr.cpp:3)
==17312==    by 0x108DA3: main (test_shared_ptr.cpp:16)
==17312==  Block was alloc'd at
==17312==    at 0x4C3017F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17312==    by 0x108D5C: main (test_shared_ptr.cpp:15)

我使用的命令是:

valgrind --tool=memcheck ./test_shared_ptr

=operator 似乎仅在我使用嵌套 shared_ptr 时才会导致此问题。 我期待来自 ptr head 的数据被释放并替换为来自 ptr head.next_ 的数据,但似乎发生了其他事情,因为我有这个无效的读取。 我想替换 head 指向的数据(而不是 head 本身),因为这个 head ptr 可能是 head 的另一个 shared_ptr 的副本。

【问题讨论】:

【参考方案1】:

隐式定义的赋值运算符如下所示:

SLLNode& SLLNode::operator=(const SLLNode& other) 
  next_ = other.next_;
  data_ = other.data_;
  return *this;

在您的示例中,otherSLLNode(2),它仅通过来自 head-&gt;next_ 的引用保持活动状态。 other.next_NULL

上面的第一个赋值将head-&gt;next_ 设置为NULL,这会导致SLLNode(2) 被销毁,并留下other 一个悬空引用。下一行通过在对象的生命周期结束后访问对象来展示未定义的行为。

【讨论】:

谢谢,我明白发生了什么。重新定义 operator= 以便 data_ 分配发生在 next_ 分配之前作为修复工作,但还有其他方法(也许更干净)? 你想达到什么目的?使用这种方法,如果你有一个像head-&gt;A-&gt;B-&gt;C-&gt;D 这样的链表并且你分配了*head = *C;,你最终会得到head-&gt;D 的链表;节点ABC 都将被销毁。这是你想要的吗? 我只是想实现一个单链表。我在示例中使用了 head,但这个想法只是删除任何节点(在本例中为 head)。所以它会是 head->A->B 如果我给 A 删除我应该有 head->B 对不起,我没有正确解释。我正在尝试编写一个将 shared_ptr 带到节点以删除它的函数。所以我必须确保前一个现在指向下一个。所以给定这个列表 head->A->B 如果我将 shared_ptr 传递给 A 到我的函数,那么列表最后应该是 head->B 通常情况下,您可以通过 previous_node-&gt;next_ = this_node-&gt;next_ 删除一个节点。我不确定您为什么要尝试分配整个节点。

以上是关于Valgrind 报告嵌套 shared_ptrs 的 =operator 读取错误的主要内容,如果未能解决你的问题,请参考以下文章

MIPS 上的 Valgrind 报告没有堆使用

Valgrind 内存泄漏报告中的时间戳不正确

为啥 valgrind 将我的记忆报告为“肯定丢失”?

valgrind 使用 std::string 报告无效读取

Valgrind报告从stdin的getline后释放的指针上的内存泄漏

C++内存检查工具valgrind