从链表中的 getNext()、getPrev() 或 getData() 函数返回 NULL

Posted

技术标签:

【中文标题】从链表中的 getNext()、getPrev() 或 getData() 函数返回 NULL【英文标题】:Returning NULL from a getNext(), getPrev() or getData() function in a linked list 【发布时间】:2021-10-22 14:50:29 【问题描述】:

我正在尝试创建一个链表,目前正在测试打印功能,但为了打印列表中的所有节点,我需要知道长度。

为了找到长度,我需要使用 for 循环从头部循环到当前为 NULL(又名尾部)的任何时候。

问题是每当列表中的节点设置为 NULL 时,当我使用 getNext() 或任何其他旨在返回 NULL 的函数时,它会导致错误或没有任何反应。

我已经尝试了很长时间来解决这个问题,但我根本找不到我做错了什么。这可能是一个愚蠢的错误,但我就是找不到。

创建一个新的空白列表:

LL::LL()

    // These are all protected variables
    head = NULL;
    tail = NULL;
    current = NULL;

创建一个新的空白节点:

Node::Node()

    // These are all protected variables
    data = v_t(); // Value_Type
    prev = NULL;
    next = NULL;

使用参数创建一个新节点:

Node::Node(const v_t& d, Node* n, Node* p)

    data = d;
    prev = p;
    next = n;

长度函数:

int LL::length()

    int answer = 0;
    for (current = head; current != NULL; current = current->getNext())
    
        answer++;
    
    return answer;

在链表尾部添加一个新节点:

void LL::addToTail(const v_t& item)

    // ------------(data, next, prev)
    tail = new Node(item, NULL, tail);

    if (tail -> getPrev() != NULL)
    
        tail -> getPrev() -> setNext(tail);
    

    if (head == NULL)
    
        head = tail;
    

下一个返回函数:

Node* Node::getNext() const

    return next;

其他两个 getter 的格式相同。

以下是完整的课程:

节点.h

#ifndef CHRIS_NODE
#define CHRIS_NODE

#include "account.h"

class Node

    public: // Members that are externally visible

        typedef Account v_t;

        // Default Constructor
        Node();
        Node(const v_t& d, Node* n, Node* p);

        // Destructor
        ~Node();

        // Pointer Getters and Setters
        void setNext(Node* n);
        void setPrev(Node* p);
        Node* getNext() const;
        Node* getPrev() const;

        // Data Getters and Setters
        void setData(v_t& d);
        v_t getData() const;

    private: // Members that are internally visible

        Node* next;
        Node* prev;
        v_t data;

;

#endif

node.cpp

#include"node.h"

Node::Node()

    data = v_t();
    prev = NULL;
    next = NULL;


Node::Node(const v_t& d, Node* n, Node* p)

    data = d;
    prev = p;
    next = n;


Node::~Node();

void Node::setNext(Node* n)

    next = n;


void Node::setPrev(Node* p)

    prev = p;


Node* Node::getNext() const

    return next;


Node* Node::getPrev() const

    return prev;


void Node::setData(v_t& d)

    data = d;


Node::v_t Node::getData() const

    return data;

链接列表.h

#ifndef CHRIS_LIST
#define CHRIS_LIST

#include "node.h"

class LL

    public: // Members that are externally visible
    typedef Node::v_t v_t;

    LL();
    LL(Node*  h, Node* t, Node* c);
    ~LL();

    int length();

    void addToHead(const v_t& item);

    void addToCurrent(const v_t& item);

    void addToTail(const v_t& item);

    bool search(const v_t& target);

    void removeHead();

    void removeCurrent();

    void removeTail();

    void clear();

    void printList();

    protected: // Members that are internally visible
    Node* head;
    Node* tail;
    Node* current;
;


#endif

链接列表.cpp

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

using namespace std;

LL::LL()

    head = NULL;
    tail = NULL;
    current = NULL;


LL::LL(Node*  h, Node* t, Node* c)

    head = h;
    tail = t;
    current = c;


LL::~LL()

    clear();
        


int LL::length()

    int answer = 0;
    for (current = head; current != NULL; current = current->getNext())
    
        answer++;
    
    return answer;


void LL::addToHead(const v_t& item)

    head = new Node(item, head, NULL);

    if (head -> getNext() != NULL)
    
        head -> getNext() -> setPrev(head);
    

    if (tail == NULL)
    
        tail = head;
    


void LL::addToCurrent(const v_t& item)

    Node* newNode = new Node(item, current, current->getPrev());
    current->setPrev(newNode);
    newNode->getPrev()->setNext(newNode);
    current = head;


void LL::addToTail(const v_t& item)

    tail = new Node(item, NULL, tail);

    if (tail -> getPrev() != NULL)
    
        tail -> getPrev() -> setNext(tail);
    

    if (head == NULL)
    
        head = tail;
    


bool LL::search(const v_t& target)

    for (current = head; current != NULL; current = current -> getNext())
    
        if (target == (current -> getData()))
        
            cout << "The data is stored in " << current << "." << endl;
            return true;
        
    
    return false;


void LL::removeHead()

    Node* temp = head;
    head = head -> getNext();
    if (head != NULL)
    
        head -> setPrev(NULL);
    
    else
    
        tail = NULL;
    
    delete temp;


void LL::removeCurrent()

    if (current == head) 
    
        removeHead();
    
    else if (current == tail)
    
        removeTail();
    

    current -> getNext() -> setPrev(current -> getPrev());
    current -> getPrev() -> setNext(current -> getNext());

    delete current;
    current = head;


void LL::removeTail()

    Node* temp = tail;
    tail = tail -> getPrev();
    if (tail != NULL)
    
        tail -> setNext(NULL);
    
    else
    
        head = NULL;
    
    delete temp;


void LL::clear()

    while (head != NULL)
    
        removeHead();
    


void LL::printList()

    if (LL::length() == 0)
    
        cout << "List Empty.\n";
    
    else
    
        current = head;

        for (int i = 1; i <= LL::length(); i++)
        
            if (current != NULL)
            
                cout << "Node " << i << ": " << current -> getData() << endl;
                current = current -> getNext();
            
        
    

帐户.h

#ifndef CHRIS_ACCOUNT
#define CHRIS_ACCOUNT

#include <string>
#include <iostream>
using namespace std;

class Account

public:
    // Members that are externally visible

    // These are member functions

    // Constructor
    // Precondition:    none
    // Postcondition:   A new instance of account is created and its 
    //                  instance data initialsed to either zero or a 
    //                  parameter-provided value
    Account(const string nm = "", const double initialValue = 0.0);

    // Members that mutate data

    // Precondition:    acct_balance has been initialised
    // Postcondition:   amount is added to the acct_balance
    void deposit(const double amount);

    // Precondition:    acct_balance has been initialised
    // Postcondition:   amount is subtracted from the acct_balance
    void withdraw(const double amount);

    void setName(const string nm);

    // Members that query data

    // Precondition:    acct_balance has been initialised
    // Postcondition:   The value of acct_balance is returned
    double balance() const;

    // Precondition:    acct_balance has been initialised
    // Postcondition:   Returns true if acct_balance is greater 
    //                  than zero, false otherwise
    bool has_funds() const;

    string getName() const;


private:

    double acc_balance;
    string name;
;

bool operator == (Account acc1, Account acc2);

ostream& operator << (ostream& out, const Account acc);

// close the macroguard
#endif 

account.cpp

#include "Account.h"

Account::Account(string nm, double initialValue)

    acc_balance = initialValue;
    name = nm;


void Account::deposit(double amount)

    acc_balance += amount;


void Account::withdraw(double amount)

    acc_balance -= amount;


double Account::balance() const

    return acc_balance;


bool Account::has_funds() const

    if (acc_balance > 0.0) 
    
        return true;
     
    else 
    
        return false;
    


string Account::getName() const

    return name;


void Account::setName(string nm) 

    name = nm;


bool operator == (Account acc1, Account acc2)

    if (acc1.getName() == acc2.getName() && acc1.balance() == acc2.balance())
    
        return true;
    
    else
    
        return false;
    


ostream& operator << (ostream& out, const Account acc)

    out << "(" << acc.getName() << ", " << acc.balance() << ")\n";
    return out;

银行.cpp

#include <iostream>
#include <cstdlib>
#include <string>
#include "Account.h"
#include "node.h"
#include "linklist.h"

using namespace std;

int main()


   int amount = 0;

   cout << "How many accounts?\n";
   cin >> amount;

   LL* LL1 = new LL();

   for (int i = 1; i <= amount; i++)
   
       string nm;
       double iv;

       cout << "What is the name for account " << i << "?\n";
       cin >> nm;
       cout << "What is the initial value for account " << i << "?\n";
       cin >> iv;

       Account newAcc(nm, iv);
       LL1 -> addToTail(newAcc);
   

    LL1 -> printList();

    return EXIT_SUCCESS;

如果您需要更多信息或代码,请告诉我:)

【问题讨论】:

"如果您需要更多信息或代码,请告诉我" - 如果您显示类的完整定义,问题会更清楚。 由于 new Node(item, NULL, tail); 与您选择向我们展示的唯一构造函数不匹配,您可能还需要添加该 ctor。事实上,包括一个正确的minimal reproducible example 并静态编码让你感到痛苦的节点插入。 @TedLyngmo 我会,但我认为这些 sn-ps 应该足以满足我的要求,而不会使用不必要的代码使帖子过于复杂。但正如我所说,如果您认为缺少一些有用的代码,或者您认为我应该发布整个课程,请告诉我。 @WhozCraig 我的错。这次我添加了正确的节点构造函数。 for (current = head; current != NULL; current-&gt;getNext()) 是一个无限循环,因为你忘了写current =。不要想太多。 【参考方案1】:

当我使用 getNext() 或任何其他旨在返回 NULL 的函数时,什么都没有发生。

那是因为您没有在length 方法中使用 getNext() 的结果。您的循环永远不会为current 分配新引用。改变这个:

for (current = head; current != NULL; current->getNext())

到:

for (current = head; current != NULL; current = current->getNext())

current 定义为LL 类的实例成员是一种反模式。这样的引用应该是一个局部变量,在需要它的函数中定义。它不应该是您的链表的状态的一部分。

所以无论你在哪里使用current,都将其定义为局部变量:

Node * current = head;

为了打印列表中的所有节点,我需要知道长度。

不一定。您可以更改您的 printList 函数以在没有 length 的情况下工作:

void LL::printList()

    Node * current = head;
    if (current == NULL)
    
        cout << "List Empty.\n";
    
    else
    
        for (int i = 1; current != NULL; i++, current = current -> getNext())
        
            cout << "Node " << i << ": " << current -> getData() << endl;
        
    

如果您保留调用length 的版本并继续使用实例变量current,则循环不会循环,因为对length() 的调用将改变current(在您初始化之后),并且将其设置为NULL

如果你真的想要实例变量current,那么只在真正需要设置的地方改变它,比如search。但它不应该被length()printList() 改变,它们不应该改变你的实例的状态。所以在length()printList() 你应该使用local 变量,而不是instance 变量。

【讨论】:

为什么在类范围内创建 current 不起作用?因为我想跟踪列表类中的当前指针,并且我目前几乎在每个函数中都使用它。第一个问题也已修复,但它仍然输出一个空字段然后崩溃。 “为什么在类范围内创建 current 不起作用?”:它起作用了;这不是一个好习惯。 “我想跟踪列表类中的当前指针”:但实际上你没有,因为你的函数只会迭代直到 currentnull。所以这对跟踪不是很有帮助。你总能找到它null。如果输出中有一个空字段,则说明您做错了其他事情。您发布了大约 400 行代码。我不想审查那个。只指出明显的错误。 我建议您减少代码,不要使用Account,而是使用纯整数,直到您可以使用它。只有这样才能扩展回使用Account。从简单开始,确保它有效,然后再扩展一点。再次测试。如果它有效......进一步扩展。当您的问题是打印链接列表时,400 行代码是错误的方式。你可以为一个可以打印的简单链表编写 50 行代码。然后扩展。 查看补充答案。我希望能更清楚地说明为什么当您坚持使用 current 实例成员时打印循环不循环,您几乎可以在所有方法中使用它。 我很抱歉。我看到您对我以前没有看到的问题的补充。我已将您的回复设为官方回复。谢谢。

以上是关于从链表中的 getNext()、getPrev() 或 getData() 函数返回 NULL的主要内容,如果未能解决你的问题,请参考以下文章

从链表中删除一个节点

从链表中删除节点(递归)

如何从链表中反转堆栈?

从链表中删除用户选择的节点,用 c 中另一个列表中的节点替换它们

从链表中删除总和值为0的连续节点

从链表中删除节点