我在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
您的代码有很多问题,但是我将尝试全部解决这些问题,而不仅仅是简单地转储经过修订的代码清单,希望您可以根据我的描述解决这些问题。
首先,不需要将链接列表“ 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的列表,否则我就离题了。
这里有一些问题,可能最严重的是您尝试将链接列表写入文件的方式。
您不能将指针成员写入文件,并且在回读文件时,它们期望相同的内存地址包含相同的对象。您可能应该看一下boost serialization library或Google's Protocol Buffers之类的东西。 。
部分原因是程序挂起;当您调用list2->addNode(acc3);
时,acc3
已经具有next
的值,因此while
中的addNode
循环永不返回。
此外,在这里检查!inAccount.eof()
是没有用的,因为在尝试第三次读取之前,您不会点击eof。第三次读取失败(嗯,它读取0个字节),但是您仍然调用addNode
。您可以使用inAccount.gcount()
检查读取了多少。如果您在读取后立即执行此操作,并且如果不是预期的数量,那么break
就会跳出循环,您可以避免进行额外的读取尝试。