Struct, linked list,delete the whole list - c++

How can I delete all objects from a linked list,implemented in struct? After my delete-funktion (loeschen) are shown comic numbers and I donĀ“t know any more where my pointers are.
struct domino {
int data1;
int data2;
domino *next;
};
int readSteine (){ //Reading from numbers from file
FILE *file;
if((file=fopen("datei.dat", "r") )==NULL) { /
std::cout<<"File cant be open"<<std::endl;
return 0;
}else {
int beginning;
int temp;
fscanf(file, "%d", &beginning);
for(int i=0; i<beginning; i++) {
domino *domino1= new domino;
//if(i>0) temp2->next=domino1;
fscanf(file, "%i", &temp);
domino1->data1=temp;
fscanf(file, "%i", &temp);
domino1->data2=temp;
printf("[%d:%d]", domino1->data1, domino1->data2);
domino1->next=0;
}
}return 0;
}
Function for deleting of the list:
void deletelist (domino *head) {
domino* tmp;
while(head != 0) {
tmp=head->next;
delete head;
head=tmp;
}
}
int main() {
domino *pHead=NULL;
domino s;
readSteine();
deletelist(pHead);
std::cout<<s.data1<<"...."<<s.data2<<std::endl;
return 0;
}

Below is an example of a clear method. The idea is to traverse over the list and delete the nodes one by one.
Node *pDel = _pHead;
/* Traverse the list and delete the node one by one from the head */
while (pDel != NULL) {
/* take out the head node */
_pHead = _pHead->_pNext;
delete pDel;
/* update the head node */
pDel = _pHead;
}
/* Reset the head and tail node */
_pTail = _pHead = NULL;

Define the function either like
void loeschen (dominostein **kopf) {
dominostein* tmp;
while(*kopf != 0) { //bzw. NULL
tmp = ( *kopf )->next;
delete *kopf;
*kopf = tmp;
}
}
and call it like
loeschen(&pKopf);
or the following way
void loeschen (dominostein * &kopf) {
dominostein* tmp;
while( kopf != 0) { //bzw. NULL
tmp = ( kopf )->next;
delete kopf;
kopf = tmp;
}
}
and call it like
loeschen(pKopf);
Your original function also is correct but after deleteing all nodes
loeschen(pKopf);
you should set pKopf to NULL
pKopf = NULL;
Or you simply should not use this pointer to access the deleted nodes.:)
Take into account that this statement
std::cout<<s.data1<<"...."<<s.data2<<std::endl;
does not make sense because data members data1 and data2 of object s were not inistilaized when the object was created
domino s;
And this object has nothing common with the list.
And function readSteine does not use node pHead defined in main
domino *pHead=NULL;
//...
readSteine();
You shpuld rewrite the function. It is wrong.

Your loeschen will never works becouse you pass to it just a NULL.
You first need to correctly construct the list. First, you need to pass your Kopf (a kind of reference to it) to the readSteine function, and assign it to each new next, and after that assign to it the new domino, and so on to form the linked list..
I strongly recommend you to read Programming: Principles and Practice Using C++ .
You mix C functions and style of programming with C++. You dont even need to know these C functions which are hard to learn right in order to begin to learn C++.

Related

Inserting element into linked list while traversing C++

I'm new to C++ and having some difficultly with the use of shared ptrs. I've been able to construct a linked list. I want to traverse the linked list, and when the nodes (elements) have a valid to_add field, to add the element into the list, and continue.
class Element{
public:
char el = 'Z';
std::shared_ptr<Element> next;
char to_add = 'Z';
Element(char input){
el = input;
next = nullptr;
}
};
^ structure of node. I'm looking at the to_add field to see if there's an element to add in.
class Polymer{
public:
std::vector<Instruction> instruction;
std::shared_ptr<Element> extract_and_store_input(std::ifstream &file);
void prepare_insertion(std::shared_ptr<Element> head);
void insert_elements(std::shared_ptr<Element> head);
Polymer(std::ifstream &file){
std::shared_ptr<Element> head = extract_and_store_input(file);
prepare_insertion(head);
insert_elements(head);
}
};
^ calling code format
v problem is somewhere here
std::shared_ptr<Element> add_element(std::shared_ptr<Element> head_cpy){
auto tmp = head_cpy->next;
head_cpy->next = std::make_shared<Element>(head_cpy->to_add);
head_cpy->next = tmp;
return tmp;
}
void Polymer::insert_elements(std::shared_ptr<Element> head) {
auto trav = head;
auto tmp= head;
// for (int i = 0; i < num_steps; i ++)
while (trav->next != nullptr) {
std::cout<<trav->el<<std::endl;
if (trav->to_add != 'Z'){
std::cout<<"adding"<<trav->to_add<<std::endl;
trav=add_element(trav);
trav=trav->next; // progress forward twice
}
trav=trav->next;
}
// while (tmp->el != 'Z'){
// std::cout<<tmp->el<<std::endl;
// tmp=tmp->next;
// }
}
I think that the issue may be that the pointer doing the traversal (trav) is updating the list itself, rather than progressing through the list? I would really appreciate any insights!
If anyone recognises the q, it's from AoC day 14 :) I'm sure there are better ways to do it, but I'd like to understand the problem here first!

insert function points to new data

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;
}
}

Adding to the front of a Simple Linked List

I'm trying to implement a linked list, with a addToFront function to start with.
Where here I'm just adding the number 5, to the front of the list. I know that if the list is empty, the list pointer should be Null, however, this does not seem to be the case.
EDITED FILES:
I've edited the files( thanks to taskinoor's answer), which now provide the output of
0 5
Instead of
5
I have the header file:
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
typedef struct List {
struct list * next;
int value;
int size;
}list;
void addToFront(int num, list **l);
void printList(list * l);
int getSize(list * l);
void initialize(list * l);
void freeList(list *l);
A c file "main.c"
#include "Header.h"
int main() {
list l;
initialize(&l);
addToFront(5, &l);
printList(&l);
_getch();
freeList(&l);
return 0;
}
void printList(list * l) {
list *current = l;
while (current != NULL) {
printf("%d ", current->value);
current = current->next;
}
}
void freeList(list *l) {
list *current = l;
while (current != NULL) {
list *tmp = current;
current = current->next;
free(tmp);
}
}
And an interface c file (which is incomplete)
#include "Header.h"
int getSize(list * l) {
return l->size;
}
void initialize(list * l) {
l->next = NULL;
l->value = 0;
l->size = 0;
}
// need to pass **l to update it
void addToFront(int num, list **l) {
// allocate memory for new node
list *tmp = (list *)malloc(sizeof(list));
tmp->value = num;
// new node should point to whatever head is currently pointing
// even if head is NULL at beginning
tmp->next = *l;
// finally l needs to point to new node
// thus new node becomes the first node
*l = tmp;
}
However, when the addToFront function is called, the if statement is never executed. Which doesn't make sense, if the list is empty, shouldn't the list pointer be null?
Next I tried to manually set l == NULL in the Initialize function, but that didn't do anything either. Also, my print function loops infinity, which I presume is an issue with malloc. Any help would be greatly appreciated.
The condition if (l == NULL) is not true in addToFront because l is NOT NULL here. You have called l = malloc(sizeof(list)); at the beginning of the main which made l not NULL. There is no need to malloc and initialize l in this way. I suppose that by l you meant the head pointer to the list. This should be NULL at beginning (i.e. do not call malloc at main and assign the returned address to l) and your memory for node should be allocated in addToFront like this:
// need to pass **l to update it
void addToFront(int num, list **l) {
// allocate memory for new node
list *tmp = (list *) malloc(sizeof(list));
tmp->value = num;
// new node should point to whatever head is currently pointing
// even if head is NULL at beginning
tmp->next = *l;
// finally l needs to point to new node
// thus new node becomes the first node
*l = tmp;
}
Remove malloc from main.
int main() {
list *l;
addToFront(5, &l); // pass address of l
printList(l);
// other stuffs
}
And printing will be like this:
void printList(list * l) {
list *current = l;
while (current != NULL) {
printf("%d ", current->value);
current = current->next;
}
}
Finally it is not enough to free only l. You need to traverse whole list and free every node in it.
void freeList(list *l) {
list *current = l;
while (current != NULL) {
list *tmp = current;
current = current->next;
free(tmp);
}
}
Ok, lets begin from the last part:
I tried to manually set l == NULL in the Initialize function, but that didn't do anything either
Actually it did something, but once you returned from the initialize function, that change was lost. Here's why:
When you say initialize(l), in the initialize function body you get a copy of the original pointer l. Then you make that pointer, point to NULL. When that function returns the original pointer l still points to the initial memory (the one allocated with malloc). If you do want this initialize function to have such behavior, you should change it to:
initalize(list **l)
Regarding the addToFront() function, actually if it was executed, you would get a segfault! You check:
if (l == null)
and if it is, you try to dereference the NULL pointer!
l->value = num;
l->next = NULL;
l->size++;
Lastly, in your print fucntion you do not advance your pointer. You should write something like
l=l->next
in order to work
This is because of your printing function.
your are only printing the values that have a next node after them. so having only 1 value will not print anything. instead you should have:
void printList(list * l) {
while (l !=NULL)
{
printf("%d ", l->value);
l=l->next;
}
}
also in your addToFront function, you have a logical error, you are only setting the data and size IF the list passed in is in fact NULL, which is not the case.
Use:
void addToFront(int num, list **l) {
list *tmp= malloc(sizeof(list));
tmp->value = num;
tmp->next = *l;
*l= tmp;
}
Note: initialize is not needed. Just pass an empty or non-empty list so main can just do:
int main()
{
list * l= NULL;
addToFront(5, &l);
...
See ForeverStudent his solution to fix the print bug.

C++ classes & linked list: adding & counting items

I need to make a linked list with classes. Each list will store two values: a URI and IP. After doing all the adding, I need to be able count the total number of items in the linked list. I have tried the following code but it doesn't compile. We are not allowed to use std::list. Any suggestions please?
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
class ip_uri_store {
protected:
string ip;
string uri;
ip_uri_store *next;
public:
ip_uri_store(string huri, string hip);
void additem(string auri, string aip);
void deleteitem(string duri);
void findbyuri(string furi);
void findbyip(string fip);
int totalitems();
};
ip_uri_store *head = NULL;
ip_uri_store *curr = NULL;
void ip_uri_store::additem(string auri, string aip)
{
curr = head;
while (curr->next != NULL) {
curr = curr->next;
}
curr->uri = auri;
curr->next = new ip_uri_store;
curr->ip = aip;
curr->next = new ip_uri_store;
curr = curr->next;
curr = head;
}
int ip_uri_store::totalitems()
{
int i = 0;
curr = head;
while (curr->next != NULL) {
i += 1;
curr = curr->next;
}
return i;
}
int main(int argc, char *argv[])
{
if (argc == 1) {
cout << "123456, 123456#student.rmit.edu.au, Gordon Brown" << endl;
return (0);
}
head = new ip_uri_store;
curr = head;
int i;
for (i = 1; i < argc; i++) {
if (argv[i][0] == 'A') //add item
{
ip_uri_store.additem(argv[i + 1], argv[i + 2]);
i += 2;
}
else if (argv[i][0] == 'N') //print total tiems
{
cout << ip_uri_store.totalitems() << endl;
}
else {
cout << "command error\n";
return 0;
}
}
return (0);
}
Your ip_uri_store::additem() is pretty messed up. In it you change the curr object before you assign a new value to it:
curr->uri = auri;
curr->next = new ip_uri_store;
In doing so you change the last item in the list instead of assigning auri to the new item added later. (Interestingly, you got the order right with ip.)
I like the idea of giving pieces of code names, so that you can read what they do. Functions are what this is done with. For example, I'd factor out the code that finds the last list node
ip_uri_store *find_last_node(ip_uri_store *curr)
{
while (curr->next != NULL) {
curr = curr->next;
}
return curr;
}
and call it from ip_uri_store::additem():
void ip_uri_store::additem(string auri, string aip)
{
ip_uri_store *curr = find_last_node(head);
// ...
Now create a new object and remember its address in curr->next
// ...
curr->next = new ip_uri_store(auri,aip);
}
Your ip_uri_store::totalitems() returns an int. Why? Do you ever expect the count of objects to be negative? Better return an unsigned type.
You should consider what happens when you delete a list node. If it still points to a next object, chances are the pointer isn't stored anywhere else, and so the object (and those it points to) is (are) leaking. One way to deal with that is to write a destructor for ip_uri_store which deletes next. (If you want to delete a node without having it delete its own tail, you could assign NULL to its next pointer first.)
Also, according to the Rule of Three, you need to think about copying of list nodes. That's not easy to get right in the first try, so you might just want to forbid it:
class ip_uri_store {
private:
ip_uri_store(const ip_uri_store&); // not implemented
ip_uri_store& operator=(const ip_uri_store&); // not implemented
// ...
Instead of using global variables, you put them into class. That way you could have more than one list. Rename ip_uri_store to ip_uri_store_impl and pout it into a new ip_uri_store class:
class ip_uri_store {
private:
class ip_uri_store_impl { /* ... */ };
ip_uri_store_impl* head;
};
Since this class manages dynamically allocated objects, you need to think about destruction and copying such objects.
The wrapper class should have public methods that invoke the methods of ip_uri_store_impl whenthey nedd to. Functions like totalitems(), which operate on the whole list (instead of a node), should probably be implemented in the wrapper class.
You need to provide the two arguments to the constructor of your ip_uri_store class:
// The constructor call needs two arguments
curr->next = new ip_uri_store(huri, hip);
You cannot call instance methods on the class itself:
// Invalid, totalitems() is valid only on instances of ip_uri_store.
cout << ip_uri_store.totalitems() << endl;
Why are the variables head and curr global? They really should be data members of a class.
Pull out the ip, uri and next members of ip_uri_store and put them in their own structure, say ip_uri_store_node. Then, ip_ur_store_node can define a constructor that initializes them. Then make ip_uri_store hold the head and curr pointers to ip_uri_store_node instances.
This is what I mean:
struct ip_uri_store_node
{
string ip;
string uri;
ip_uri_store_node* next;
ip_uri_store_node(const char* u, const char* i)
: ip(i), uri(u), next(0) {}
};
class ip_uri_store
{
private:
ip_uri_store_node* head;
ip_uri_store_node* curr;
public:
// Initializes head and curr
ip_uri_store();
// These functions woud act on head and curr.
void additem(string auri, string aip);
void deleteitem(string duri);
void findbyuri(string furi);
void findbyip(string fip);
int totalitems();
};
int main()
{
ip_uri_store list;
// Do things with the list...
return 0;
}
The function additem can create new instances of ip_uri_store_node like this:
curr->next = new ip_uri_store_node(auri, aip);
The rest is up to you.

Simple Linked List Implementation in C++

I'm a programming student in my first C++ class, and recently we covered linked lists, and we were given an assignment to implement a simple one. I have coded everything but my pop_back() function, which is supossed to return a pointer to the Node that needs to be deleted in Main(). No Node deletion is to be done in the actual function. So my question is:
Would you be willing to help point me in the right direction for my pop_back() function? Also, if you notice anything else that I'm doing wrong, let me know.
Also, this linked list is just to work with strings. In this case, a grocery list, so one string for the quantity of the item(1,2), and one string for the item type. (Milk, Eggs, etc.)
Below I've included my List & Node class implementations, so you can get an idea of what I've done so far.
Node.cpp
Node::Node(void)
{
descrip = " ";
quantity = " ";
previous = NULL;
next = NULL;
}
Node::Node(string q, string d)
{
descrip = d;
quantity = q;
previous = NULL;
next = NULL;
}
Node* Node::GetNext()
{
return next;
}
Node* Node::GetPrevious()
{
return previous;
}
void Node::SetNext(Node * setter)
{
next = setter;
}
void Node::SetPrevious(Node * setter)
{
previous = setter;
}
List.cpp
List::List(void)
{
first = NULL;
last = NULL;
numNodes = 0;
}
Node* List::GetFirst()
{
return first;
}
Node* List::GetLast()
{
return last;
}
void List::SetFirst(Node* setter)
{
first = setter;
}
void List::SetLast(Node* setter)
{
last = setter;
}
int List::GetNumNodes()
{
return numNodes;
}
void List::push_front(Node* item)
{
if (first == NULL)
{
first = item;
last = item;
}
else
{
Node* pFirst = first;
item->SetNext(pFirst);
first = item;
numNodes++;
}
}
void List::push_back(Node * item)
{
if (last == NULL)
{
first = item;
last = item;
}
else
{
last->SetNext(item);
last = item;
numNodes++;
}
}
Node* List::pop_front()
{
Node* temp = first;
first = first->GetNext();
if (first == NULL)
{
temp = first->GetNext();
first = p;
}
if (first == NULL)
{
last = NULL;
}
if (numNodes > 0)
{
numNodes--;
}
return temp;
}
Node* List::pop_back() // this whole function may be wrong, this is just my attempt at it
{
Node* temp;
temp = first;
while((temp->GetNext()) != NULL)
// im stuck here
}
Some pointers:
0x1243bfa3
0x45afc56e
0xdeadbeef
Some more pointers:
You should prefer to initialize your class members in the initialization list, not in the constructor's body.
In C++, unlike C89, we declare and define a function with no parameters as void f();, not void f(void);.
In C++ we commonly reset pointers with 0, not NULL.
See below for what I mean in code.
Good C++ code will try to take advantage of RAII. This implies avoiding primitive pointers for the most part. In this case plain old std::auto_ptr<> would make a perfectly sufficient substitute for the primitve Node* pointers. However, I do reckon part of the exercise here is pointer arithmetics, and so I just leave this as a side-note.
It would be useful for us if you'd attach the class declarations. I assumes all those accessors and mutators, GetFirst() and SetFirst() etc., are there because they are public. That's a bad idea. First, they expose the private pointers, which defeats the whole point of accessor. Second, they don't do anything special so they're just extra code -- which means extra room for bugs. This brings me to the next point.
Your mutators are incorrect. You blindly assign a new value to the private member pointer, without deleting what you had before. That's a memory leak.
Ever tried to pop_front() when the list is empty?
Finally, 8 being a round number it's time we get to the question at hand. pop_back(). My question to you is, why are you traversing the list all the way to the end if you so meticulously maintain a pointer to the last node of your list? Indeed, if you wouldn't bother with maintaining a pointer to the end of the list then you'd have to traverse all the way to the last node in order to pop it. And for that you were in the right direction. Except that ...
When you access members through pointers, as in first->GetNext(), always make sure first isn't a null pointer -- or else state in the function's documentation comment that you assume the pointer is not null.
These should get you started.
Points 1, 2 and 3 in code:
Node::Node()
: descrip(" "), quantity(" "), previous(0), next(0)
{
}
So if I understand this right you just want to run through your linked list until you get to the last node in the linked list and return the pointer to it?
I'm pretty sure what you have there will do it except
Node* List::pop_back() // this whole function may be wrong, this is just my attempt at it
{
Node* temp;
temp = first;
while(temp->GetNext() != NULL)
{
temp = temp->GetNext();
}
return temp;
}
So if I read it right, there it will continually loop around until it gets to the node with none in the line behind it, then return it.
I like the previous posters answer, but one thing you might want to keep in mind is if you have an empty list. Then your first pointer will equal NULL and you would be trying to call NULL->GetNext() basically and Seg Fault. I think you can edit the above code slightly and still get have it work like this:
Node* List::pop_back()
{
Node* temp;
temp = first;
while(temp != NULL && temp->GetNext() != NULL)
{
temp = temp->GetNext();
}
return temp;
}
This will have the function return NULL if there is nothing in the list and still work properly.
It would definitely have helped me if you also had posted your class declaration. I cannot guarantee that the below is correct but it makes sense to me
Node* List::pop_back()
{
Node *temp = NULL;
if(numNodes == 1)
{
temp = first;
// setting the list pointers to NULL
first = NULL;
// setting the list pointers to NULL
last = NULL;
//You should also probably remove the links from your node
//to the next and previous nodes but since you didn't specify
//this it is up to you
numNodes--;
}
else if(numNodes > 1) //more than one element
{
//the pointer you want to return
temp = last;
//For clarity I am creating another variable here
Node *newLast = temp->GetPrevious();
//Setting the new last node to point at nothing so now temp
//is "disconnected from the list"
newLast->next = NULL;
//the last pointer of the list is now pointing at the new last node
last = newLast;
//You should also probably remove the links from your node
//to the next and previous nodes but since you didn't specify this it is up to you
numNodes--; //decrement the counter
}
return temp;
}