当两个节点指向彼此时,不可避免的内存泄漏

时间:2014-02-18 07:09:27

标签: c++ shared-ptr

我对shared_ptr有一个非常奇怪的问题,其中两个Node正在使用shared_ptr指向彼此。

我先粘贴我的测试代码并输出valgrind,然后按照我的理解。

测试代码

#include <iostream>
#include <memory>

struct Node {
  int val_;
  std::shared_ptr<Node> next_;
  std::shared_ptr<Node> prev_;

  Node(int val)
      : val_(val), next_(nullptr), prev_(nullptr) {
  }
};

class List {
public:
  List() 
      : head_(nullptr){
  }

  void insert(int val) {
    auto new_node = std::make_shared<Node>(val);
    if (!head_)
      head_ = new_node;
    else {
      head_->next_ = new_node;
      new_node->prev_ = head_;
    }
  }

  void debug() {
    std::shared_ptr<Node> cur;
    for (cur = head_; cur != nullptr; cur = cur->next_)
      std::cout << cur->val_ << std::endl;
  }
private:
  std::shared_ptr<Node> head_;
};

int main(int argc, const char *argv[]) {
  List l;
  l.insert(199);
  l.insert(200);

  l.debug();

  return 0;
}

valgrind的输出。

==8282== Memcheck, a memory error detector
==8282== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==8282== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==8282== Command: ./a.out
==8282== 
==8282== 
==8282== HEAP SUMMARY:
==8282==     in use at exit: 128 bytes in 2 blocks
==8282==   total heap usage: 2 allocs, 0 frees, 128 bytes allocated
==8282== 
==8282== 128 (64 direct, 64 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==8282==    at 0x4C28C90: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8282==    by 0x401825: __gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<Node, std::allocator<Node>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (in /home/lightmanhk/Backup/repo/scripts_code/docs/c++/tech/module/shared_ptr/a.out)
==8282==    by 0x401751: std::allocator_traits<std::allocator<std::_Sp_counted_ptr_inplace<Node, std::allocator<Node>, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_ptr_inplace<Node, std::allocator<Node>, (__gnu_cxx::_Lock_policy)2> >&, unsigned long) (in /home/lightmanhk/Backup/repo/scripts_code/docs/c++/tech/module/shared_ptr/a.out)
==8282==    by 0x4015B1: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<Node, std::allocator<Node>, int&>(std::_Sp_make_shared_tag, Node*, std::allocator<Node> const&, int&) (in /home/lightmanhk/Backup/repo/scripts_code/docs/c++/tech/module/shared_ptr/a.out)
==8282==    by 0x4014FB: std::__shared_ptr<Node, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<Node>, int&>(std::_Sp_make_shared_tag, std::allocator<Node> const&, int&) (in /home/lightmanhk/Backup/repo/scripts_code/docs/c++/tech/module/shared_ptr/a.out)
==8282==    by 0x401451: std::shared_ptr<Node>::shared_ptr<std::allocator<Node>, int&>(std::_Sp_make_shared_tag, std::allocator<Node> const&, int&) (in /home/lightmanhk/Backup/repo/scripts_code/docs/c++/tech/module/shared_ptr/a.out)
==8282==    by 0x40139D: std::shared_ptr<Node> std::allocate_shared<Node, std::allocator<Node>, int&>(std::allocator<Node> const&, int&) (in /home/lightmanhk/Backup/repo/scripts_code/docs/c++/tech/module/shared_ptr/a.out)
==8282==    by 0x4011D2: _ZSt11make_sharedI4NodeIRiEESt10shared_ptrIT_EDpOT0_ (in /home/lightmanhk/Backup/repo/scripts_code/docs/c++/tech/module/shared_ptr/a.out)
==8282==    by 0x401000: List::insert(int) (in /home/lightmanhk/Backup/repo/scripts_code/docs/c++/tech/module/shared_ptr/a.out)
==8282==    by 0x400DAC: main (in /home/lightmanhk/Backup/repo/scripts_code/docs/c++/tech/module/shared_ptr/a.out)
==8282== 
==8282== LEAK SUMMARY:
==8282==    definitely lost: 64 bytes in 1 blocks
==8282==    indirectly lost: 64 bytes in 1 blocks
==8282==      possibly lost: 0 bytes in 0 blocks
==8282==    still reachable: 0 bytes in 0 blocks
==8282==         suppressed: 0 bytes in 0 blocks
==8282== 
==8282== For counts of detected and suppressed errors, rerun with: -v
==8282== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 1 from 1)

理解

|------|               |-------|
|next----------------->|next(nullptr)
|prev(nullptr)|<--------prev   |
|------|               |-------|
 head_                  new_node

我相信我遇到内存泄漏的原因是当List的析构函数被调用时,它只会将对head_的引用减少1而不是释放head_ (因为new_node->prev_也指向head_。 (也许我错了?)

1&GT; 我的问题如何解决这个问题。

希望有人给我建议。非常感谢。

3 个答案:

答案 0 :(得分:2)

你是对的。有很多方法可以解决这个问题,但我建议不要使用强指针圈。这个类的一个简单修复是使前向指针成为强指针,后指针成为弱指针。这样,删除列表将删除指向头节点的唯一强指针,这将删除指向第二个节点的唯一强指针,依此类推。

在操作列表中的节点时,请确保拥有自己强大的指向您正在操作的节点的指针,以确保在操作其前向指针时它们不会被销毁。

答案 1 :(得分:2)

shared_ptr不是灵丹妙药。您应该问问自己,Node 是否拥有下一个/上一个Node

替代方案是List 拥有所有Node,并Node链接到其他Node。< / p>

从语义上讲,shared_ptrunique_ptr意味着所有权,*(或&)意味着一种关系。

有关不同数据结构的最新示例,请参阅Is there a more efficient implementation for a bidirectional map?。您可以看到在管理对象的内存和管理对象之间的关系之间存在很好的分离。

答案 2 :(得分:0)

您没有提供太多上下文,但如果这些是图表的节点, 你可能根本不想要智能指针。在这种情况下, 最好的解决方案通常是让图形对象保持谨慎 其节点的所有内存管理,因为它(只有它) 知道什么时候不再使用节点。 shared_ptr 一个很好的解决方案。 (注意,没有任何实现 我见过的std::list使用shared_ptr。)