I’m trying to create a basic ArrayList implementation in C++ for class. The course’s requirements are quite restrictive, and the data members and functions are required. However, when I run this, I get “Unable to access memory location”, when it tries to delete the list pointer in deepCopy. I can’t seem to figure out why. Please help. Thanks!
Edit:
I’ve narrowed the code down. I still want to give you enough information to be able to help, though.
Edit #2
US requested the full code, so I’ve added it back.
List.h
#ifndef LIST_H
#define LIST_H
/**
* This implentation of list is a pseudo ArrayList. Since we don't guarantee that a class has a hashCode in C++
* this will only work for objects which have an id field.
*
*/
template <class T>
class List{
private:
// If you want the max size of the list (100), then you should probably create a const. Example:
// const int MAX_SIZE;
// T[] list = new T[MAX_SIZE];
// But really, that is not much better. This is just bad, overall. It's like we're implementing a very poor ArrayList.
T * list; // This is our array for our elements.
int numberInList; //
int listSize; // Getter method will be getMaxSize. This seems unnecessary if we're initializing it to 100...
int nextIndex; // this is a number of the index for the next element. Really, this should be inside a private class (who has the pointer to its next).
double loadFactor; // We need to determine when we need to allocate additional slots so we don't run out if they try to add more than they have allocated.
void deepCopy(const List<T> & toBeCopied ); // We need a method to perform a deepCopy. Abstracts the implementation from each method. Private because we don't want it exposed to clients.
bool isAboveThreshold(); // Check if the list is above the loadFactor.
// Publicly available API
public:
List();
List(int size); // Overloaded constructor to initialize the List to a runtime size
List(const List<T> & toBeCopied); // Copy constructor
~List(); // Destructor -- Get rid of the dynamically allocated member.
T next(); // Gets the next element in the list, and increments next
bool add( T & element ); // Adds an element to the list. Also checks to make sure the element hasn't already been added.
bool contains(T & element); // Checks the list to see if the element exists in the list already.
int getSize(); // return the number of elements in the list.
List<T> & List<T>::operator =( const List<T> & toBeCopied );
};
template <class T>
List<T>::List(){
listSize = 100;
list = new T[listSize];
numberInList = 0;
nextIndex = 0; // Initialize the next element to 0.
loadFactor = 0.75;
};
template <class T>
List<T>::List(int size){
list = new T[size];
numberInList = 0;
nextIndex = 0; // Initialize the next element to 0.
listSize = size;
loadFactor = 0.75;
};
template <class T>
List<T>::List(const List<T> & toBeCopied){
deepCopy(toBeCopied);
};
/****
*
* We need to release the allocated heap memory. Non-pointer members will be deallocated when they are out of scope.
*
*/
template <class T>
List<T>::~List(){
delete [] list;
};
/**
* Return the number of elements in the list.
*/
template <class T>
int List<T>::getSize(){
return numberInList;
}
/**
* Return the number of elements in the list.
*/
template <class T>
T List<T>::next(){
return list[nextIndex++];
}
/**
* Check if the element is already in the list
*/
template <class T>
bool List<T>::contains( T & element){
// Now, to check if the item already exists, we could just iterate over the array, but that gives us linear execution time.
// It seems sub-optimal to work in linear time here, when it feels like we shouldn't have to, but honestly, I'm too tired
// to care at this point.
for(int i = 0; i < (numberInList); i++){
if(element != list[i]) // We do this so that if the first item is not equal, we don't even bother checking the second condition.
{
// The element isn't matched. We have to finish iterating, though, before we can add it.
}
else{ // The element matched. Return false.
element = list[i];
return true;
}
}
return false;
}
/**
* The implementation for this is very bad. But, the requirements in the homework documentation are very restrictive.
*
* Ideally, we would have a companion class named Entry which kept the reference to the element via composition.
*
* Obviously, this is a list, so we only want unique entries.
*
* if successful, we return a true. Else, we're returning false.
*
*/
template<class T>
bool List<T>::add( T & element ){
// If we've exceeded the loadFactor, we want to expand our array before we add.
if( isAboveThreshold() ){
int newSize = listSize*2;
List<T> tempPtr = List<T>(newSize);
for(int i = 0; i < numberInList; i++){
tempPtr.add(list[i]);
}
deepCopy(tempPtr);
}
if(!contains( element )){
list[numberInList] = element; // if there are 4 in the list, the next empty index is 4, so this works. We get our element, then post-increment.
numberInList++;
return true;
}
return false;
}
/**
* Deep copy mechanism
*/
template<class T>
void List<T>::deepCopy(const List<T> & toBeCopied){
// Take care of shallow copying first.
numberInList = toBeCopied.numberInList;
listSize = toBeCopied.listSize;
nextIndex = 0; // We're getting a new list, so our iterator should start over.
// Now, to initialize the new list
T *tempList = new T[listSize];
for(int i = 0; i < toBeCopied.numberInList; i++){
// We can do this because we're in the List class. We have access to private members.
tempList[i] = toBeCopied.list[i];
}
delete [] list;
list = tempList;
}
/**
* boolean for if we've exceeded the loadFactor threshold.
*/
template<class T>
bool List<T>::isAboveThreshold(){
if(numberInList != 0){
double division = (double)numberInList/listSize;
return (division >= loadFactor)? true : false;
}else
return false;
}
/***
* Overloaded assignment operator
*/
template <class T>
List<T> & List<T>::operator =( const List<T> & assigner ){
if(*this == &assigner)
return *this;
delete[] list;
deepCopy(assigner);
return *this;
}
#endif
Client.cpp
#include "List.h"
#include <string>
#include<iomanip>
#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;
struct Customer{
int id;
string name;
string city;
string address;
float amount;
Customer(){id=0; city="Default"; name="N/A"; address="N/A", amount = 0;}
bool operator==( const Customer & assign){
if(assign.id == id)
return true;
else
return false;
}
bool operator!=( const Customer & assign){
if(assign.id != id)
return true;
else
return false;
}
};
List<Customer> readCustomers();
void printCustomers();
int main(){
cout.setf(std::ios::fixed);
printCustomers();
return 0;
}
// Definitions
List<Customer> readCustomers(){
List<Customer> WebsterCommunications(50);
ifstream custFile;
custFile.open("Customers.csv");
// This could be abstracted out into another method, where we took the struct, a struct name, and the inFile, and spit back a
// customer from the file. But, for now, we'll just settle with the code duplication.
if(!custFile){
cout << "There was a problem reading the Customer File..." << endl;
exit(99);
}
while(!custFile.eof()){
Customer tempCust;
custFile>>tempCust.id;
if(tempCust.id == 0)
break;
custFile.ignore();
getline(custFile, tempCust.name, ',');
getline(custFile, tempCust.address, ',');
getline(custFile, tempCust.city, ',');
custFile>>tempCust.amount;
custFile.ignore();
WebsterCommunications.add(tempCust);
}
custFile.close();
return WebsterCommunications;
}
void printCustomers(){
List<Customer> customers = readCustomers();
double addCalc = 0.0;
cout << string( 100, '\n' );
for(int i = 0; i < customers.getSize(); i++){
Customer cust;
cust = customers.next();
cout << "id: " << cust.id << " name: " << cust.name << " address: " << cust.address << " balance: " << cust.amount << endl;
addCalc += cust.amount;
}
cout.precision(2);
cout << "average: " << (addCalc / customers.getSize()) << endl;
int isActive = 1;
cout << "Please enter a customer's id to find them (0 to exit):" << endl;
while(isActive){
cin >> isActive;
if(!isActive){
return;
}
Customer tempCust;
tempCust.id = isActive;
if(customers.contains(tempCust)){
cout << "id: " << tempCust.id << " name: " << tempCust.name << " address: " << tempCust.address << " balance: " << tempCust.amount << endl;
}
else{
cout << "That customer is not found" << endl;
}
cout << "Please enter a customer's id to find them (0 to exit):" << endl;
}
}
The problem was the copy constructor. At the point where it calls deepCopy, the list pointer has not had memory allocated for it. So, by initializing the pointer to an array of Customer structs, we eliminate the problem.