从链表中的 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->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 不起作用?”:它起作用了;这不是一个好习惯。 “我想跟踪列表类中的当前指针”:但实际上你没有,因为你的函数只会迭代直到current
是 null
。所以这对跟踪不是很有帮助。你总能找到它null
。如果输出中有一个空字段,则说明您做错了其他事情。您发布了大约 400 行代码。我不想审查那个。只指出明显的错误。
我建议您减少代码,不要使用Account
,而是使用纯整数,直到您可以使用它。只有这样才能扩展回使用Account
。从简单开始,确保它有效,然后再扩展一点。再次测试。如果它有效......进一步扩展。当您的问题是打印链接列表时,400 行代码是错误的方式。你可以为一个可以打印的简单链表编写 50 行代码。然后扩展。
查看补充答案。我希望能更清楚地说明为什么当您坚持使用 current
实例成员时打印循环不循环,您几乎可以在所有方法中使用它。
我很抱歉。我看到您对我以前没有看到的问题的补充。我已将您的回复设为官方回复。谢谢。以上是关于从链表中的 getNext()、getPrev() 或 getData() 函数返回 NULL的主要内容,如果未能解决你的问题,请参考以下文章