0

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

我的 node.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;
}

任何帮助将不胜感激。谢谢。

4

2 回答 2

2

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

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)
{
}

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

void Node::setNext(Node * next_ptr){
    next = next_ptr;
}
于 2013-02-14T18:49:25.447 回答
0

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

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

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

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

因此,如前所述,跟踪每个操作的所有可能情况也很重要。如果这个程序是一种学习数据结构或指针的方法,正如我所怀疑的那样,你无疑会被要求实现更高级的列表。更高级的列表可能很棘手,因此您应该尽早养成尝试确定执行特定操作可能需要额外注意的习惯(极端情况)。对于列表,您应该考虑列表为空、有 1 个元素、有 2 个元素或 2+ 个元素的情况。您可能还需要考虑是否从开头、结尾或中间的某个位置插入/删除元素。再次,广泛使用映射来了解这些情况将如何发挥作用。您确实应该尝试使用上述方法来考虑这一点,但您也可以检查维基百科或数据结构教科书以获取更多信息。

顺便说一句,正如雷蒙德所说,您可以使用调试器来帮助您查看问题。事实上,如果您以前没有使用过 C/C++ 调试器,那么这是学习的最佳时机。完成这项工作后,请在适用于您的平台的 C/C++ 调试器上查找文档。尝试使用它来单步执行main()您的非工作代码,并查看数据值是否与映射中的预期匹配。这种技能将在以后证明是无价的。

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

于 2013-02-16T17:57:42.790 回答