【问题标题】:C++ Unable to Print Pointer Data of a Linked ListC++ 无法打印链表的指针数据
【发布时间】:2015-06-09 00:11:23
【问题描述】:

我正在处理一个双向链表。它由类组成,并以当前节点为中心(而不是列表开头或结尾的节点)。现在我的打印函数会抛出一个错误,但前提是我已经遍历了列表。我的打印功能只是打印当前节点中的数据(如果它不为空)。这是我的打印功能:(在底部对我的文件层次结构和代码进行了更详细的描述)

void queue::print(){
    if (current){
        std::cout << std::endl << std::endl << "+++++++++++++++++++ Webpage +++++++++++++++++++" << std::endl
            << "URL: " << current->data.getURL() << std::endl
            << "-----------------------------------------------" << std::endl 
            << "Title: " << current->data.getTitle() << std::endl
            << "-----------------------------------------------" << std::endl
            << "Content: " << current->data.getContent() << std::endl
            << "+++++++++++++++++++++++++++++++++++++++++++++++" << std::endl << std::endl;
    }
    else{
        std::cout << std::endl << "Your not on a page. Please navigate to a page first." << std::endl;
    }

现在,如果我用两个数据节点填充列表,并执行我的打印功能,它会很好地打印节点中的数据。但是,如果我使用 goBack() 函数遍历到上一个节点:

void queue::goBack(){
    if (!current->previous){
        std::cout << "No previous page to go to!" << std::endl;
    }
    else{
        temp2 = current;
        current = current->previous;
    }
}

这会执行得很好,但是当我尝试在节点中打印数据时(使用相同的打印功能)我收到此错误:

Web Browser.exe 中 0x003E7926 处的未处理异常:0xC0000005:访问冲突读取位置 0xCDCDCE19。

Visual Studio 会打开一个没有扩展类型的文件,其中包含 C 代码,称为 xstring,其中有一个指向第 1754 行的中断箭头。

现在让我更详细地解释一下我的代码。我有五个文件:webQueue.h、webQueue.cpp、webPage.h、webPage.cpp 和 main.cpp。我列出的所有函数都在我的 webQueue.cpp 文件中。

这里是 webQueue.h:

#include "webPage.h"
class queue{
public:
    queue();
    void newPage(std::string u, std::string t, std::string c);
    void goForward();
    void goBack();
    void print();
private:
    struct Node{
        webPage data;
        Node* next;
        Node* previous;
    };
    Node* temp;
    Node* current;
    Node* temp2;
};

这里是 webPage.h:

#include <string>
class webPage{
public:
    webPage();
    webPage(std::string u, std::string t, std::string c);
    std::string getURL();
    void setURL(std::string u);
    std::string getTitle();
    void setTitle(std::string t);
    std::string getContent();
    void setContent(std::string c);
private:
    std::string URL;
    std::string title;
    std::string content;
};

我的 webQueue.cpp 文件包括:

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

我的 webPage.cpp 文件只包含 webPage.h,而我的 main.cpp 文件(包含我的执行函数)包括:

#include <iostream>
#include <string>
#include "webQueue.h"

虽然这些关系看起来有些复杂,但它们都应该是有效的。 cpp 文件以完全相同的名称链接到它们的头文件(前提是头文件存在),main.cpp 链接到 webQueue.h,webQueue.h 链接到 webPage.h。我看不出我的代码做错了什么——尽管这可能只是因为我很难理解指针是如何工作的。我想错误出现在我的 print()、goBack() 和 goForward() 函数的代码中(尽管在修复 goBack() 函数之前我无法测试我的 goForward() 函数)但我不能告诉你出了什么问题。

你们可以提供的任何帮助将不胜感激,因为我很难过。这是所有文件的 Dropbox 链接,您可以自己测试这个程序,看看我是否还有其他功能出错:https://www.dropbox.com/s/yekrz6dln1v9npk/webQueue.zip?dl=0

【问题讨论】:

  • 欢迎来到 Stack Overflow。我不能过分强调学习准备minimal complete example 的重要性。将您的代码缩减为仍然重现错误的最简单示例,并且错误可能很明显;如果不是,您可以发布一个独立的小型示例供我们查看。
  • @Beta 这就是我包含 Dropbox 下载的原因。
  • Visual Studio 有很酷的调试功能,比如你可以在程序遇到未处理的异常之前在一行设置断点。还有一个观察列表,因此您可以查看程序中变量的值。请在打印之前使用这些值来查看这些值是否符合您的预期。
  • 您没有阅读帮助文件。 整个代码库zip文件链接并不是一个最小的完整示例。我不会尝试筛选您的文件。
  • 此外,由于 Visual Studio 非常友好地停下来并指出错误,因此它还提供堆栈跟踪的可能性非常大,因此您可以看到程序在哪里调用了启动程序的函数在走向灭亡的路上。通常,此堆栈跟踪将允许您查看进入调用的变量。它们可能已被崩溃破坏,因此 @KompjoeFriek 的建议成立,但堆栈跟踪会告诉您放置断点,以便您可以充分利用它。

标签: c++ linked-list nodes


【解决方案1】:

您的Node 管理有误。

一个小问题是queue::goBack()queue::goForward() 在访问其字段之前没有检查current 是否为空,因此如果用户在选择之前选择“返回”或“前进”,您的代码将会崩溃到“转到网页”(至少print() 正在检查是否为空)。所以添加这些检查,甚至可能更新printMenu() 以在队列没有可用的当前页面时甚至不输出这些选项。

但更重要的是,您的 queue::newPage() 实现完全损坏。当current 不为空时,您将该节点的next 成员设置为指向自身,而不是您创建的新节点,并且您没有设置新节点的previous 字段根本没有,更不用说指向要插入它的现有节点了。

您还应该删除 queue 类的 temptemp2 成员。他们一开始就不属于那里。它们只在queue::newPage() 内部有用(queue::goBack() 根本不需要使用temp,就像goForward()),所以它们应该只在queue::newPage() 内部更改为局部变量。更好的是,它们可以完全删除,因为 queue::newPage() 可以在完全不使用它们的情况下实现。

您的 queue 实现应该看起来更像这样(这甚至不包括复制/移动语义 - 请参阅 rule of three/five/zero):

#include "webPage.h"

class queue {
public:
    queue();
    void newPage(std::string u, std::string t, std::string c);
    void goForward();
    void goBack();
    void print();
private:
    struct Node {
        webPage data;
        Node* next;
        Node* previous;
    };
    Node* current;
};

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

queue::queue() {
    current = nullptr;
}

void queue::newPage(std::string u, std::string t, std::string c) {
    Node* n = new Node;
    n->data = webPage(u, t, c);
    n->next = nullptr;
    n->previous = nullptr;

    if (current) {
        if (current->next) {
            n->next = current->next;
            current->next->previous = n;
        }
        n->previous = current;
        current->next = n;
    }

    current = n;
}

void queue::goBack() {
    if ((current) && (current->previous)) {
        current = current->previous;
    }
    else {
        std::cout << "No previous page to go to!" << std::endl;
    }
}

void queue::goForward() {
    if ((current) && (current->next)) {
        current = current->next;
    }
    else {
        std::cout << "No next page to go to!" << std::endl;
    }
}

void queue::print() {
    if (current) {
        std::cout << std::endl << std::endl
            << "+++++++++++++++++++ Webpage +++++++++++++++++++" << std::endl
            << "URL: " << current->data.getURL() << std::endl
            << "-----------------------------------------------" << std::endl 
            << "Title: " << current->data.getTitle() << std::endl
            << "-----------------------------------------------" << std::endl
            << "Content: " << current->data.getContent() << std::endl
            << "+++++++++++++++++++++++++++++++++++++++++++++++" << std::endl << std::endl;
    }
    else {
        std::cout << std::endl << "You are not on a page. Please navigate to a page first." << std::endl;
    }
}

然后,当您使用它时,您应该重写 queue 以完全停止使用手动节点管理并改用标准的 std::list 类:

#include "webPage.h"
#include <list>

class queue {
public:
    void newPage(std::string u, std::string t, std::string c);
    void goForward();
    void goBack();
    void print();
private:
    std::list<webPage> data;
    std::list<webPage>::iterator current;
};

#include "webQueue.h"
#include <iostream>
#include <iterator>

void queue::newPage(std::string u, std::string t, std::string c) {
    webPage p(u, t, c);
    if (data.empty()) {
        data.push_back(p);
        current = data.begin();
    }
    else {
        current = data.insert(std::next(current), p);
    }
}

void queue::goBack() {
    if ((!data.empty()) && (current != data.begin()))
        current = std::prev(current);
    else
        std::cout << "No previous page to go to!" << std::endl;
}

void queue::goForward() {
    if (!data.empty()) {
        std::list<webPage>::iterator iter = std::next(current);
        if (iter != data.end()) {
            current = iter;
            return;
        }
    }
    std::cout << "No next page to go to!" << std::endl;
}

void queue::print() {
    if (!data.empty()) {
        std::cout << std::endl << std::endl
            << "+++++++++++++++++++ Webpage +++++++++++++++++++" << std::endl
            << "URL: " << current->data.getURL() << std::endl
            << "-----------------------------------------------" << std::endl 
            << "Title: " << current->data.getTitle() << std::endl
            << "-----------------------------------------------" << std::endl
            << "Content: " << current->data.getContent() << std::endl
            << "+++++++++++++++++++++++++++++++++++++++++++++++" << std::endl << std::endl;
    }
    else {
        std::cout << std::endl << "You are not on a page. Please navigate to a page first." << std::endl;
    }
}

【讨论】:

  • 谢谢!这修复了我的代码,但为什么标准列表更好?
  • 我也对这行代码有点困惑:current-&gt;next-&gt;previous = n;。这是否相当于current-&gt;next = n;current-&gt;previous = n;?很抱歉打扰您,我五个月前才开始学习 C++。
  • std::list 类更好用,因为它是 C++ STL 规范中定义的 标准化 类,因此它可以移植到所有 C++ 编译器,它具有可预测性行为,处理正确节点管理的所有脏细节等。不,current-&gt;next-&gt;previous = n;current-&gt;next = n; current-&gt;previous = n; 不同。仔细看看这些陈述在做什么......
  • current-&gt;next-&gt;previous = n; - 插入新节点时在两个现有节点之间,列表中的下一个节点需要更新为指向现在将在列表中位于它之前的新节点,并且新节点需要指向同一个 existing 节点,因为它现在将跟随列表中的新节点(n-&gt;next = current-&gt;next; 语句)。
  • current-&gt;next = n; current-&gt;previous = n; - 将当前节点设置为指向您自己两侧的新节点。这在任何情况下都是完全错误的(一个节点怎么可能同时存在于列表的两个位置!),并且忽略更新列表中的 next 节点以指向其新的前一个节点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-30
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
  • 2017-03-09
  • 1970-01-01
相关资源
最近更新 更多