在方法超出范围后,是否应该删除引用的std :: shared_ptr?

时间:2015-01-22 19:00:41

标签: c++ c++11 shared-ptr smart-pointers singly-linked-list

我正在学习智能指针,学习它比在堆上实现简单结构(如Linked List)更好。

我创建了一个链表结构,如下所示......

// linked list node definition
#ifndef __LINKED_LIST_NODE_H
#define __LINKED_LIST_NODE_H

class LinkedListNode {
    friend class LinkedList;
public:
    int                 m_value;
    LinkedListNode *    m_pNext;
public:
    LinkedListNode();
    LinkedListNode(int);
    LinkedListNode(const LinkedListNode &);
    ~LinkedListNode();
};

#endif

// linked list definition
#ifndef __LINKED_LIST_H
#define __LINKED_LIST_H

class LinkedList {
    LinkedListNode * m_pHead;
    LinkedListNode * m_pTail;
public:
    LinkedList();
    LinkedList(int);
    LinkedList(const LinkedList &);
    ~LinkedList();
    void PrintList() const;
    void AddItem(int);
    void RemoveItem(int);
    LinkedListNode * FindNode(int) const;
    LinkedListNode * FindMin() const;
    LinkedListNode * FindMax() const;
};

#endif

以下是LinkedListNode和LinkedList类的必要方法(构造函数和析构函数),看看它们是什么样的(IIRC这些应该是正确的)......

// list node
LinkedListNode::LinkedListNode()
{
    m_value = 0;
    m_pNext = nullptr;
}

LinkedListNode::LinkedListNode(int value)
{
    m_value = value;
    m_pNext = nullptr;
}

LinkedListNode::LinkedListNode(const LinkedListNode & copyNode)
{
    m_value = copyNode.m_value;
    m_pNext = copyNode.m_pNext;
}

LinkedListNode::~LinkedListNode()
{
    // not needed, no dynamic allocation
}


// linked list
LinkedList::LinkedList()
{
    m_pHead = nullptr;
    m_pTail = m_pHead;
}

LinkedList::LinkedList(int value)
{
    std::shared_ptr<LinkedListNode>newNode{ new LinkedListNode(value) };
    m_pHead = newNode.get();
    m_pHead->m_pNext = nullptr;
    m_pTail = m_pHead;
}

LinkedList::LinkedList(const LinkedList & copyList)
{
    if (copyList.m_pHead == nullptr)
    {
        m_pHead = nullptr;
        m_pTail = m_pHead;
    }
    else
    {
        std::shared_ptr<LinkedListNode>NodeResource{ new LinkedListNode(*copyList.m_pHead) };

        LinkedListNode * tempNode = NodeResource.get();

        m_pHead = tempNode;

        while (tempNode->m_pNext != nullptr)
        {           
            std::shared_ptr<LinkedListNode>NodeResourceNext{ new LinkedListNode(*tempNode->m_pNext) };
            tempNode->m_pNext = NodeResourceNext.get();
            tempNode = NodeResourceNext.get();
        }

        m_pTail = tempNode;
    }
}

LinkedList::~LinkedList()
{
    // not needed, allocating using smart pointers
}

现在,LinkedList类包含AddItem方法,其正文如下:

void LinkedList::AddItem(int value)
{
    std::shared_ptr<LinkedListNode>newNode{ new LinkedListNode(value) };

    if (m_pHead == nullptr) // linked list is empty
    {
        m_pHead = newNode.get();
        m_pTail = newNode.get();
    }
    else
    {
        m_pTail->m_pNext = newNode.get();
        m_pTail = newNode.get();
    }
}

我不知道为什么,但是当我尝试将一个项目添加到我的链接列表时,当你超出方法的范围时,似乎会删除newNode变量。

以下是我尝试调试程序时的样子......

首先我们从空链表开始 Empty Linked List

然后在AddItem函数中我得到以下结果(看起来m_pHead和m_pTail正确地指向堆上新创建的newNode。

Inside Method AddItem()

但是当AddItem()方法超出范围时,这就是我剩下的

After AddItem() method

我想,一旦没有引用指针,就会删除std :: share_ptr。在我的例子中,newNode由两个指针m_pHead和m_pTail引用。离开AddItem()方法时它是否真的被删除了,或者我的代码中是否有缺陷我还没有被发现?

非常感谢你的意见,伙计们。

2 个答案:

答案 0 :(得分:4)

您要将newNode.get()分配给LinkedListNode*的原始指针(LinkedListNode)成员。

这是不正确的,因为shared_ptr(在这种情况下至少)拥有底层指针。当它超出范围时,相关的内存被释放,但LinkedListNode仍然拥有以前分配的内存的成员。

您可能应该更改LinkedListNode的定义,以便其LinkedListNode*成员为shared_ptr<LinkedListNode>,确保只要您的实例存在,就会引用基础内存。

答案 1 :(得分:1)

您的问题太长了,但我发现您似乎没有以适当的方式使用std::shared_ptr。例如,

LinkedList::LinkedList(int value)
{
    std::shared_ptr<LinkedListNode>newNode{ new LinkedListNode(value) };
    m_pHead = newNode.get();
    m_pHead->m_pNext = nullptr;
    m_pTail = m_pHead;
}   // <-- call of std::shared_ptr::~std::shared_ptr

newNode将在函数体的末尾被销毁。然后,这将删除共享指针的对象(恰好在一个std::shared_ptr之间),从而使m_pHead留下悬空指针

shared_ptr的基本思想是分享某些资源的所有权,只要任何共享所有者仍然活着,那么资源也是如此:当最后一个所有者去世时,资源被删除。

在绝大多数优秀代码中,资源由某些对象拥有,即共享所有权是一种相当罕见的利基情况。在简单的链表实现中肯定没有必要。


链表的一种可能的所有权模型是每个节点拥有下一个节点。那么你会有

template<typename T> class list;
template<typename T>
class node {
  friend class list<T>;
  T m_data;
  unique_ptr<node> m_next;
  explicit node(T const&value)
  : m_data(value) {}
  node(T const&value, unique_ptr<node>&&n)
  : m_data(value), m_next(n) {}
public:
  node*next() { return m_next.get(); }
  const node*next() const { return m_next.get(); }
};

template<typename T>
class list {
  typedef node<T> node_type;
  unique_ptr<node_type> m_head;
  node_type            *m_tail;
  ~list() {}  // destructor of m_head destroys all nodes recursively
  explicit list(T const&value)
  : m_head(new node_type(value)), m_tail(m_head.get()) {}
  void push(T const&value)
  {
    // move the existing m_head to be m_next of the new node,
    // which in turn becomes the new m_head. m_tail is unaffected.
    m_head = new node_type(value,std::move(m_head));
  }
};

但是,你必须小心如何实现插入和切片等。