我的程序在将节点读入链接列表后连续循环,并且仅重复显示一个节点

问题描述 投票:0回答:2

我在C ++程序中创建了一个链表,该链表将节点写入二进制文件,然后将它们读回链表。但是,当显示回列表时,程序仅连续显示一个节点(连续循环)。

这里是代码:

 #ifndef ACCOUNT_H
    #define ACCOUNT_H

    class Account{

private:
    double balance;
    unsigned int accountNumber;
    Account *next;
public:
    Account(){
        balance=0.0;
        accountNumber=0;
        next=NULL;
    }// primary constructor

    Account(unsigned int acc, double bal){
        accountNumber=acc;
        balance=bal;
        next=NULL;
    }// primary constructor

    Account::~Account(){
        //delete next;
    }// destructor


    void setBalance(double b){
        balance=b;
    }// end of setBalance


    double getBalance(){
        return balance;
    }

    void setAcc(unsigned int acc){
        accountNumber=acc;
    }

    unsigned int getAccNum(){
        return accountNumber;
    }

    //  links
    void setNextAccount(Account *nAcc){
        next=nAcc;
        //delete nAcc;
    }// Mutator for next

    Account *getNextAccount(){
        //Account *temp=next;
        return next;
    }
};
#endif

链接列表头文件:

#ifndef ACCOUNTLIST_H
#define ACCOUNTLIST_H

class AccountList{
private :
    Account *head;
    //Account * temp;
    //Account *current;
public:
    AccountList(){
        head=NULL;
        //temp=NULL;
        //current=NULL;
        //current=NULL;
    }// end of default constructor

    AccountList::~AccountList(){
        /*Account *temp;
        while (head!= NULL)
        {
            temp = head;
            head = head->getNextAccount();
            delete temp;
        } */
        delete head;
        //delete current;
    }// destructor for list

    void addNode(Account *h){
        //Account *temp;
        Account *current;
        //temp=h;
        //temp->setNextAccount(NULL);
        if(head==NULL){
            head=h;
        }
        else{
            current=head;
            while((current->getNextAccount())!=NULL){
                current= current->getNextAccount();
            }
            current->setNextAccount(h);
        }
        //delete current;
    }// mutator for head

    void displayAll(){
        Account *temp=head;
        while ((temp!=NULL) && (temp->getAccNum()!=0)){         
            cout << "Account number: " <<temp->getAccNum() << " has a balnce of: " << temp->getBalance() <<endl;
            temp=temp->getNextAccount();
        }

        delete temp;
    }

    void displayNode(int id){
        Account *temp= head;
        while (temp !=NULL){

            if (temp->getAccNum()==id){
                //temp->display();
                cout << "Account Number : " << temp->getAccNum() <<"has a balance of " << temp->getBalance() <<endl;
                delete temp;
                return;
            }
            temp= temp->getNextAccount();// gets next node in the list
        }
        cout << "Employer was not found" << endl;
    }// end of displayNode


    Account* getHead(){
        return head;
    }

    void removeAll(){
        Account *temp;
        while (head!=NULL){
            temp=head;
            head= head->getNextAccount();
            delete temp;
        }
    }// end of method removeAll

};

#endif

主驱动程序:

#include <iostream>
#include <fstream>
#include "Account.h"
#Include "AccountList"
//#include <cstdlib>
#include <stdlib.h>
using namespace std;

void main(){
    Account *acc1=new Account(1, 546.34); // create object and initialize attributes 
    Account *acc2=new Account(2,7896.34);
    Account *acc3=new Account();
    AccountList *list1= new AccountList();
    AccountList *list2= new AccountList();

    // add nodes to linked list
    list1->addNode(acc1);
    list1->addNode(acc2);
    //cout <<"Hello"<<endl;

    // file operation
    ofstream outAccount("account.dat", ios::out|ios::binary);

    // checks if ofstream could open file
    if(!outAccount){
        cerr<<"File could not be open" << endl;
        exit(1);
    }   
    acc3=list1->getHead();
    while( acc3!=NULL){
        outAccount.write(reinterpret_cast < const char*>(&acc3), sizeof(Account));
        acc3=acc3->getNextAccount();
        //outAccount.write(reinterpret_cast < const char*>(&acc2), `sizeof(Account));`
    }
    //cout <<"Hello"<<endl;
        outAccount.close();


    // read and display contents of file
    ifstream inAccount("account.dat", ios::in|ios::binary);
    if(!inAccount){
        cerr <<"File could not be openned for reading from file" << endl;
        exit(1);
    }
    //Account *accTemp=new Account();
    while(inAccount && !inAccount.eof()){

        inAccount.read(reinterpret_cast < char* >(&acc3), sizeof(Account));
        list2->addNode(acc3);

        //cout <<"Account Number : " << acc3->getAccNum()<< "has a balance of: " `<< acc3->getBalance() <<endl;`
    }

    inAccount.close();
    cout <<"Hello"<<endl;

    list2->displayAll();
    system("PAUSE");

    system("PAUSE");
}// end of main
c++ linked-list binaryfiles
2个回答
4
投票

您的代码有很多问题,但是我将尝试全部解决这些问题,而不仅仅是简单地转储经过修订的代码清单,希望您可以根据我的描述解决这些问题。

首先,不需要将链接列表“ wheel”改写为std :: list,它将为您提供与AccountList类一起尝试做的所有事情,但是要做到这一点,您必须熟悉迭代器。迭代器与指针本质上是相同的东西,但是我承认它们可能会使具有C背景的人感到困惑。无论如何,其余的讨论都假定您继续使用AccountList类,而不是使用std :: list。

第二,在Account和AccountList构造函数中,应该使用初始化程序列表来初始化成员变量,因为有关变量的任何事情都不需要任何计算或复杂的逻辑。例如。执行此操作:

Account()
 : balance(0.0), accountNumber(0), next(NULL) {}

现在是实际的错误:

当您执行outAccount.write()时,您正在编写acc3的全部内容,包括其指针。这些指针仅在那一刻才有效,并且仅对“ list1”有效。下次您运行程序时,系统可能在这些地址上分配了其他内容,并且这些指针将不包含“ Account”对象,与以前的对象相比要少得多。向下移动到inAccount.read()时要理解这一点很重要,在该处您正在读取Account对象的旧内容以及旧的指针值。此时,这些地址不再有效,或者至少不适用于“ list2”。当您调用list2-> addNode(acc3)时,实现这一点很重要。现在来看一下AccountList :: addNode()。您传入的Account对象“ h”仍在其“ next”字段中包含该旧指针值。读取Account对象的代码中没有任何内容将其“ next”设置为NULL,addNode()也没有设置。如果您不选择使用std :: list <>,我建议您先调用h-> setNextAccount(NULL)使addNode()开始,然后再执行其他操作。这解决了过时的指针错误。

在继续下一个错误之前,我想提到AccountList :: addNode()是时间复杂度O(n)。随着AccountList大小的增加,扫描到列表末尾的时间将越来越长,以便简单地将下一个Account对象添加到列表中。您可以做得更好:如果您坚持要在AccountList的末尾添加下一个Account,则在AccountList中不仅要维护指向头的指针,还要维护指向尾节点的指针。这样,addNode()将成为时间复杂度O(1)。或者,将新的Account对象添加到列表的开头:将新对象的“ next”设置为当前标题,然后将“ head”更改为新添加的Account(这种方式大大简化了添加新Account对象的逻辑到列表中)。请注意,尽管根据帐户号查找帐户仍然会是O(n)的顺序,并且如果您的软件正在处理数千个帐户,则查找帐户的性能会非常差。如果使用std :: map,则可以获得O(log n)帐户查找时间。在那种情况下,您的“ addNode”时间也将是O(log n),比O(1)还差,但是您将做更多的事情,添加帐户还是查找现有帐户?可能正在查找现有帐户。

[确定,现在是下一个错误:在main()中,当您第一次检查inAccount.read()循环的退出条件时,甚至没有读入第一条记录。我猜那儿没什么大不了的,但是当您最后读完最后一个书时,inAccount.eof()尚不正确,因此您再次输入了循环主体,但是这次inAccount.read()不读取任何内容。因此,acc3自上次迭代以来未发生变化。然后,即使您没有阅读任何内容,仍然可以调用list2-> addNode(acc3)。您将最后一条记录添加了两次!我会让您找出解决此问题的方法。

最后,您正在进行一些非常可怕的内存/对象管理。请注意,您永远不会在main()中删除两个动态分配的AccountList。如果您打算在程序退出时由系统自动收回AccountList,那么这并不是什么大问题,但是如果您打算在程序的生命周期中不时出现多个AccountList,则需要确保他们干净地删除其内容。似乎您希望AccountList对象拥有分配给它们的Account对象,因为在〜AccountList()析构函数中,您删除了“头” Account对象。由于〜Account()析构函数不会删除“下一个”对象,因此列表中唯一要删除的Account对象是主Account。由于AccountList仅具有指向head对象的指针,因此删除列表中其余部分的有效位置可能在〜Account()中,正如我看到的那样,您考虑过基于注释掉的delete调用进行操作。如您所想,您可以让每个Account对象仅删除其自己的“ next”,但是这种方法存在一个细微的错误:每次后续删除都会向运行时堆栈添加另一个函数调用,并在直到到达列表末尾。如果您有成千上万个Account对象,则可以确保多次浪费运行时堆栈。更好的方法如下:

~Account(){
    Account *victim, *itsNext = next;
    while (itsNext){
        victim = itsNext;
        itsNext = victim->next;
        victim->next = NULL; // prevent recursion & blown stack
        delete victim;
    }
    next = NULL;
}

嗯...我看到您有一个AccountList :: removeAll()方法,其结构与上述〜Account()非常相似。也许您可以从〜AccountList()析构函数中调用它。

现在,如果您决定使用std :: list或std :: map(我建议使用map),则您不必担心内存泄漏或删除方式/时间,只要您的std :: list或std :: map是实际Account对象的列表或映射,因为当删除列表/映射条目时(通过用delete()或整个列表/映射删除时),包含的Account对象将被正确删除随之而来。如果它们是“帐户”指针的列表或映射,则仍然需要遍历并手动删除它们。除非您使用了auto_ptrs或smart_ptrs的列表,否则我就离题了。


0
投票

这里有一些问题,可能最严重的是您尝试将链接列表写入文件的方式。

您不能将指针成员写入文件,并且在回读文件时,它们期望相同的内存地址包含相同的对象。您可能应该看一下boost serialization libraryGoogle's Protocol Buffers之类的东西。 。

部分原因是程序挂起;当您调用list2->addNode(acc3);时,acc3已经具有next的值,因此while中的addNode循环永不返回。

此外,在这里检查!inAccount.eof()是没有用的,因为在尝试第三次读取之前,您不会点击eof。第三次读取失败(嗯,它读取0个字节),但是您仍然调用addNode。您可以使用inAccount.gcount()检查读取了多少。如果您在读取后立即执行此操作,并且如果不是预期的数量,那么break就会跳出循环,您可以避免进行额外的读取尝试。

© www.soinside.com 2019 - 2024. All rights reserved.