【问题标题】:Why doesn't this code work for deleting nodes in a linked list?为什么此代码不适用于删除链表中的节点?
【发布时间】:2015-10-03 22:59:50
【问题描述】:

我一直在学习链接列表的工作原理,并开始用 C++ 构建一个实现来强化这些概念。在我做了一个删除所有节点的函数之前,一切都很顺利。我想出了一个解决方案(这是注释代码),但我似乎无法弄清楚为什么其他代码不起作用。

Node 对象是使用“new”创建的类的实例。因此,'delete' 用于删除它。

我认为这可能与删除对象和重用指针变量有关。然后我遇到了这个:What happens to a pointer itself after delete? 我已经盯着它看了一段时间,试图弄清楚它可能是什么,而我研究过的任何东西似乎都没有提供答案。

到目前为止,我认为这与我的实现无关,因为在将代码替换为解决方案代码时,程序会按预期工作。

代码输出每个地址,但似乎并没有真正删除对象。如果我在 Windows 中运行程序,程序实际上会锁定并且永远不会离开 while 循环。不是无限循环,它只是卡住了,函数永远不会完成。如果我在 C4Droid 上运行它,程序不会锁定,但函数退出后节点仍然存在。

所以我的问题是,为什么当前的代码不起作用? (忽略注释的代码。这是一个可行的解决方案。)有什么简单的我忽略了指针变量?先感谢您。

void LinkedList::deleteAll() {
Node *pCurrent = pHead;

while(pCurrent){
    Node *pNext = pCurrent->pNext;
    std::cout << pCurrent << std::endl;
    delete pCurrent;
    pCurrent = nullptr;
    pCurrent = pNext;

//  pHead = pHead->pNext;
//  delete pCurrent;
//  pCurrent = pHead;
}
}

节点类

class  Node{
    public:
        Node(string content):data(content){}
        string getData(){
            return data;
        }
        Node *pNext = nullptr;
    private:
        string data;
    };

LinkedList.h

/*
 * LinkedList.h
 *
 *  Created on: Oct 3, 2015
 *      Author: Anthony
 */

#ifndef LINKEDLIST_H_
#define LINKEDLIST_H_

#include<string>
using std::string;

class LinkedList {
public:
    LinkedList();
    virtual ~LinkedList();
    int length();
    void addNode(string nodeContent);
    void deleteNode(string nodeContent);
    void deleteAll();
private:
    class  Node{
    public:
        Node(string content):data(content){}
        string getData(){
            return data;
        }
        Node *pNext = nullptr;
    private:
        string data;
    };

    Node *pHead = nullptr;

};

#endif /* LINKEDLIST_H_ */

LinkedList.cpp

    /*
 * LinkedList.cpp
 *
 *  Created on: Oct 3, 2015
 *      Author: Anthony
 */

#include "LinkedList.h"
#include <iostream>
LinkedList::LinkedList() {
    // TODO Auto-generated constructor stub

}

LinkedList::~LinkedList() {
    // TODO Auto-generated destructor stub
}

int LinkedList::length() {
    Node *current = pHead;
    int count = 0;
    while(current){
        count++;
        current = current->pNext;
    }
    return count;
}

void LinkedList::addNode(std::string nodeContent) {
    Node *newNode = new Node(nodeContent);
    newNode->pNext = pHead;
    pHead = newNode;
}

void LinkedList::deleteNode(std::string nodeContent) {
}

void LinkedList::deleteAll() {
    Node *pCurrent = pHead;

    while(pCurrent){
        Node *pNext = pCurrent->pNext;
        std::cout << pCurrent->pNext << std::endl;
        delete pCurrent;
        pCurrent = nullptr;
        pCurrent = pNext;

    //  pHead = pHead->pNext;
    //  delete pCurrent;
    //  pCurrent = pHead;
    }
}

main.cpp

/*
 * main.cpp
 *
 *  Created on: Oct 3, 2015
 *      Author: Anthony
 */

#include<iostream>
#include "LinkedList.h"

int main(int argc, char **argv){

    using namespace std;

    LinkedList list = LinkedList();
    list.addNode(string("Test"));
    list.addNode(string("Test1"));

    list.deleteAll();
    cout << list.length() << endl;

    return 0;
}

【问题讨论】:

  • 你的Node 类有析构函数吗?如果是这样,请发布它。 我已经盯着它看了一段时间 -- 使用你的调试器来调试代码。无需盯着程序。
  • @PaulMcKenzie 没有析构函数。 Node 类的代码已放在描述中。
  • 我猜你在这个函数之后的某个地方堆栈(通过调试器检查它或在最后添加一些打印),只需尝试在最后(循环之后)将pHead设置为null。
  • 可能你的链表已经损坏或者在你调用函数时被错误地组合在一起。您也没有在循环后将head 指针设置为 NULL。

标签: c++ pointers linked-list delete-operator


【解决方案1】:

假设(这是一个很大的假设)您的链表被正确地组合在一起,那么为什么注释代码有效而新代码无效的问题就相当简单了。

  pHead = pHead->pNext;
  delete pCurrent;
  pCurrent = pHead;

在上面的代码中,您在循环时将pHead 指针移动到列表中。当循环结束时,pHead 指针是nullptr,这是正确的,因为列表现在是空的。

Node *pNext = pCurrent->pNext;
    std::cout << pCurrent << std::endl;
    delete pCurrent;
    pCurrent = nullptr;
    pCurrent = pNext;

使用新的、未注释的代码,您没有在循环结束后设置pHead 指针,从而使其指向垃圾。之后对链表的任何使用都将失效。

所以不是函数没有删除所有节点,而是删除节点后,链表有一个野生pHead指针,并使用链表的pHead节点在任何后续函数中变得不稳定。

尝试以下方法:

void LinkedList::deleteAll() {
Node *pCurrent = pHead;
while(pCurrent){
    Node *pNext = pCurrent->pNext;
    delete pCurrent;
    pCurrent = nullptr;
    pCurrent = pNext;
}
pHead = nullptr;  // Sets the head pointer to nullptr, denoting that the list is empty.

【讨论】:

  • 非常感谢!我现在知道了。这取决于我使用额外的节点指针而不是直接在注释代码所做的列表上操作的事实。 pHead 用于开始遍历,但从未对其自身进行操作。感谢您的时间和详细的回答!
【解决方案2】:

当您使用“new”关键字创建对象时,涉及两个主要内存区域,“调用堆栈”跟踪局部变量和已调用的函数,以及“堆”设计以牺牲速度为代价保存大量数据。

当您声明局部变量 pCurrent 时,会在“调用堆栈”上创建一个指针,就像使用声明“int a;”将局部整数变量放入堆栈一样局部变量,栈上的变量,不需要删除。

所有使用“new”创建的对象都需要被删除,因为它们是在堆上创建的。

正如 PaulMcKenzie 所写,确保也将您的头指针设置为 null。

void LinkedList::deleteAll() {

    Node *pCurrent = pHead;

    while(pCurrent){
        Node *pNext = pCurrent->pNext;
        std::cout << pCurrent << std::end;
        delete pCurrent;

        pCurrent = pNext;
    }

    pHead = nullptr;
}

【讨论】:

  • nullptr 是 C++11 中包含的关键字。我在指向新对象的指针上使用删除。
  • 哦,明白了。不知道那个。
  • 我仍然不明白您为什么要将 pCurrent 设置为 nullptr 然后立即重新分配它。当然它不会伤害任何东西,但它似乎有点没用。
  • 因为一旦对象被删除,指针就会指向无效的内存。我养成了将其设为 nullptr 的习惯。如果我返回并在与指针混淆的那两行之间添加代码,那将是非常危险的。这在这个小程序中并不明显,但在更大的程序中,查明错误要困难得多。无论如何,在我看来。
  • 对,这是一般的好习惯。但这不是重新分配的问题。如果我的指针指向内存地址 0xfff 并且我将它重新分配给 0xaaa,那么我就没有机会访问 0xfff 的内存了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-04
  • 2015-07-18
  • 1970-01-01
  • 1970-01-01
  • 2022-01-14
  • 2013-10-14
  • 1970-01-01
相关资源
最近更新 更多