存储类对象的C++模板链表-未处理的异常访问冲突读取位置

C++ Template Linked List storing Class Objects - Unhandled Exception Access Violation Reading Location

本文关键字:异常 未处理 访问冲突 读取 位置 对象 C++ 存储 链表      更新时间:2023-10-16

我正在尝试将一个模板链表实现到我正在使用许多类进行的项目中。它的工作方式有点像一个有关键数据和价值数据的地图或集合。我创建了它,并在一个单独的项目中使用int和字符串值对其进行了测试,它内部的所有函数都可以工作,我甚至可以使用BOOST库对其进行序列化。

这是我列表的整个类文件:

#ifndef LINKED_LIST_H
#define LINKED_LIST_H
#include <boostserializationaccess.hpp>
#include <iostream>
#include <string>
using namespace std;
template<typename Tkey, typename Tvalue>
class Node
{
public:
Tkey keyData;
Tvalue valueData;
Node * next;
Node()
{   
next = NULL;
}
Node(Tkey x, Tvalue y)
{
keyData = x;
valueData = y;
next = NULL;
}
Node(Tkey x, Tvalue y, Node * z)
{
keyData = x;
valueData = y;
next = z;
}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & keyData;
ar & valueData;
ar & next;
}
};
template<typename Tkey, typename Tvalue>
class LinkedList
{
Node<Tkey, Tvalue> *head;
int count;
public:
LinkedList()
{
head = NULL;
count = 0;
}
~LinkedList()
{
Clear();
}
template<class Archive>
void serialize(Archive &ar, const unsigned int version)
{
ar & head;
ar & count;
}
int Size()
{
return count;
}
bool Empty()
{
return (count == 0);
}
bool Add(Tkey key, Tvalue value)
{
Node<Tkey, Tvalue> *p;
if(head == NULL)
{
head = new Node<Tkey, Tvalue> (key, value, NULL);
count++;
return true;
}
else
{
p = head;
while(p->next !=NULL)
p=p->next;
p->next = new Node<Tkey, Tvalue> (key, value, NULL);
count++;
return true;
}
return false;
}
bool Remove(Tkey key)
{
Node<Tkey, Tvalue> *p;
if (head->keyData == key)
{
head = head->next;
return true;
}
p=head;
while (p->next->keyData != key && p->next->next != NULL)
{
p = p->next;
}
if (p->next->keyData == key && p->next != NULL)
{
Node<Tkey, Tvalue> *temp;
temp = p->next;
p->next = temp->next;
count--;
delete temp;
return true;
}
return false;
}
Tvalue Get(Tkey key)
{
Node<Tkey, Tvalue> *p;
p = head;
while (p->next != NULL)
{
if (p->keyData == key)
{
return p->valueData;
}
p = p->next;
}
return NULL;
}
Tvalue Find(int i)
{
int selection = 0;
Node<Tkey, Tvalue> *p;
p = head;
while (selection != i && p->next != NULL)
{
p = p->next;
selection++;
}
if (p->next == NULL)
{
return 0;
}
return p->valueData;
}
Tkey Exists (Tvalue value)
{
Node<Tkey, Tvalue> *p;
p = head;
while (p->next != NULL)
{
if (p->valueData == value)
{
return p->keyData;
}
}
return NULL;
}
void Clear()
{
Node<Tkey,Tvalue> * temp;
while (head != 0)
{
temp = head;
head = head->next;
count--;
delete temp;
}
}
void Print(Tkey key)
{
Node<Tkey, Tvalue> * p;
p = head;
while(p->keyData != key && p->next != NULL)
{
p = p->next;
}
if (p->keyData == key && p->next != NULL)
{
cout << p->keyData << " " << p->valueData << endl;
}
else
{
cout << "Data not found" << endl;
}
}
void PrintAll()
{
Node<Tkey, Tvalue> * p;
p = head;
while(p != NULL)
{
cout << p->keyData << " " << p->valueData << "n";
p = p->next;
}
}
};
#endif

为了在我的项目中进行测试,我创建了一个带有int和一个类的链表变量,然后尝试添加并获取值数据来修改值。

LinkedList<int,Customer *> customerList;
customerList.Add(1, new Customer("Jack Smith", "1 Adelade Road", "5678", "Male", 4,8,1999));
customerList.Get(1)->AddAccount("9081", new JuniorCurrentAccount("100126764", 50, 25,12,2010));

但是,当我运行它时,在指向如果(head==NULL)

异常显示"访问违规读取位置">

到目前为止,我已经尝试在列表检查节点时将NULL更改为0s,但这并没有阻止异常。我还尝试包括类文件的头,但这导致了许多涉及访问这些类中的变量的错误。

我相信类本身有问题,但我找不到原因。正如我所说,这个类可以很好地处理基本数据类型(int、字符串等),但类是导致错误的原因。任何帮助都将是伟大的!

EDIT:这是Customer Class.cpp文件的代码
#include"Customer.h"#包括"Account.h"#包括"CorportateSavings.h"#包括"CurrentAccount.h"#包括"JuniorCurrentAccount.h"#包括"StudentSavings.h"#包括"Mortgage.h"#包括"BuyToLetMortgage.h"#包括"DiscountMortgage.h"#包括"FixedRatePortgage.h"#包括#包括#包括#包括#包括

using namespace std;
Customer::Customer(string Name, string Address, string TelephoneNo, string Gender, int DOBday, int DOBmonth, int DOByear) : name(Name), address(Address), telephoneNo(TelephoneNo), gender(Gender), DOB(DOBday,DOBmonth,DOByear)
{
parent = 0;
}
Customer::Customer(string Name, string Address, string TelephoneNo, string Gender, int DOBday, int DOBmonth, int DOByear, Customer *c) : name(Name), address(Address), telephoneNo(TelephoneNo), gender(Gender), DOB(DOBday,DOBmonth,DOByear)
{
parent = c;
}
Customer::~Customer(void)
{
}
void Customer::printDetails()
{
//Prints out details of customer
cout << "Name       : " << name << endl;
cout << "Address        : " << address << endl;
cout << "Telephone No   : " << telephoneNo << endl;
cout << "Gender     : " << gender << endl;
cout << "Date of Birth  : ";
printStringDOB();
//If customer has a registered parent/guardian
if (parent != 0)
cout << "Parent/Guardian Name: " << parent->getName() << endl;
cout << endl;
}
string Customer::getName()
{
return name;
}
void Customer::setName(string Name)
{
name = Name;
}
string Customer::getAddress()
{
return address;
}
void Customer::setAddress(string Address)
{
address = Address;
}
string Customer::getTelephoneNo()
{
return telephoneNo;
}
void Customer::setTelephoneNo(string TelephoneNo)
{
telephoneNo = TelephoneNo;
}
string Customer::getGender()
{
return gender;
}
void Customer::setGender(string Gender)
{
gender = Gender;
}
void Customer::printStringDOB()
{
DOB.printString(); 
}
double Customer::getDOB()
{
return DOB.juldays();
}
void Customer::setDOB(int day, int month, int year)
{
DOB.setDay(day);
DOB.setMonth(month);
DOB.setYear(year);
}
int Customer::getAge(int Day, int Month, int Year)
{
return DOB.DifferenceYear(Day, Month, Year);
}
void Customer::setParent(Customer * c)
{
parent = c;
}
Customer * Customer::getParent()
{
if (parent = 0)
{
return NULL;
}
else
{
return parent;
}
}
void Customer::AccountManager()
{
int choice = AccountManagerChooser();
cout << endl;
while (choice != 9)
{
switch (choice)
{
case 1:
//Create a bank acount
CreateAccountManager();
choice = AccountManagerChooser();
break;
case 2:
{
//Use a bank account
string pin;
cout << "Enter PIN: ";
cin >> pin;
UseAccount(pin);
choice = AccountManagerChooser();
break;
}
case 3:
{
//Remove a bank account
char yn;
cout << "Enter PIN: ";
string pin;
cin >> pin;
if (accountList.Get(pin) != NULL)
{
cout << "Are you sure? (Y/N) ";
cin >> yn;
if (yn == 'y' || yn == 'Y')
{
accountList.Remove(pin);
cout << "Account Removed Successfully" << endl;
}
}
else
{
cout << "Invalid PIN or Account does not exist" << endl;
}
choice = AccountManagerChooser();
break;
}
case 4:
//Print Customer Details
printDetails();
choice = 0;
break;
case 5:
{
//Change Customer Details
int detailChange = 0;
while (detailChange != -1)
{
cout << "1 Name" << endl;
cout << "2 Address" << endl;
cout << "3 Telephone No" << endl;
cout << "4 Gender" << endl;
cout << "5 Date of Birth" << endl;
cout << "Which piece of detail do you want to change? ";
cin >> detailChange;
string str;
getline(cin, str);
cout << endl;
switch (detailChange)
{
case 1:
{
string newName;
cout << "Enter new name: ";
getline(cin, newName);
setName(newName);
break;
}
case 2:
{
string newAddress;
cout << "Enter new Address: ";
getline (cin,newAddress);
setAddress(newAddress);
break;
}
case 3:
{
string newTelephone;
cout << "Enter new Telephone Number: ";
getline (cin,newTelephone);
setTelephoneNo(newTelephone);
break;
}
case 4:
{
string newGender;
cout << "Enter Gender" << endl;
getline (cin,newGender);
gender = newGender;
break;
}
case 5:
int newDay = 0;
int newMonth = 0;
int newYear = 0;
cout << "Enter Day: ";
cin >> newDay;
cout << "Enter Month: ";
cin >> newMonth;
cout << "Enter Year: ";
cin >> newYear;
setDOB(newDay,newMonth,newYear);
break;
}
char yn = 0;
cout << "Would you like to change anything else? (Y/N) ";
cin >> yn;
if (yn == 'Y' || yn == 'y')
{
detailChange = 0;
cout << endl;
}
else
{
detailChange = -1;
cout << endl;
}
}
choice = AccountManagerChooser();
cout << endl;
}
break;
case 6:
//Create a mortgage
{
time_t t = time(0);
struct tm * now = localtime( & t );
CreateMortgageManager();
choice = AccountManagerChooser();
cout << endl;
}
break;
case 7:
//Use a mortgage
{
string str;
string pin;
getline(cin,str);
cout << "Enter PIN: ";
getline(cin,pin);
UseMortgage(pin);
choice = AccountManagerChooser();
cout << endl;
}
break;
case 8:
//Remove a mortgage
{
string str;
string pin;
getline(cin,str);
cout << "Enter PIN: ";
getline(cin,pin);
RemoveMortgage(pin);
choice = AccountManagerChooser();
cout << endl;
}
break;
case 9:
break;
default:
cout << endl;
choice = AccountManagerChooser();
break;
}
if (choice == 9)
{
cout << "Thank you for your time!nn" << endl;
}
else if (choice == 0)
{
choice = AccountManagerChooser();
}
}
}
int Customer::AccountManagerChooser()
{
cout << "What would you like to do?" << endl;
cout << "1: Create a new account" << endl;
cout << "2: Use an account" << endl;
cout << "3: Close an account" << endl;
cout << "4: Show Personal Details" << endl;
cout << "5: Update Personal Details" << endl;
cout << "6: Create a Mortgage" << endl;
cout << "7: Use a Mortgage" << endl;
cout << "8: Remove a Mortgage" << endl;
cout << "9: Exit" << endl;
cout << "Enter Choice: ";
int choice;
cin >> choice;
cout << endl;
return choice;
}
void Customer::CreateAccountManager()
{
int choice = 0;
while (choice != -1)
{
time_t t = time(0);
struct tm * now = localtime( & t );
cout << "What account do you wish to create?" << endl;
cout << "1: Corporate Savings" << endl;
cout << "2: Current Account" << endl;
cout << "3: Junior Account" << endl;
cout << "4: Student Savings Account" << endl;
cout << "5: Cancel" << endl;
cout << "Enter Choice: ";
cin >> choice;
cout << endl;
switch (choice)
{
case 1:
//Corporate Savings Account Made
//Grabs age from current date
if (getAge(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900) >= 16)
{
cout << "Please enter how much you wish to deposit: ";
double initDeposit;
cin >> initDeposit;
cout << "Please enter a four digit number to set as the PIN: ";
string PIN;
cin >> PIN;
cout << "Please enter a 9 digit number to set as the account number: ";
string AccountNo;
cin >> AccountNo;
AddAccount(PIN, new CorporateSavings(AccountNo, initDeposit, now->tm_mday, now->tm_mon + 1, now->tm_year + 1900));
cout << "Corporate Savings Account Created" << endl;
choice = -1;
}
else if (getAge(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900) < 16)
{
cout << "Corportate Savings Account is only available to customers over the age of 16" << endl;
}
break;
case 2:
//Current Account Made
//Grabs age from current date
if (getAge(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900) >= 16)
{
cout << "Please enter how much you wish to deposit: ";
double initDeposit;
cin >> initDeposit;
cout << "Please enter a four digit number to set as the PIN: ";
string PIN;
cin >> PIN;
cout << "Please enter a 9 digit number to set as the account number: ";
string AccountNo;
cin >> AccountNo;
AddAccount(PIN, new CurrentAccount(AccountNo, initDeposit, now->tm_mday, now->tm_mon + 1, now->tm_year + 1900));
cout << "Current Account Created" << endl;
choice = -1;
}
else if (getAge(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900) < 16)
{
cout << "Current Account is only available to customers over the age of 16" << endl;
}
break;
case 3:
//Junior Current Account Made
//Grabs age from current date
if (getAge(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900) <= 16)
{
cout << "Please enter how much you wish to deposit: ";
double initDeposit;
cin >> initDeposit;
cout << "Please enter a four digit number to set as the PIN: ";
string PIN;
cin >> PIN;
cout << "Please enter a 9 digit number to set as the account number: ";
string AccountNo;
cin >> AccountNo;
AddAccount(PIN, new JuniorCurrentAccount(AccountNo, initDeposit, now->tm_mday, now->tm_mon + 1, now->tm_year + 1900));
cout << "Junior Account Created" << endl;
choice = -1;
}
else if (getAge(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900) > 16)
{
cout << "Junior Account is only available to customers under the age of 16" << endl;
}
else if (parent != 0)
{
cout << "Customers applying for Junior Account must have an adult registered as a guardian" << endl;
}
break;
case 4:
//Student Savings Account Made
//Grabs age from current date
if (getAge(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900) >= 18)
{
cout << "Please enter how much you wish to deposit: ";
double initDeposit;
cin >> initDeposit;
cout << "Please enter a four digit number to set as the PIN: ";
string PIN;
cin >> PIN;
cout << "Please enter a 9 digit number to set as the account number: ";
string AccountNo;
cin >> AccountNo;
AddAccount(PIN, new StudentSavings(AccountNo, initDeposit, now->tm_mday, now->tm_mon + 1, now->tm_year + 1900));
cout << "Student Account Created" << endl;
choice = -1;
}
else if (getAge(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900) < 18)
{
cout << "Student Account is only available to customers over the age of 18" << endl;
}
break;
case 5:
choice = -1;
break;
default:
cout << "Not a valid choice" << endl;
break;
}
}
}
void Customer::AddAccount(string PIN, Account * a)
{
accountList.Add(PIN, a);
}
bool Customer::UseAccount(string PIN)
{
if (accountList.Get(PIN) != NULL)
{
int choice = 0;
while (choice != -1)
{
cout << "What would you like to do?" << endl;
cout << "1: Deposit Cash" << endl;
cout << "2: Deposit Cheque" << endl;
cout << "3: Withdraw Cash" << endl;
cout << "4: Show Balance" << endl;
cout << "5: Exit" << endl;
cout << "Enter Choice: ";
cin >> choice;
cout << endl;
switch (choice)
{
case 1:
{
cout << "Enter Deposit Amount: ";
double amount;
cin >> amount;
if (accountList.Get(PIN)->depositAmount(amount))
{
time_t t = time(0);
struct tm * now = localtime( & t );
ofstream output;
output.open("Transactions.txt", ios::app);
output << "Name: " << name << " - Account Number: " << accountList.Get(PIN)->AccountNumber() << " - Deposit: £" << 
amount << " - Date: " << now->tm_mday << "/" << now->tm_mon + 1 << "/" << now->tm_year + 1900 << endl;
output.close();
cout << "Amount Deposited Successfully" << endl;
}
else
cout << "Deposit Invalid" << endl;
choice = 0;
}
break;
case 2:
{
time_t t = time(0);
struct tm * now = localtime( & t );
ofstream output;
output.open("Transactions.txt", ios::app);
output << "Name: " << name << " - Account Number: " << accountList.Get(PIN)->AccountNumber() << " - Cheque Deposit: £" 
<< " - Date: " << now->tm_mday << "/" << now->tm_mon + 1 << "/" << now->tm_year + 1900 << endl;
output.close();
cout << "Amount Deposited Successfully" << endl;
}
break;
case 3:
{
cout << "Enter Withdraw Amount: ";
double amount;
cin >> amount;
if (accountList.Get(PIN)->widthdrawAmount(amount))
{
time_t t = time(0);
struct tm * now = localtime( & t );
ofstream output;
output.open("Transactions.txt", ios::app);
output << "Name: " << name << " - Account Number: " << accountList.Get(PIN)->AccountNumber() << " - Withdrawal: £" << 
amount << " - Date: " << now->tm_mday << "/" << now->tm_mon + 1 << "/" << now->tm_year + 1900 << endl;
output.close();
cout << "Amount Withdrawn Successfully" << endl;
}
else
cout << "Withdraw Invalid" << endl;
choice = 0;
}
break;
case 4:
{
time_t t = time(0);
struct tm * now = localtime( & t );
accountList.Get(PIN)->addInterest(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900);
cout << "Balance: £" << accountList.Get(PIN)->Amount() << endl;
choice = 0;
}
break;
case 5:
choice = -1;
break;
default:
choice = 0;
break;
}
char yn;
cout << "Do you want to use another service? (Y/N) ";
cin >> yn;
if ((yn == 'y' || yn == 'Y') && choice != -1)
{
choice = 0;
}
else
{
choice = -1;
}
}
return true;
}
return false;
}
void Customer::RemoveAccount(string PIN)
{
char choice;
cout << "Are you sure? (Y/N): ";
cin >> choice;
cout << endl;
while (choice != 'y' || choice != 'Y' || choice != 'n' || choice != 'N')
{
cout << "Are you sure? (Y/N): ";
cin >> choice;
cout << endl;
}
accountList.Remove(PIN);
}
void Customer::CreateMortgageManager()
{
//Grabs age from current date
time_t t = time(0);
struct tm * now = localtime( & t );
if (getAge(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900) >= 18)
{
int choice = 0;
cout << "Please enter the value of your House/Apartment: ";
double value;
cin >> value;
cout << "Please enter the length of the payment (years) : 1, 2 or 4" << endl;
int length;
cin >> length;
cout << "Please enter the PIN number of the bank account you wish to pay installments to: ";
string accountPIN;
cin >> accountPIN;
if (accountList.Get(accountPIN) == NULL)
{
while (accountList.Get(accountPIN) == NULL)
{
cout << "Account PIN not valid or Account doesn't existn" << endl;         
cout << "Please enter the PIN number of the bank account you wish to pay installments to: ";
cin >> accountPIN;
}
}
while (choice != -1)
{
cout << "What kind of Mortgage do you wish to create?" << endl;
cout << "1: Fixed Rate Mortgage : " << ((value*0.8)+50)/(length * 12) << " per month with 5% Interest" << endl;
cout << "2: Discount Mortgage   : " << ((value*0.8)+80)/(length * 12) << " per month with 3.8% Interest (5% Interest after 2 years)" << endl;
cout << "3: Buy to Let Mortgage : "  << ((value*0.8)+120)/(length * 12) << " per month with 7.35% Interest" << endl;
cout << "4: Cancel" << endl;
cout << "Enter Choice: ";
cin >> choice;
cout << endl;
string str;
getline(cin,str);
switch (choice)
{
case 1:
{
cout << "Please enter a PIN, so you can access your mortgage securely: ";
string PIN;
getline(cin,PIN);
AddMortgage(PIN, new FixedRateMortgage(value, 50.0, accountList.Get(accountPIN), length, now->tm_mday, now->tm_mon + 1, now->tm_year + 1900));
cout << "Fixed Rate Mortgage Created" << endl;
choice = -1;
}
break;
case 2:
{
cout << "Please enter a PIN, so you can access your mortgage securely: ";
string PIN;
getline(cin,PIN);
AddMortgage(PIN, new DiscountMortgage(value, 45.0, accountList.Get(accountPIN), length, now->tm_mday, now->tm_mon + 1, now->tm_year + 1900));
cout << "Discount Mortgage Created" << endl;
choice = -1;
}
break;
case 3:
{
cout << "Please enter a PIN, so you can access your mortgage securely: ";
string PIN;
getline(cin,PIN);
AddMortgage(PIN, new BuyToLetMortgage(value, 70.0, accountList.Get(accountPIN), length, now->tm_mday, now->tm_mon + 1, now->tm_year + 1900));
cout << "Buy To Let Mortgage Created" << endl;
choice = -1;
}
break;
case 4:
choice = -1;
break;
default:
cout << "Not a valid choice" << endl;
break;
}
}
}
else
{
cout << "Mortgages are only available to customers over the age of 18" << endl;
}
}
void Customer::AddMortgage(string PIN, Mortgage * m)
{
mortgageList.Add(PIN, m);
}
void Customer::UseMortgage(string PIN)
{
if (mortgageList.Get(PIN) != NULL)
{
int choice = 0;
while (choice != -1)
{
cout << "What would you like to do?" << endl;
cout << "1: Pay Installment" << endl;
cout << "2: Print Payment List" << endl;
cout << "3: Exit" << endl;
cout << "Enter Choice: ";
cin >> choice;
cout << endl;
switch (choice)
{
case 1:
{
time_t t = time(0);
struct tm * now = localtime( & t );
mortgageList.Get(PIN)->PayInstallment(now->tm_mday, now->tm_mon + 1, now->tm_year + 1900);
choice = 0;
}
break;
case 2:
{
mortgageList.Get(PIN)->PrintPaymentLog();
}
break;
case 3:
choice = -1;
break;
default:
choice = 0;
break;
}
char yn;
cout << "Do you want to use another service? (Y/N) ";
cin >> yn;
if ((yn == 'y' || yn == 'Y') && choice != -1)
{
choice = 0;
}
else
{
choice = -1;
}
}
}
}
void Customer::RemoveMortgage(string PIN)
{
char choice;
cout << "Are you sure? (Y/N): ";
cin >> choice;
cout << endl;
while (choice != 'y' || choice != 'Y' || choice != 'n' || choice != 'N')
{
cout << "Are you sure? (Y/N): ";
cin >> choice;
cout << endl;
}
mortgageList.Remove(PIN);
}

如果您有一个1元素列表并调用Get,会发生什么?

Tvalue Get(Tkey key)
{
Node<Tkey, Tvalue> *p;
p = head;
while (p->next != NULL)
{
if (p->keyData == key)
{
return p->valueData;
}
p = p->next;
}
return NULL;
}

p不是NULL,但p->next是NULL,这意味着您永远不会进入while循环的主体。这意味着您将返回NULL,如果您随后尝试取消引用:

customerList.Get(1)->AddAccount("9081", new JuniorCurrentAccount("100126764", 50, 25,12,2010));

那么你可能会受到访问侵犯。