c++ Linked List losing data between functions - c++

I am having a problem with pointers and scope. I am trying to maintain an array of linked lists of pointers to objects. When I try to push_front() in one function, it works. However, if I try to iterate through the list in another part of my program, the list no longer contains any data, and my pointers are bad.
This is part of my parseCommands function. the problem is when printList is called:
Administrator *adminPtr = new Administrator(); // create new Administrator pointer
//local variables...
string adminName; //administrator's name
int adminMNum; //administrator's M Number
string adminEmail; //administrator's email address
string adminTitle; // administrator's title
// read in & store data for new administrator
inData >> adminName; //read in data
adminPtr->setName(adminName); //set admin name
inData >> adminMNum;
adminPtr->setMNum(adminMNum); // set admin M Number
inData >> adminEmail;
adminPtr->setEmail(adminEmail); // set admin email address
inData >> adminTitle;
adminPtr->setTitle(adminTitle); //set admin title
// finished storing new administrator info
// add Administrator to list
cout << "Adding Administrator: " << endl;
cout << "in records office adminPtr/newPerson: " << adminPtr << endl;
universityList.addPerson(adminPtr); // call addPerson--hashTable
//universityList.printPerson(adminPtr); // print admin info using polymorphic method
//cout << "The load factor (alpha) is: " << universityList.getLength()/universityList.getMaxTableSize() << endl; // print alpha
universityList.printList(adminPtr->getMNum()); // print all items at table[loc]--breaks here
cout << endl;
The addPerson function where printList works fine:
template <typename T>
void HashTable<T>::addPerson(T newPerson) { //newPerson is a pointer to a person object
int loc; // array location provided by hashFunction
cout << "in hashtable newPerson: " << newPerson << endl;
loc = hashFunction(newPerson->getMNum()); // get loc
table[loc].push_front(&newPerson); // add to list at table[loc] passing address of pointer to person
printList(newPerson->getMNum()); // print all items at table[loc]--works here
size++; // increment size
} //end function
The printList function that works when called in addPerson, but not in parseCommands:
template <typename T>
void HashTable<T>::printList(searchKeyType key) { //print list of items held in array location
int loc = hashFunction(key); // get array location
if (table[loc].empty()) { // if list is empty
cout << "Can not print person M" << key << " NOT found" << endl << endl;
} //end if empty
else{
list<T*>::iterator iter; // stl iterator
iter = table[loc].begin();
cout << "in printList table[loc]begin " << *iter << endl; //print address of table[loc]begin.()--where iter points
cout << "in printList item in table[loc]begin " << **iter << endl; // print address of the pointer that iter points to
while(iter != table[loc].end()) { // for each item in the list
(**iter)->print(); // print person info using polymorphic method
++iter;
} //end for
} // end else
} // end printList
The print function:
void Administrator::print()const {
// print Administrator info
cout << " " << "Full Name: " << getName() << endl;
cout << " " << "M Number : "<< getMNum() << endl;
cout << " " << "Email Addr: " << getEmail() << endl;
cout << " " << "Title: " << getTitle() << endl;
}; // end print function
The hashTable class:
template<typename T>
class HashTable{
public:
HashTable(); // constructor
bool isEmpty()const; //determines if the hash table is empty
int getLength() const; // returns (size) number of Persons in table (accessor)
int getMaxTableSize() const; // returns tableSize (size of array)
void addPerson(T person); // adds new Person
void removePerson(searchKeyType key); // deletes Person from the HashTable
void printPerson(T person); // prints Person info
T getNodeItem(int mNumber); //returns person object (accessor)
void printList(searchKeyType key); //print list of items held in array location
private:
int size; // number of Persons in table
static const int tableSize = 1; // number of buckets/array size -- planning on using 70001--assuming 35,000 entries at once; largest prime > 2*35000
list <T*> table[tableSize]; // array of STL lists for chains
int hashFunction(searchKeyType searchKey); // hash function to return location (array index) of item
}; //end HashTable class
I pass adminPtr to addPerson, and it seems to add it to the list. Why am I losing the data when I return to the parseCommands function? Is it a stack vs. heap issue? Do I need "new" somewhere? There are a few extra lines in there where I was printing out the address of the pointers trying to figure out what's going on.
This was a programming problem for a class that I was unable to resolve. We had to simulate a hash table using an array of STL linked lists. We were not allowed to use vectors, maps, etc. The program involves an abstract base class (Person) with derived classes (Administrator, etc.) and a templated hash table class. There is one more class (RecordsOffice) that holds the hash table.
class RecordsOffice {
public:
RecordsOffice(); // default constructor
void parseCommands(string fileName); // function to parse commands from a file to maintain the StudentList
private:
HashTable <Person*> universityList; // creates empty hashtable
};

The problem is in these two places.
universityList.addPerson(adminPtr);
//...
You are passing a copy of adminPtr.
template <typename T>
void HashTable<T>::addPerson(T newPerson) { //newPerson is a pointer to a person object
// ...
table[loc].push_front(&newPerson); // add to list at table[loc] passing address of pointer to person
// ....
}
newPerson is a local variable to addPerson. When it returns it is no more valid. But you are adding it´s address in to the table.
the issue is that
list <T*> table[tableSize];
is storing pointers to pointers of Person.
I don't think passing by reference would solve the problem too. Because then you will be dependent on the automatically created pointer here.
Administrator *adminPtr = new Administrator();
What adminPtr pointer points to will stay but not adminPtr itself. So you can not depend on its address (unless you are sstill in the same function that created it). One possible way to solve it would be to allocate adminPtr itself dynamically.
Administrator **adminPtr = new Administrator*;
adminPtr = new Administrator();
But maybe you should revise the requirements.

Your table is declared like this:
list <T*> table[tableSize];
That means any pointers it contains need to be dynamically allocated, or need to remain in scope for the entire lifetime of the container. This is not the case. In your function addPerson you add the address of a local variable:
table[loc].push_front(&newPerson);
You should do one of the following:
Change the table to an array of list<T> objects.
Copy the data dynamically. eg table[loc].push_front(new T(newPerson))
Because this is a list, I would go for option 1 because the list will copy locally anyway, and you won't have to clean up the pointers later. A third option would be to use list<unique_ptr<T> > or similar.

Related

Class that holds objects of another class within an std::array unexpectedly goes into constructor of the subclass within the array

We are tasked to create a class based Todo-List. I have created a Class called Task and another one called List. List has an std::array as a member which contains objects of type Task. List has a member function that creates a task and one that prints the entire list.
The problem I'm facing is that the std::array<Task,3> declaration automatically calls the constructor of Task 3 times and writes the tasks into the array. This is less of a problem when using an array and saves me the createTask() function which I no longer need to call, but if i want to use a Std::vector to hold a variable amount of tasks i always get an error "vector subscript out of range". Additionally I only want to add a task when I want to, not until whatever container type is full. How do I go about tackling this problem?
class Task
{
private:
std::array<std::string, 3> m_list;
std::string m_completed{ "no" };
public:
Task()
{
std::cout << "Enter Task Name: ";std::cin >> m_list[0]; //cant do white spaces yet
m_list[1] = "23.04.2021"; //needs way of getting current date
m_list[2] = m_completed;
system("CLS");
}
void printTask()const
{
std::cout << "Task Name: " << m_list[0] << " " << "created on: " << m_list[1] << " " << "completed?: " << m_list[2] << std::endl;
std::cout << "------------------------------------------------------------------------------------------------------------------------" << '\n';
}
};
class List{
private:
std::array<Task,3> listOfTasks; //this automatically calls the constructor 3 times and writes the objects into the array
int m_vectorsize{ 0 };
public:
List() {};
void createTask()
{
Task t1;
listOfTasks[m_vectorsize] = t1;
++m_vectorsize;
}
void printTasklist()
{
for (int count{ 0 }; count <3;count++)
{
listOfTasks[count].printTask();
}
}
};

Add an element to a vector inside a struct, from within another struct

This is most probably trivial and I'm confusing struct allocation and pointers somehow, I apologize for this. I have read answers to similar questions but it didn't help. The code is, as always, way more complicted, this is a reduction from 3000+ lines of code to the gist.
The output I expected was
prep 1
main 1
Instead, I get
prep 1
main 0
This is the code:
#include <iostream>
#include <vector>
using namespace std;
struct Entry
{
vector<int> list;
};
struct Registry
{
vector<Entry> entries;
void prep()
{
Entry* entry = new Entry();
entries.push_back(*entry);
entry->list.push_back(0);
cout << "prep " << entry->list.size() << "\n";
}
};
int main()
{
Registry registry;
registry.prep();
cout << "main " << registry.entries[0].list.size() << "\n";
return 1;
}
You don't store pointers in your vector<Entry> so you should not use new. Instead add a default constructed Entry using emplace_back.
A C++17 approach:
void prep()
{
Entry& entry = entries.emplace_back(); // returns a ref the added element
entry.list.push_back(0);
cout << "prep " << entry.list.size() << "\n";
}
Prior to C++17:
void prep()
{
entries.emplace_back(); // does NOT return a ref the added element
Entry& entry = entries.back(); // obtain a ref to the added element
entry.list.push_back(0);
cout << "prep " << entry.list.size() << "\n";
}
If you do want to create and maniplate your Entry before adding it to entries, you can do that too and then std::move it into entries.
void prep()
{
Entry entry;
entry.list.push_back(0);
cout << "prep " << entry.list.size() << "\n";
entries.push_back(std::move(entry)); // moving is a lot cheaper than copying
}
The problem is the order of the prep() function. If you change to push an element into the Element object, and then push it tho the entries vector, the behavior will be the expected.
void prep()
{
Entry* entry = new Entry();
entry->list.push_back(0);
entries.push_back(*entry);
cout << "prep " << entry->list.size() << "\n";
}
This is happening, because you uses a copy in the entries list.
It is also possible to store the pointer of the object therefore you can edit the actual instance after you pushed to the entries vector.
Edit:
As Ted mentioned, there is a memory leak, because the entry created with the new operator never deleted. Another approach could be to use smart pointers (however, in this small example it seems overkill, just use reference)
void prep()
{
std::unique_ptr<Entry> entry = std::make_unique<Entry>();
entry->list.push_back(0);
entries.push_back(*entry.get()); // get the pointer behind unique_ptr, then dereference it
cout << "prep " << entry->list.size() << "\n";
} // unique_ptr freed when gets out of scope
You need to change the implementation of prep():
void prep()
{
Entry entry;
entry.list.push_back(0);
entries.emplace_back(entry);
cout << "prep " << entries.back().list.size() << "\n";
}
There is no need to allocate a Entry on the heap just to make a copy of it.

Incorrect values with auto iterator on std::list

I firstly intialize a hotel object and then initialize some room objects and print their id's respectively (which print out correctly.)
for(int j=0;j<5;j++){
Room r(1, 30);
hotel.addRoom(r);
cout << "Id: " << r.getId() << endl;
}
Then, I do this iteration on the list:
cout << "Initialized rooms with Ids: ";
for(auto iterator : hotel.getRooms()){
cout << iterator->getId() << " ";
}
cout << endl;
Also, here is the implementation on those methods, on the Hotel class:
//header
list<Room*> rooms;
//source
list<Room*> & Hotel::getRooms(){
return rooms;
}
And look at the output!
Every other part (like id generation and construction of objects) is tested and works fine.
Since you haven't posted a definition for the addRoom function, by the power of deduction I conclude it does something similar to:
void Hotel::addRoom(const Room& room) {
rooms.push_back(&room);
}
And then given the following loop:
for(int j=0;j<5;j++)
{
// This creates a Room object local to each iterator of the loop
Room r(1, 30);
// This adds the address of this local variable in the list
hotel.addRoom(r);
cout << "Id: " << r.getId() << endl;
// The variable r is destroyed here
}
So in the end your std::list<Room*> is filled with dangling pointers - pointers which point to memory which is no longer yours. This causes undefined behavior.
I recommend you drop the pointers altogether and change to:
class Hotel
{
std::list<Room> rooms;
public:
void addRoom(const Room& room) {
rooms.push_back(room);
}
std::list<Room>& Hotel::getRooms() {
return rooms;
}
const std::list<Room>& Hotel::getRooms() const {
return rooms;
}
};

Strange output when attempting to print from an array of pointers c++

I'm attempting to create an array of pointers.
struct vertex
{
std::string id;
std::string name;
int networkID;
std::vector<adjVertex> friends;
bool visited;
};
struct hobbylist
{
std::string hobby;
std::vector<vertex*> list;
};
hobbylist * hobbies[HASHMAP_SIZE];
adding the user to the hobbies array:
int Graph::addUserToHobby(std::string hobby1, std::string id){
// initial key is based on the first 2 characters of the hobby name
int key = (hobby1[0] + hobby1[1]) % HASHMAP_SIZE;
cout << " initial hashmap key " << key << endl;
hobbylist *h = new hobbylist;
h->hobby = hobby1;
hobbies[key] = h;
}
my goal is to create an array of pointers with the hobbylist type, when attempting to print the contents of that array I end up with a very strange random symbol output:
GLIBC_2.2.5GLIBCXX_3.4.13GLIBCXX_3.4.14CXXABI_1.3GLIBCXX_3.4� P&y
I attempt to print it as so:
void Graph::displayHobbies(){
cout << "========================================\n";
cout << "DISPLAYING HOBBY INTERESTS =============" << endl;
for(auto const& value: hobbies)
{
cout << value->hobby << ":" << endl;
}
}
I was wondering if I am printing incorrectly or if I am adding the hobby to the hobbies array incorrectly.
Changed Code:
hobbylist *h = new hobbylist;
h->hobby = hobby1;
if(hobbies[key] ==NULL){
h->list.push_back(user);
hobbies[key] = h;
}
else if (hobbies[key]!=NULL){
h= hobbies[key];
h->list.push_back(user);
}
Changed code is above and I am getting a seg fault at the last line in the else statement when running the function the first time and I am confused why the function would go to the else statement when the array should be empty and therefore hobbies[key] should be null the first time the function is run?
You have at least two bugs.
hobbylist *h = new hobbylist;
h->hobby = hobby1;
hobbies[key] = h;
key is your hash key. If hobbies[key] already has a pointer, this is going to leak memory.
for(auto const& value: hobbies)
{
cout << value->hobby << ":" << endl;
}
This assumes that every slot in the hobbies hash array contains a pointer. This is unlikely. If a particular value in hobbies has never been initialized (none of the previously inserted hobbies mapped to that hash key), the pointer will be NULL, and value->hobby will attempt to dereference a NULL pointer, resulting in undefined behavior. That's your likely crash.

Using a map that has a abstract base class pointer and calling a derived class function

I've searched on the web and can't find any solutions to my problem I hope you can help.
So I have constructed an abstract base class and have two derived classes that represents different experiments. (one is actually a derived derived class of my base class) And I made a map as such in a separate header file to store different types of experiments.
//Map class template to store name of experiment and the experiment within a project
typedef map <string, baseData <double>*> ExpContainer;
void search(string searchName, ExpContainer exps) {
ExpContainer::iterator Iter;
Iter = exps.find(searchName); //finds the entry corresponding to searchName and returns the iterator
if (Iter != exps.end()) { //need this as if entry is not found, the return will be end iter.
cout << "Found the experiment " << Iter->first << "." << endl;
Iter->second->print();
}
else {
cout << "Sorry, experiment " << searchName << " is not found" << endl;
}
}
The print() function is different for each experiment type and I know there's a problem called slicing so I've made sure that print() is virtual in the base class. Here's my base class:
namespace mynmsp {
//base class of data can be measurements or errors
template < class T> class baseData {
public:
virtual void print() =0;
virtual ~baseData() {
cout << "Destructor called for the base class." << endl;
}
};
}
Then in my main.cpp I've constructed different types of experiment and I want to print them. Each experiment class has different implementation of the print function that overrides the print function from the base class, like:
void print(){ //do something };
And in my main.cpp I have the map defined as:
ExpContainer project;
And after I have constructed each experiment, I've asked the user for the name of the experiment (expName) and inserted into project as such:
project[expName] = &expC;
I think the insertion is fine as I tested the size of project and it was correct.
However, a runtime error occured when my search function was called like this:
search(name, project);
I don't know if there's a problem with slicing or with my pointers?
I tried to make print() a virtual function in each derived class but that doesn't seem to work either.
Apologies for the long question, please help!
Edit: I've constructed my experiments inside a do while loop while project is declared outside. The whole code is very long but its basics is something like this:
string repeatstr; //user input whether to repeat do while loop or not
bool repeat = true; //condition for staying inside do while loop
ExpContainer project; //declared project to store experiments
do {
string expName;
string ans1; //character to store user input
cout << "What is the name of your experiment? " << endl;
cin >> expName;
cout << "Is this a experiment C ? (y/n)" << endl;
cin >> ans1;
if(ans1 =="y"){
//call the constructor for expC
project[expName] = &expC;
}else {
//call the constructor for expB
project[expName] = &expB;
}
cout << "Do you want to enter another experiment? (y/n)" << endl;
cin >> repeatstr;
if (repeatstr == "n") { repeat = false; }
}while (repeat); //loop over this do-while loop while repeat is true
cout << "There are " << project.size() << " in this database." << endl;
//to retrieve info from a certain experiment
string input, name;
cout << "Would you like to retrieve any experiments (y/n)? " << endl;
input = EitherInput("y", "n");
if (input == "y") {
cout << "Please enter the name of the experiment you want to retrieve: " << endl;
cin >> name;
search(name, project); //code breaks down here!
}
You are saving a pointer to the object that was already destroyed. You can check the addresses that you have in the map, most probably they are the same. You should store your experiment object in dynamic memory
if(ans1 =="y")
{
project[expName] = new expC();
} // Scope ends here and all variable created in it will be destroyed.
else
{
project[expName] = new expB();
} // Scope ends here and all variable created in it will be destroyed.
And after you are done with them you need to call delete on each pointer to avoid memory leak. Also you need to check if the items in the map are already existing, otherwise you will loose pointers to allocated memory which is automatically a memory leak.
I would recommend you to use std::share_ptr< baseData<double> > instead of bare baseData<double>*. Here you can read more about it. And also consider using typedef in order to have more clear syntax.
P.S.
The function
void search(string searchName, ExpContainer exps)
will copy whole map to its body. Use constant reference instead
void search(string searchName, const ExpContainer& exps)
But then you'll also need to declare function print as const:
virtual void print() const = 0;
and override it with const modifier:
virtual void print() const override;
And use constant iterator ExpContainer::const_iterator Iter