【问题标题】:Linked List destructor in C++: should I delete?C++ 中的链表析构函数:我应该删除吗?
【发布时间】:2016-09-24 13:21:51
【问题描述】:

我已经开始在 C++ 中实现一些数据结构,从链表开始。 来自 Java 背景,我仍然在思考指针和对象的生命周期。

链表:

struct Node
{
    int data;
    Node *next;
};

class LinkedList
{
private:
    Node *head;
    Node *tail;
    int length;

public:
    LinkedList();
    ~LinkedList();

    void addToHead(Node &newHead);
    void popHead();

    void printList();
};

然后我就这样实现了:

LinkedList::LinkedList()
{ 
    head = NULL;
    tail = NULL;
    length = 0;
}

LinkedList::~LinkedList(){}

void LinkedList::addToHead(Node& newHead)
{
    newHead.next = head;
    head = &newHead;
    length++;
}

void LinkedList::popHead()
{
    Node *currHead = head;
    head = head->next;
    length--;
}

void LinkedList::printList()
{
    Node *curr = head;

    while(curr)
    {
        curr = curr->next;  
    }
}

最后有一个简单的主要内容:

int main()
{
    LinkedList list;
    Node n1 = {3};
    Node n2 = {4};
    Node n3 = {5};
    list.addToHead(n1);
    list.addToHead(n2);
    list.addToHead(n3);
    list.printList();
    list.popHead();
    list.printList();

    return 0;
}

这是一个相当幼稚的实现,我想知道是否必须提供一个适当的析构函数,在迭代时删除 Node* 指针。 每当我尝试添加它时,程序都会导致内存错误,我认为正在分配的内存也在 main 结束时被释放,因为所有的 Node* 都在那里。

我应该修复我的析构函数吗?我应该改变整个界面吗?

提前致谢!

【问题讨论】:

  • delete 用于使用 new 创建的东西。
  • 所以当 main() 完成运行时 Node*s 将被销毁..?我只是想了解我是否在这里泄漏了任何内存
  • @user 换句话说,如果你没有使用new,你就不必使用delete。如果您没有使用new,则您的示例中不可能存在内存泄漏
  • @user49428 n1n2n3 在堆栈上创建(即,不使用 new),因此当它们超出范围时,它们使用的内存可用.请注意,列表类包含一个指向节点的指针,节点可能在列表之前被销毁,这会使您的列表处于不良状态(悬空指针)。
  • 严格来说不是关于您的问题,但您似乎正在学习 C++98 / C++03。你可能最好学习 C++11——还有 C++14,但这只是修复了 C++11 的一些小疏忽,所以关于 C++11 的文献并没有过时,而我肯定会称之为 C ++98 / C++03 那个。

标签: c++ memory-management memory-leaks


【解决方案1】:

虽然您的代码中没有内存泄漏,但我认为您应该更改您的界面。

您的链接列表并没有做您可能认为它在做的事情 - 获取其内容的所有权。不拥有其内容的链表是一种奇怪的野兽,可能是您不打算的。

获得所有权的一种简单方法是更改​​您的设计以使用std::unique_ptr 而不是原始指针。然后,您的 addToHead 函数将更改为采用 std::unique_ptr r-value 引用指针(或者如果太高级,则只是在内部创建新 std::unique_ptr 的原始指针)

这是您的实现更改为使用std::unique_ptr。它有点粗略,但应该能让你上路:

#include <memory>

struct Node
{
    Node(int i) : data(i) 
    {}

    int data;
    std::unique_ptr<Node> next;
};

class LinkedList
{
private:
    std::unique_ptr<Node> head;
    Node *tail;
    int length;

public:
    LinkedList();
    ~LinkedList();

    void addToHead(std::unique_ptr<Node>&& newHead);
    void popHead();

    void printList();
};

LinkedList::LinkedList()
{ 
    head = NULL;
    tail = NULL;
    length = 0;
}

LinkedList::~LinkedList(){}

void LinkedList::addToHead(std::unique_ptr<Node>&& newHead)
{
    newHead->next = std::move(head);
    head = std::move(newHead);
    length++;
}

void LinkedList::popHead()
{
    head = std::move(head->next);
    length--;
}

void LinkedList::printList()
{
    auto* curr = head.get();

    while(curr)
    {
        curr = curr->next.get();  
    }
}

int main()
{
    LinkedList list;
    list.addToHead(std::make_unique<Node>(3));
    list.addToHead(std::make_unique<Node>(4));
    list.addToHead(std::make_unique<Node>(5));
    list.printList();
    list.popHead();
    list.printList();

    return 0;
}

【讨论】:

  • 对它需要什么样的修改有什么建议吗? =)
  • 有一个很好的教程实现here
  • NULL 应替换为 nullptr
  • 这感觉就像第一次调用 popHead() 时会删除整个列表。否则弹出的条目会泄漏。
  • 我明白你为什么认为它“感觉”那样但不会发生。在unique_ptr 赋值操作中,右轴的“移动”发生在旧左轴的删除之前——为了异常安全,它必须如此循环。因此,当旧头被销毁时,它不再拥有其next - 这已被移至新头。
猜你喜欢
  • 1970-01-01
  • 2020-09-25
  • 2017-01-11
  • 2012-12-22
  • 2018-03-27
  • 2013-04-12
  • 2016-03-26
  • 2017-08-30
  • 2010-10-02
相关资源
最近更新 更多