Hi i am frankly new to these forums so i tried looking up as much as i could but i couldn't find anything relate-able to my problem.
I am trying to read nodes that i stored into a file and insert them into a linked list that i created
THE insert functions works perfectly fine however while loading it when i attempt to insert the node ,
1.it inserts the node
2.reads the new node
3.since im using a pointer , the pointer now points to the new read node
4.inserts the newly read node from the file into the list overwriting the old data.
This causes my old data to be completely lost and the new node to act as the header of the file
load function :
void load_file_students(linked_student &students)
{
node_student *test = new node_student;
ifstream stu_list("students.dat",ios::binary);
stu_list.read((char*)test, sizeof(*test));
while (!stu_list.eof())
{
//students.insert_node_list(test);
students.insert_node_list(test);
stu_list.read((char*)test, sizeof(*test));
}
stu_list.close();
}
the insert_node function:
void linked_student::insert_node_list(node_student *student)
{
node_student* temp = new node_student;
temp = student;
if (head == NULL)
{
head = temp;
}
else
{
node_student *ptr = this->head;
while (ptr->next != 0)
{
ptr = ptr->next;
}
temp->previous = ptr;
ptr->next= temp;
}
}
the node :
#pragma once
#include <string>
using namespace std;
static int roll_number = 1; // used for the personal identification of the student
class node_student
{
public:
bool fees_paid = true;
string name;
float CGPA;
int ID; // same as the static roll_number provided
int semester;
string department;
string elective_subjects[5];
node_student *next;
node_student *previous;
node_student();
~node_student();
};
during debugging mode it is evident that when i re-read the data in the load function during
stu_list.read((char*)test, sizeof(*test));
it overwrites the old data in the insert function as well even though it is called before this line.
Once again the insertion into the file works perfectly fine , i can actually see the values being loaded via the debugging mode
here is the insert function for reference
INSERT :
void update_student_file(linked_student testing)
{
node_student *temp = testing.head;
ofstream stu_list("students.dat",ios::binary);
while (temp != NULL)
{
stu_list.write((char*)temp, sizeof(*temp));
temp = temp->next;
}
stu_list.close();
}
Here a fix to load_file_students:
void load_file_students(linked_student &students)
{
node_student test; //way use dynamic allocation?
ifstream stu_list("students.dat",ios::binary);
while (stu_list.read((char*)test, sizeof(*test)))
{
students.insert_node_list(&test);
}
//file will be closed at the destructor.
}
Another fix for insert_node_list
void linked_student::insert_node_list(node_student *student)
{
node_student* temp = new node_student;
*temp = *student; //call copy constructor, do not copy pointer value.
if (head == NULL)
{
head = temp;
}
else
{
node_student *ptr = this->head;
while (ptr->next != 0)
{
ptr = ptr->next;
}
temp->previous = ptr;
ptr->next= temp;
}
}
Related
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
}
}
I'm working on a C++ assignment where I'll create a search engine on a linked list of linked lists. As per the requirements, I can't use other libraries nor STL.
Basically it will be like this (I removed the variables from small list since they are irrelevant):
My structs are these:
struct small
{
int data;
struct small *next;
};
struct big
{
int playerID;
string playerName;
string playerTeam;
struct small *goals;
struct big *next;
};
Here's the relevant code snippet, I think the problem is at addGoals(...) where I'm failing to assign the small element to the temp->goals.
class biglist
{
private:
big *head, *tail;
public:
biglist()
{
head = NULL;
tail = NULL;
}
. . .
void createbig(int ID, string name, string team)
{
big *temp = new big;
temp->playerID = ID;
temp->playerName = name;
temp->playerTeam = team;
temp->goals = NULL;
temp->next = NULL;
if (head == NULL)
{
head = temp;
tail = temp;
temp = NULL;
}
else
{
tail->next = temp;
tail = temp;
}
}
void addGoals(int id, small *s)
{
big *temp = head;
while (temp != NULL)
{
if (temp->playerID == id)
{
temp->goals = s;
break;
}
temp = temp->next;
}
}
void test()
{
big *temp = head;
while (temp != NULL)
{
if (temp->playerID == 1)
{
if (temp->goals !=NULL)
{
cout << temp->goals->data << endl;
}
else
{
cout << "goals null" << endl;
}
}
temp = temp->next;
}
}
}
. . .
class smalllist
{
private:
small *head, *tail;
public:
smalllist()
{
head = NULL;
tail = NULL;
}
void createsmall(int ID, biglist b)
{
small *temp = new small;
temp->data = ID;
temp->next = NULL;
if (head == NULL)
{
head = temp;
tail = temp;
temp = NULL;
}
else
{
tail->next = temp;
tail = temp;
}
b.addGoals(1, temp);
}
};
Finally, my main code:
int main()
{
biglist obj;
obj.createbig(1, "Player1", "Team1");
obj.createbig(2, "Player2", "Team2");
obj.displaybig();
smalllist sml;
sml.createsmall(9, obj);
sml.displaysmall();
obj.displaybig();
obj.test();
}
Debugging throws an exception at:
cout << temp->goals->data << endl;
saying that
Exception thrown: read access violation. temp->goals was nullptr.
I'm 90% sure I messed up something with pointers; but other stuff I've tried gave errors before compiling. I checked out some books / tutorials but couldn't figure it out.
Also if you have a better approach or saw one of the horrible mistakes that I'm making, please don't hold back :)
Thanks.
EDIT I changed my createbig() like this.
Currently it works with following codes:
void createbig(int ID, string name, string team, small *s)
{
big *temp = new big;
temp->playerID = ID;
temp->playerName = name;
temp->playerTeam = team;
temp->goals = s;
temp->next = NULL;
if (head == NULL)
{
head = temp;
tail = temp;
temp = NULL;
}
else
{
tail->next = temp;
tail = temp;
}
}
and added this to small
small getsmall(int i)
{
small *temp = head;
while (temp != NULL)
{
if (temp->data == i)
{
return *temp;
}
}
}
My final main function is
int main()
{
smalllist sml;
sml.createsmall(9);
sml.displaysmall();
biglist obj;
small s = sml.getsmall(9);
obj.createbig(1, "Player1", "Team1", &s);
//obj.createbig(2, "Player2", "Team2");
obj.displaybig();
obj.test();
}
While it ends successfully now, it gives the address of goals and I get this in debug section:
Let's look at what your code does, going through the main function. (Being able to walk through code like this is a useful skill. You can also use a debugger to help out, stepping through your function line-by-line.)
biglist obj;
Default construct a biglist. The head and tail are null. (By the way, nullptr is C++'s replacement for C's NULL.)
obj.createbig(1, "Player1", "Team1");
obj.createbig(2, "Player2", "Team2");
Add entries in obj for players with IDs 1 and 2. Their goals are null.
obj.displaybig();
Presumably an output of obj?
smalllist sml;
sml.createsmall(9);
sml.displaysmall();
These lines do something with a smalllist, but do not reference obj, so they are not relevant to this issue.
obj.displaybig();
Presumably an output of obj? Kind of redundant since nothing affected obj since the last display.
obj.test();
Call the test code, which finds the element for player ID 1 and outputs the data of that player's first goal. However, if you look up where that player was added, the goal is null, so you get a crash.
Separate from the above, there is probably some confusion in createsmall. Inside that function, a new biglist is created (not obj), and that list is told to add a goal to the player with ID 1. However, this has no effect the biglist in the main function.
You don't seem to have added any goals, so I'm assuming the code initializes with null.
and so the nullptr exception.
call addgoals() with the goals to player before test().
the other suggestions would be
to add a null check before printing goals
temp pointers need not be initialized with new big or small just the head of the list would be enough
I am trying to implement a priority Queue by using a linked list in c++. However, when I run the program it triggers a breakpoint within "priorityQLinkedList::dequeue()" method. Can someone tell why this is the case and give me suggestions on how to fix it?
Code:
#include <iostream>
#include <cstring>
#include <iomanip>
using namespace std;
struct DAT
{
int id;
char fullname[50];
double savings;
};
struct NODE
{
DAT data;
NODE *N;
NODE *P;
NODE(const int i, const char *f, const double s)
{
data.id = i;
strcpy_s(data.fullname, f);
data.savings = s;
N = NULL;
P = NULL;
}
};
class priorityQLinkedList
{
private:
NODE *front;
NODE *back;
public:
priorityQLinkedList() { front = NULL; back = NULL; }
~priorityQLinkedList() { destroyList(); }
void enqueue(NODE *);
NODE* dequeue();
void destroyList();
};
void priorityQLinkedList::enqueue(NODE *n)
{
if (front == NULL) {
front = n;
back = n;
}
else {
NODE *temp = front;
if (n->data.id > temp->data.id)
{
front->P = n;
n->N = front;
front = n;
}
else
{
//search for the posistion for the new node.
while (n->data.id < temp->data.id)
{
if (temp->N == NULL) {
break;
}
temp = temp->N;
}
//New node id's smallest then all others
if (temp->N == NULL && n->data.id < temp->data.id)
{
back->N = n;
n->P = back;
back = n;
}
//New node id's is in the medium range.
else {
temp->P->N = n;
n->P = temp->P;
n->N = temp;
temp->P = n;
}
}
}
}
NODE* priorityQLinkedList::dequeue()
{
NODE *temp;
//no nodes
if (back == NULL) {
return NULL;
}
//there is only one node
else if (back->P == NULL) {
NODE *temp2 = back;
temp = temp2;
front = NULL;
back = NULL;
delete temp2;
return temp;
}
//there are more than one node
else {
NODE *temp2 = back;
temp = temp2;
back = back->P;
back->N = NULL;
delete temp2;
return temp;
}
}
void priorityQLinkedList::destroyList()
{
while (front != NULL) {
NODE *temp = front;
front = front->N;
delete temp;
}
}
void disp(NODE *m) {
if (m == NULL) {
cout << "\nQueue is Empty!!!" << endl;
}
else {
cout << "\nID No. : " << m->data.id;
cout << "\nFull Name : " << m->data.fullname;
cout << "\nSalary : " << setprecision(15) << m->data.savings << endl;
}
}
int main() {
priorityQLinkedList *Queue = new priorityQLinkedList();
NODE No1(101, "Qasim Imtiaz", 567000.0000);
NODE No2(102, "Hamad Ahmed", 360200.0000);
NODE No3(103, "Fahad Ahmed", 726000.0000);
NODE No4(104, "Usmaan Arif", 689000.0000);
Queue->enqueue(&No4);
Queue->enqueue(&No3);
Queue->enqueue(&No1);
Queue->enqueue(&No2);
disp(Queue->dequeue());
disp(Queue->dequeue());
disp(Queue->dequeue());
disp(Queue->dequeue());
disp(Queue->dequeue());
delete Queue;
return 0;
}
One problem which stands out in your dequeue() method is that you are calling delete on a NODE pointer, and then attempting to return this deleted pointer to the caller. This could cause an error either in dequeue() itself, or certainly in the caller who thinks he is getting back a pointer to an actual live NODE object.
One potential fix would be to create a copy of the NODE being dequeued. You would still remove the target from your list, but the caller would then be returned a valid pointer, which he could free later.
NODE* priorityQLinkedList::dequeue()
{
NODE *temp;
// no nodes
if (back == NULL) {
return NULL;
}
NODE *temp2 = back;
temp = new NODE(temp2->data.id, temp2->data.fullname, temp2->data.savings);
// there is only one node
else if (back->P == NULL) {
front = NULL;
back = NULL;
delete temp2;
return temp;
}
// there are more than one node
else {
back = back->P;
back->N = NULL;
delete temp2;
return temp;
}
}
You're deleting pointers in dequeue that priorityQLinkedList does not own, so you don't know if it is safe to delete them.
In this case, they are not since the node pointers passed to enqueue are addresses of local, stacked based variables and have not been allocated by new. (There's also the already mentioned problem of deleting a pointer then returning it, which is Undefined Behavior.)
The fix for the code as shown is to remove the calls to delete in dequeue. However, if changes are made so that the nodes passed to enqueue are dynamically allocated, you'll need to add something to handle that.
1.First change strcpy_s to strcpy is struct NODE.
2.Instead of Delete(temp2) use temp2--.
//no nodes
if (back == NULL) {
return NULL;
}
//there is only one node
else if (back->P == NULL) {
NODE *temp2 = back;
temp = temp2;
front = NULL;
back = NULL;
temp2--;
return temp;
}
//there are more than one node
else {
NODE *temp2 = back;
temp = temp2;
back = back->P;
back->N = NULL;
temp2--;
return temp;
}
I hope this will resolve the problem.
Please refer the code given here:
This code is a part of stack implementation in C++:
Code 1:
void Stack::pop()
{
if (top != 0) {
node* temp = top;
top = top -> link;
delete temp;
}
}
Code 2:
void Stack::pop()
{
if (top != 0) {
node* temp = new node;
temp = top;
top = top -> link;
delete temp;
}
}
In first example, I didn't use new, while I did use it in second one. On running, both give same output with the complete program, which can be found below:
#include <iostream>
using namespace std;
struct node {
string name;
node* link;
};
class Stack
{
node* top;
public:
Stack();
void push(string s);
void pop();
void display();
~Stack(){}
};
Stack::Stack() {
top = 0;
}
void Stack::push(string s)
{
node* temp = new node;
temp -> name = s;
temp -> link = top;
top = temp;
}
void Stack::pop() // Function in question
{
if (top != 0) {
node* temp = new node;
temp = top;
top = top -> link;
delete temp;
}
}
void Stack::display()
{
node* temp = new node;
temp = top;
while (temp != 0)
{
cout << temp -> name << "\n";
temp = temp -> link;
}
}
int main() {
Stack s;
s.push("Ra");
s.push("Sa");
s.push("Ga");
s.pop();
s.display();
}
What is the difference in using and not using new pointer here?
Also does the memory automatically free itself or do I have to do it in destructor? If so, how to do it?
There's memory leak in the 2nd code snippet, even though it looks working well. new node is meaningless for node* temp = new node;, because temp is assigned to top at once. Then the original memory address created by new node is lost and couldn't be deleted again.
Also does the memory automatically free itself or do I have to do it in destructor?
Each object newed has to be deleted by yourself. Consider about smart pointers, they will manage such things for you.
In these lines:
node* temp = new node;
temp = top;
you allocate new node, store it in temp variable and ton the next variable store in the same variable another pointer. So that new node is lost and newer used. node* temp = new node; has no effect other than leaking memory.
Also does the memory automatically free itself or do I have to do it
in destructor?
No. Memory is not freed automatically. And you nearly never actually call object destructor manually.
If so, how to do it?
The old way is to use delete. But im modern C++ you should not use naked owning pointers and really should consider using std::unique_ptr.
While using new/allocating the memory, you are causing a memory leak.
node* temp = new node;
temp = top; //temp is now pointing to a new memory location.
//Thus the memory allocated by in the previous code line gets leaked
Code1 is the correct way to do it. Code 2 causes memory leak.
You have to delete the memory that you allocate using the delete operator in the destructor.
Stack::~Stack()
{
while(NULL != top)
{
node* temp = top;
top = top->link;
delete temp;
}
}
A smart pointer Solution. You will need c++11 compiler for the following code to compile/work.
#include <iostream>
#include <memory>
using namespace std;
struct node {
string name;
std::unique_ptr<node> link;
};
typedef std::unique_ptr<node> node_ptr;
class Stack
{
node_ptr top;
public:
Stack();
void push(string s);
void pop();
void display();
~Stack(){}
};
Stack::Stack() {
}
void Stack::push(string s)
{
auto temp = std::make_unique<node>();
temp -> name = s;
temp -> link = top;
top = temp;
}
void Stack::pop()
{
if (top != null) {
top = top -> link;
}
}
void Stack::display() const
{
node* temp = top.get();
while (temp != 0)
{
cout << temp -> name << "\n";
temp = (temp -> link).get();
}
}
int main() {
Stack s;
s.push("Ra");
s.push("Sa");
s.push("Ga");
s.pop();
s.display();
}