实现链表

Implementing a Linked List

本文关键字:链表 实现      更新时间:2023-10-16

我正在C++中实现链表。虽然我过去在java中做过这件事,但我不知道如何在C++中用指针来做,因为代码正在编译,但当我运行它时,它会给我一个分段错误。我做错了什么?

我的节点.h文件

#ifndef NODE_H
#define NODE_H
#include <string>
using namespace std;
class Node
{
public:
    Node(const string, const int) ;
    ~Node() { }
    void setNext(Node *); // setter for the next variable
    Node * getNext();     // getter for the next variable
    string getKey();      // getter for the key variable
    int getDistance();    // getter for the dist variable
private:
   Node *next;
   int dist;
   string key;
};
#endif

我的Node.cpp文件

#include "node.h"
#include <string>
Node::Node(string key, int dist){
    key = key;
    dist = dist;
}
void Node::setNext(Node * next){
    next->next;
}
Node * Node::getNext(){
    return this->next;
}
string Node::getKey(){
    return key;
}
int Node::getDistance(){
    return dist;
}

和我的main.cpp文件

#include "node.h"
#include <iostream>
using namespace std;
int main(){
    Node* nptr1 = new Node("Test1", 2);
    Node* nptr2 = new Node("Test2", 2);
    Node* temp;
    nptr1->setNext(nptr2);
    temp = nptr1->getNext();
    cout << temp->getKey() << "-" << temp->getDistance() << endl;
}

如有任何帮助,我们将不胜感激。谢谢

您应该将所有成员初始化为定义的值。您不应该对参数和成员进行相同的命名,这几乎总是会导致混淆,或者更可能导致错误

Node::Node(string key_val, int distance)
    : next(0)
{
    key = key_val;
    dist = distance;
}

更好的是,使用成员初始化

Node::Node(string key_val, int distance)
    : next(0),
      key(key_val),
      dist(distance)
{
}

正如评论者已经指出的那样,必须将setNext()中的next指针设置为给定的参数,并且应该而不是修改参数,而是this->next成员

void Node::setNext(Node * next_ptr){
    next = next_ptr;
}

在具有指针/动态内存分配的语言中,链表和其他数据结构的关键是为每个操作映射出所有可能的状态(情况)。您必须确保您的代码正确地处理每种情况下的指针和内存。这可能就是为什么你被要求实现它的原因:教你如何思考出现的陷阱。因此,我不会简单地给你一个直接的解决方案,而是概述你如何抓住这个机会来理解基本概念,这些概念将在未来帮助你和其他人解决这个问题。

即使是在一个带有尾部插入的stage1(非常基本的)链表中,也应该开发某种映射方案。我个人的经验是,对于main():中的每一行

  1. 为每个对象绘制一个方框
  2. 在框中列出所有数据变量
  3. 在非指针变量旁边列出任何初始化/赋值
  4. 从指针变量到您知道正在指向的对象绘制箭头
  5. 将箭头从指针变量绘制到未分配指针的空白区域

需要知道的一件事是,未初始化的数据和指针显示出未定义的属性,这些属性因操作系统/编译器而异,因此,用合适的初始值定义它们中的大多数通常是至关重要的。我发现,在声明时始终初始化为安全默认值对我很有帮助(在某些情况下,这是不可能的或会产生性能问题,但这些问题可以根据需要进行处理,例如懒惰初始化)。与Java不同,基本C++默认情况下不会引发空指针异常、提供初始值或垃圾收集(它高度依赖于编译器/库和传递的选项)。在最好的情况下,你的程序会出错(一个更普遍的例外,通常意味着你已经访问了不应该访问的内存),在更糟糕的情况下它要么仍然工作,但行为不可预测,要么在没有反馈的情况下崩溃。因此,您应该使用映射方案来验证是否没有对指向NULL的指针或指向已执行free/delete的对象的指针执行操作。此外,您还需要确保它们指向的位置是有意义的。与任何链接方案一样,您可能会遇到悖论,如将节点链接到自身或创建循环链接。

因此,如前所述,跟踪每次手术的所有可能病例也很重要。如果这个程序是一种学习数据结构或指针的方法,正如我所怀疑的那样,那么毫无疑问,您将被要求实现更高级的列表。更高级的列表可能很棘手,所以你应该尽早养成习惯,尝试确定执行特定操作可能需要格外小心(角落案例)。对于列表,您应该考虑列表为空、有1个元素、有2个元素或有2个以上元素的情况。您可能还需要考虑是否从开始、结束或两者之间的某个位置插入/删除元素。再次,广泛使用映射来了解这些情况将如何发展。你真的应该试着用前面提到的方法来思考这个问题,但你也可以查看维基百科或数据结构教科书来了解更多信息。

顺便说一句,正如Raymond提到的,你可以使用调试器来帮助你发现问题。事实上,如果您以前没有使用过C/C++调试器,那么现在正是学习的最佳时机。完成这项工作后,请查阅针对您的平台的C/C++调试器上的文档。尝试使用它逐步遍历非工作代码的main(),并查看数据值是否与映射中的期望值匹配。这种技能将在以后被证明是非常宝贵的

此外,请注意,this通常是C++中的指针,因此不要将其与Java/Python中的使用方式混淆:正如一些人所建议的,使用this->somedatanotthis.somedata访问成员数据。话虽如此,为了更清楚起见,您可能希望在参数名称与成员变量相同的地方使用this->somedata = somedata的约定。当然,使用不同参数名称的建议也同样有效。