I have written a set of functions which together build a list of "Hubs" from a .csv file. I wish to generate a linked-list of these hubs in my main function I have a "head" pointer that I can pass to other functions in main.
The code in main:
HubNode *hh = NULL;
HubNode **head;
*head = hh;
Tools::loadHubs(head);
cout << hh->name; /* HubNodes have a string called "name" */
The code in Tools:
void Tools::loadHubs(HubNode **head)
{
string line, name, location;
// headFlights = currFlights = prevFlights = NULL;
ifstream myfile("Hub.csv");
if (myfile.is_open())
{
while (getline(myfile, line)) {// Omit the Caption Line.
while (getline(myfile, name, ','))//Get every value in order.
{
getline(myfile, location, '\n');
// cout << line << "\n";
// cout << name << "\n";
// cout << location << "\n";
HubNode::AddHub(name, location, head);
}
}
myfile.close();
}
else { cout << "\nUnable to open file\n"; }
}
The code in HubNode:
void HubNode::AddHub(string sname, string slocation, HubNode **head)
{
HubNode* newNode = new HubNode;
HubNode *point;
newNode->next = NULL;
newNode->name = sname;
newNode->location = slocation;
if (*head != NULL)
{
HubNode *curr = *head;
while (curr->next != NULL)
{
curr = curr->next;
}
curr->next = newNode;
}
else
{
point = newNode;
*head = point;
}
}
I thought that using a double pointer to the head of the list in this way would work, so that from "hh" in main, I would have access to the whole linked-list.
When I compile and start debugging, I can see that AddHubs is successfully creating the HubNodes in its scope, But when I try to access any of the elements from main (e.g. by cout << hh->name), I get a segmentation fault.
What have I done wrong? (Let me know if I need to post more code...)
You wouldn't do this:
int value = 10;
int *p;
*p = value;
So why would you think this would work:
HubNode *hh = NULL;
HubNode **head;
*head = hh;
The indirection is the same, only the type has changed. And both snippets invoke undefined behavior. This code should do this:
HubNode *hh = NULL;
Tools::loadHubs(&hh);
cout << hh->name;
Further, your add function should be:
void HubNode::AddHub(const string& sname, const string& slocation, HubNode **head)
{
HubNode* newNode = new HubNode;
newNode->next = NULL;
newNode->name = sname;
newNode->location = slocation;
while (*head)
head = &(*head)->next;
*head = newNode;
}
It gets even more straight forward if you provide a proper constructor for HubNode that takes the sname and slocation as construction parameters and initializes the node next member to NULL. if you code that, the add becomes simply:
void HubNode::AddHub(const string& sname, const string& slocation, HubNode **head)
{
while (*head)
head = &(*head)->next;
*head = new HubNode(sname, slocation);
}
Related
I have to dynamically allocate a list of robots for a school project. In an actual program, there will be other member functions that will require the list of names in order to perform certain functions.
As of right now, I just learned about this concept, and have tried really hard to put together some things I have seen online. The issue at the moment is that I can not tell if my list is properly being stored -- I am also getting wonky output when I try to call my display of list function.
Please help if you can. Also, I am happy to hear any tips for literally anything, as I am fairly new to programming.
class Node{
public:
std::string name_;
Node* next;
};
class linkedBotList{
public:
linkedBotList() {head = nullptr;} //constructor
~linkedBotList(){}; // destructure
void addNode();
void display();
private:
Node* head;
};
int main(int argc, const char * argv[]) {
linkedBotList* list = new linkedBotList();
int siz;
std::cout << "How many Robots?" << std::endl;
std::cout << "What are the names?" << std::endl;
std::cin >> siz;
for(int i = 0; i < siz; i++){
list->addNode();
}
delete list;
return 0;
}
void linkedBotList::addNode(){
std::string botName;
Node* newNode = new Node();
newNode->name_ = botName;
newNode->next = nullptr;
std::cin >> botName;
if(head == nullptr){
head = newNode;
}
else {
Node* temp = head; // head is not null
while(temp->next != nullptr){ // go until at the end of the list
temp = temp->next;
}
temp->next = new Node; // linking to new node
}
}
void linkedBotList::display() {
if (head == NULL) {
std::cout << "List is empty!" << std::endl;
}
else {
Node* temp = head;
while (temp != NULL) {
std::cout << "Made it to display funct.\n";
std::cout << temp->name_ << " ";
temp = temp->next;
}
std::cout << std::endl;
}
}
I did try a few things, like switching around my temp variable, and a few other re-assignments. Maybe someone can quickly spot the issue and help?
Your display function is fine.
The problem is that you have 2 logic flaws in addNode():
you are not storing strings in your list correctly. You are assigning botName to newNode->name_ before botName has been assigned a value. So all of your nodes have empty strings. Assigning botName afterwards will not update newNode->name_. 1
if the list is not empty, you iterate to the end of the list correctly 2, but then you assign a new blank node to temp->next instead of assigning your newNode that you already populated. And your Node constructor is not initializing the next member to nullptr, so you are creating a corrupted list, which will cause subsequent loops through the list to invoke undefined behavior.
Try this instead:
void linkedBotList::addNode(){
std::string botName;
std::cin >> botName; // <-- move up here
Node* newNode = new Node{botName, nullptr};
if (!head){
head = newNode;
}
else {
Node* temp = head;
while (temp->next){
temp = temp->next;
}
temp->next = newNode; // <-- linking to new node
}
}
Alternatively, you can eliminate the if like this:
void linkedBotList::addNode(){
std::string botName;
std::cin >> botName;
Node** temp = &head;
while (*temp){
temp = &((*temp)->next);
}
*temp = new Node{botName, nullptr};
}
1: A better design would be to have addNode() take in a string as an input parameter, and then move the cin call into your loop in main().
2: consider adding a tail member to your list to avoid having to loop on each addition.
Try this alternate design:
class Node{
public:
std::string name;
Node* next = nullptr;
};
class linkedBotList{
public:
linkedBotList() = default;
~linkedBotList();
void addNode(std::string name);
void display() const;
private:
Node* head = nullptr;
Node* tail = nullptr;
};
int main() {
linkedBotList list;
int siz;
std::string botName;
std::cout << "How many Robots?" << std::endl;
std::cin >> siz;
std::cout << "What are the names?" << std::endl;
for(int i = 0; i < siz; i++){
std::cin >> botName;
list.addNode(botName);
}
list.display();
return 0;
}
linkedBotList::~linkedBotList(){
Node *temp = head, *next;
while (temp) {
next = temp->next;
delete temp;
temp = next;
}
}
void linkedBotList::addNode(std::string name){
Node* newNode = new Node{name};
if (tail)
tail->next = newNode;
else
head = newNode;
tail = newNode;
}
void linkedBotList::display() const {
if (!head) {
std::cout << "List is empty!" << std::endl;
}
else {
Node* temp = head;
do {
std::cout << temp->name << " ";
temp = temp->next;
}
while (temp);
std::cout << std::endl;
}
}
Recordlabel.cpp
void recordLabel::addArtist(char* artistName)
{
Node* temp = new Node;
temp->artistName = artistName;
temp->next = head;
head = temp;
}
void recordLabel::displayArtists()
{
Node* tmp = head;
tmp = tmp->next;
while (tmp != NULL)
{
cout << tmp->artistName << " ";
tmp = tmp->next;
}
}
Main.cpp
int main()
{
recordLabel recordLabel;
char* artistName = new char[25];
char repeatLoop = 'y';
while (repeatLoop == 'y' || repeatLoop == 'Y')
{
cout << "Please Enter an Artist Name: ";
cin.getline(artistName,25);
recordLabel.addArtist(artistName);
cout << "Do you want to add a Name? (y/n): ";
cin >> repeatLoop;
cin.ignore();
}
recordLabel.displayArtists();
//delete[] artistName;
system("pause");
return 0;
}
So I'm trying to display my linked list but when I enter input like "john" "kyle" "david" the output from the display function justs ends up being david david david. Can someone help me with this? Also, I realize using string would solve most of my problems but I'm trying to just use Chars.
Thanks
Modify the method addArtist in this way:
void recordLabel::addArtist(char* artistName)
{
Node* temp = new Node;
temp->artistName = strdup(artistName);
temp->next = head;
head = temp;
}
You need to include also string.h
#include <cstring>
Do not forget to clean the memory with the destructor
All nodes of the list contain in their data members artistName the address of the allocated memory stored in the pointer artistName declared in main.
char* artistName = new char[25];
//...
recordLabel.addArtist(artistName);
and
void recordLabel::addArtist(char* artistName)
{
Node* temp = new Node;
temp->artistName = artistName;
//...
That is all the data members store the address of the same allocated memory.
As a result all the data members will point to the last string stored in this dynamically allocated memory.
You need to crate a copy of the stored string in the current moment.
For example
#include <cstring>
//...
void recordLabel::addArtist( const char *artistName )
{
Node* temp = new Node;
temp->artistName = new char[strlen( artistName ) + 1];
strcpy( temp->artistName, artistName );
temp->next = head;
head = temp;
}
When you should free all the allocated memory for strings and nodes in the destructor of the list.
Also it is unclear why the output of the list starts from the second node
void recordLabel::displayArtists()
{
Node* tmp = head;
tmp = tmp->next;
while (tmp != NULL)
//...
If initially the pointer head is equal to nullptr then the function can invoke undefined behavior when will be called for an empty list.
You could make your life easier if the data member artistName had the type std::string instead of char *.
For example if the class Node is defined something like
struct Node
{
std::string artistName;
Node *next;
}
then the member function addArtist could look very simply.
#include <string>
//...
void recordLabel::addArtist( const char *artistName )
{
head = new Node { artistName, head };
}
When I build the code, I don't get any errors in the output window. However, after running it, the compiler throws an exception (I'll comment where it is being thrown) at my code saying "Exception thrown: read access violation.
temp was 0xCDCDCDCD.".
I tried researching what this error is, and I found that this is for unassigned memory, but I don't see where something is being unassigned.
This is my Linked List .cpp file. The exception is thrown at a line towards the end of this file.
#include "linkedlist.h"
struct ll::node
{
weapons data;
node* next;
};
ll::ll()
{
head = NULL;
}
ll::~ll()
{
while (head != NULL)
{
node* temp = head;
head = head->next;
delete temp;
}
}
void ll::addItem(weapons obj)
{
node* newNode = new node;
node* temp = head;
newNode->data = obj;
if (head == NULL)
head = newNode;
return;
while (temp->next != NULL)
{
temp = temp->next;
}
if (temp->next == NULL)
{
temp->next = newNode;
return;
}
}
void ll::displayItems()
{
for (node* temp = head; temp != NULL; temp = temp->next)
{
temp->data.getDescription(); //EXCEPTION THROWN HERE
}
}
This file has the inherited class "Weapons" which is the object that is being called as "temp->data". As well as where I have "getDescription".
#include <vector>
using namespace std;
//base class
class inventory
{
protected:
//number of items in inventory
int mNumItems;
public:
//getters
void displayInv();
int getNumItems();
virtual void getDescription();
};
//weapon class
class weapons : public inventory
{
private:
//name of object
string mName;
//what the object is
string mInfo;
//how much of the object
int mAmount;
//how much damage does it do
double mDamage;
public:
//constructor
weapons();
weapons(string, string, double, int);
//getters
string getName();
void getDescription();
int getAmount();
double getDamage();
string getInfo();
//mutators
void setAmount(int);
};
This is where I define weapons
//weapon class
weapons::weapons()
{
mName = " ";
mInfo = " ";
mDamage = 0.0;
mAmount = 0;
}
weapons::weapons(string name, string info, double dmg, int amt)
{
mName = name;
mInfo = info;
mDamage = dmg;
mAmount = amt;
}
string weapons::getName()
{
return mName;
}
int weapons::getAmount()
{
return mAmount;
}
double weapons::getDamage()
{
return mDamage;
}
string weapons::getInfo()
{
return mInfo;
}
void weapons::getDescription()
{
cout << getName() << ", " << getDamage() << " damage, " << getInfo() << " Amount: " << getAmount() << endl;
}
void weapons::setAmount(int amt)
{
mAmount = amt;
}
Let me know if I need to include anymore files!
I get the expected results, which is for it to describe an item which I have in the Linked List. Unfortunately, my only problem is that this exception is being thrown.
Problem
In
struct ll::node
{
weapons data;
node* next;
};
and
void ll::addItem(weapons obj)
{
node* newNode = new node; // leaks if node not added
node* temp = head;
newNode->data = obj;
if (head == NULL)
head = newNode;
return; // this is a NASTY bug of a different sort. Most of the time
// the function will exit without doing ANYTHING
while (temp->next != NULL)
{
temp = temp->next;
}
if (temp->next == NULL) // the only way out of the above loop is if
// temp->next == NULL. This if is redundant.
{
temp->next = newNode;
return;
}
}
Nothing ever sets newNode->next to a safe value. That allows
while (temp->next != NULL)
{
temp = temp->next;
}
to fail because there are no guarantees that temp->next is ever NULL and the loop goes marching off the end of the list.
Solution
Force next to a safe value.
struct ll::node
{
weapons data;
node* next = NULL;
};
Or a more versatile version
struct ll::node
{
weapons data;
node* next;
node(const weapons & weap, // const reference eliminates a possible copy
node * link = NULL): // if you know what the next link will be,
// you can add it here. If not, it's always NULL
data(weap),
next(link)
{
}
};
addItem now looks something like
void ll::addItem(const weapons & obj)
{
if (head == NULL)
{
head = new node(obj); // only making node if we need it
// less chance of leak
}
else
{
node * temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = newNode(obj);
}
}
But you can do something really sneaky here to make life easier. head is really a next pointer by another name, so if you can abstract the different name... And we can by tracking a pointer to next rather than a pointer to the node. This is really handy when you have to insert or remove an item: You have a reference both to the node in question and the insertion point in the previous node.
void ll::addItem(const weapons & obj)
{
node ** temp = &head; // get pointer to insertion point
while ((*temp) != NULL) // next node, including head, is not null
{
temp = &(*temp)->next; // get pointer to next insertion point
}
*temp = newNode(obj); // insert node
}
Half the code. Example of how this helps remove:
void ll::removeItem(const weapons & obj)
{
node ** temp = &head;
while ((*temp) != NULL && (*temp)->data != obj)
{
temp = &(*temp)->next;
}
if (*temp != NULL) // found it!
{
node * rem = *temp; // get node to remove so we don't lose it when we relink
*temp = rem->next; // point at item after rem
delete rem; // release item
}
}
The purpose of my program is to read in data from a file and build a linked list with this data and then deallocate all the nodes used.
the program also needs to print out the address of nodes after they are created and then after that they are deleted
#include <iostream>
#include <string>
#include <fstream>
#include "BigHero.h"
using namespace std;
// Linked List Struct
struct Node{
BigHero data;
Node* Next;
};
// Funtion Prototypes
int countHeros(string,int&);
void createList(BigHero,int,Node*&,Node*&,Node*&);
void printList(Node*,Node*,Node*);
void deallocateList(Node*&,Node*&,Node*&);
int main()
{
// Program Variables
Node* head;
Node* currentPtr;
Node* newNodePtr;
string Filename = "ola5party.dat"; // File string varible
int charNumber = 0; // variable to hold number of Heroes
int i = 0; // Loop control varible
countHeros(Filename,charNumber); // Function call used to count number of Heros
ifstream inFile;
inFile.open(Filename.c_str());
if(!inFile){
cout << "Error in opening file" << endl;
return 0;
}
BigHero Hero;
while(inFile)
{
inFile >> Hero;
createList(Hero,charNumber,head,currentPtr,newNodePtr);
}
printList(head,currentPtr,newNodePtr);
deallocateList(head,currentPtr,newNodePtr);
inFile.close();
return 0;
}
int countHeros(string Filename,int& charNumber)
{
ifstream inFile;
inFile.open(Filename.c_str());
string aLineStr;
while (getline(inFile, aLineStr))
{
if (!aLineStr.empty())
charNumber++;
}
inFile.close();
return charNumber;
}
void createList(BigHero Hero, int charNumber,Node*& head, Node*& currentPtr, Node*& newNodePtr)
{
head = new Node;
head->data =Hero;
currentPtr = head;
newNodePtr = new Node;
cout << "Allocated # " << newNodePtr << endl;
newNodePtr->data = Hero;
currentPtr->Next = newNodePtr;
currentPtr = newNodePtr;
}
void printList(Node* head, Node* currentPtr, Node* newNodePtr)
{
if(head != NULL)
{
currentPtr = head;
while(currentPtr->Next != NULL)
{
cout << currentPtr->data << endl;
currentPtr = currentPtr->Next;
}
}
}
void deallocateList(Node*& head ,Node*& currentPtr,Node*& newNodePtr)
{
if( head != NULL)
{
currentPtr = head;
while( head -> Next != NULL)
{
head = head->Next;
cout << "Deleting # " << head << endl;
delete currentPtr;
currentPtr = head;
}
delete head;
head = NULL;
currentPtr = NULL;
}
}
the program like this runs without errors, but here is the problem it will input all the information required but since i only have one variable hero class it is constantly replacing the information.
i tried to make a class array (example hero[i]) but cant seem to get it right and am not even sure if that is the solution. Everything is fine but i cant get the desired number of class object and i always end up with one class
this is my desired output but i only get one class object
Allocated#0x8722178
Allocated#0x87221d0
Allocated#0x8722210
Allocated#0x8722230
Allocated#0x8722288
Allocated#0x87222c8
Hero:MacWarriorLevel134,(34,16,48)Exp:13425
Hero:LinuxMageLevel149,(24,54,21)Exp:14926
Hero:PCBardLevel122,(18,32,17)Exp:12221
Hero:PythonThiefLevel90,(24,18,61)Exp:9001
Hero:CplusPaladinLevel159,(31,38,29)Exp:15925
Deleting#0x8722178
Deleting#0x87221d0
Deleting#0x8722210
Deleting#0x8722230
Deleting#0x8722288
Deleting#0x87222c8
It seems you have misunderstood the basic idea behind a link listed. You are not supposed to overwrite head again and again when adding element. head shall only be changed when the list is empty.
Try something like this:
struct Node
{
BigHero data;
Node* next;
};
void addNewNode(Node*& head, ....)
{
if (head == nullptr)
{
// List empty so add new node as head
head = new Node;
head->next = nullptr;
return;
}
// Find last element in list (performance can be improved with a tail*)
Node* temp = head;
while (temp->next != nullptr) temp = temp->next;
// Add new element to end of list
temp->next = new Node;
temp->next->next = nullptr
return;
}
int main()
{
Node* head = nullptr;
addNewNode(head, ....);
return 0;
}
For performance it is often good to have a tail-pointer also.
Further you should not define head in main() but make a class/struct for it and put the relevant functions in the class. Like:
struct Node
{
BigHero data;
Node* next;
};
class ListOfNode
{
public:
ListOfNode() : head(nullptr), size(0) {}
~ListOfNode()
{
// Delete all nodes
}
void addNewNode(....)
{
// ....
++size;
}
size_t size() { return size; }
private:
Node* head; // Optional: Add a tail* for better performance
size_t size;
};
int main()
{
ListOfNode list;
list.addNewNode(....);
cout << list.size() << endl;
return 0;
}
New to C++ and I have a general linked list question. Say I have:
struct Node
{
string name;
Node *next
};
in a header file and I have a function
Node ReadIntoList(const string INPUT_FILE)
{
ifstream inFile;
Node *head;
head = NULL;
Node *perPtr;
perPtr = new Node;
inFile.open(INPUT_FILE);
while(inFile && perPtr!= NULL)
{
getline(inFile, perPtr->name);
perPtr -> next = head;
head = perPtr;
perPtr = new Node;
}
delete perPtr;
perPtr = NULL;
return *head;
}
My question is what do I need to return from the ReadIntoList function to main
so that I can access the list and then be able to setup a function to output it
to a file. Here is my main so far...
int main()
{
ofstream oFile;
string inputFile;
Node *head;
cout << left;
cout << "Please enter the name of the input file you would like to "
"use: ";
getline(cin, inputFile);
head = ReadIntoList(inputFile);
oFile.open("OFile.txt");
return 0;
}
This Node in main is not setup properly obviously but I'm not sure how to access the info from ReadIntoList.
In your code, ReadIntoList() is returning the content of the last node in the list (which will crash if the input file fails to open). ReadIntoList() needs to instead return a pointer to the first node in the list. That will allow main() to loop through the list, eg:
Node* ReadIntoList(const string &inputFile)
{
ifstream inFile;
Node *head = NULL;
Node *last = NULL;
Node *perPtr = NULL;
inFile.open(inputFile);
if (inFile)
{
do
{
perPtr = new Node;
if (!getline(inFile, perPtr->name))
{
delete perPtr;
break;
}
perPtr->next = NULL;
if (!head) head = perPtr;
if (last) last->next = perPtr;
last = perPtr;
}
while (true);
}
return head;
}
int main()
{
ofstream oFile;
string inputFile;
Node *head;
Node *perPtr;
Node *tmp;
cout << left;
cout << "Please enter the name of the input file you would like to use: ";
getline(cin, inputFile);
head = ReadIntoList(inputFile);
if (head)
{
oFile.open("OFile.txt");
if (oFile)
{
perPtr = head;
do
{
oFile << perPtr->name << endl;
perPtr = perPtr->next;
}
while (perPtr != NULL);
}
perPtr = head;
do
{
tmp = perPtr->next;
delete perPtr;
perPtr = tmp;
}
while (perPtr != NULL);
}
return 0;
}
That being said, since you are using C++, you should be using std::list (or std::forward_list in C++11 and later) instead, eg:
#include <list>
bool ReadIntoList(const string &inputFile, list<string> &outList)
{
outList.clear();
ifstream inFile(inputFile);
if (!inFile)
return false;
string name;
while (getline(inFile, name))
outList.push_back(name);
return true;
}
int main()
{
ofstream oFile;
string inputFile;
list<string> items;
cout << left;
cout << "Please enter the name of the input file you would like to use: ";
getline(cin, inputFile);
if (ReadIntoList(inputFile, items))
{
oFile.open("OFile.txt");
if (oFile)
{
for (list<string>::const_iterator iter = items.begin(), end = items.end(); iter != end; ++iter)
{
oFile << *iter << endl;
}
}
}
return 0;
}