So I'm working on making a linked list, and everything about it is working exactly as I want it to except for the list's destructor:
PhoneList::~PhoneList()
{
PhoneNode *ptr = head;
while (ptr != NULL)
{
head = head->getNext();
delete ptr;
ptr = head;
}
}
When the program runs, it does everything else I need it to, but then seems to loop indefinitely on the destructor when it tries to call it and end the program. This is my first question here, and I'm a bit of a newcomer to this whole programming thing, so if you need to see any more of my program code to make sense of this destructor I will gladly share it!
You would be better off adding a destructor for PhoneNode, such that each each deletes its successor. Then you don't need the loop at all:
PhoneNode::~PhoneNode()
{
delete next;
}
Note that this traverses the entire remaining list.
then your PhoneList destructor just looks like:
PhoneList::~PhoneList()
{
delete head;
}
Take care in your Delete(int id) method that you zero the next pointer of a node you're going to delete if it's in the middle of the list (i.e. it isn't already zero). Otherwise you lose the entire rest of the list.
Related
I was reading this: https://www.geeksforgeeks.org/reverse-a-linked-list/
I think I found an easier answer but since it wasn't written their and they used more complicated one I think something is wrong with mine and can't figure it out.
We start from the first node which we will copy and insert it into a new list.
then we go one step to the right, copy the value, create a new list with that value and setting its right the previous list and so on.
What's wrong with my algorithm?
If I interpret your idea correctly, it is something like this:
void forward_list::reverse() {
forward_list new_list;
for(auto& v : *this)
new_list.emplace_front(std::move(v));
std::swap(new_list, *this); // or *this = std::move(new_list);
}
... and this would work. It even looks pretty nice I'd say.
What's wrong with my algorithm?
It's creates a new node for every old node and then has to copy/move the data from the old node to the new node. The old nodes will then be destroyed.
Some types aren't even copyable or moveable so this reverse algorithm couldn't be used with such types.
It invalidates references and iterators.
Consider the alternative, reversing the links. It's a bit more complex but gets the job done without any of the drawbacks mentioned above.
Here's my take on reversing the links, which is not implemented exactly the same way as in the link you shared but it works pretty much in the same way. I think this one has fewer assignments though.
curr will point one step ahead of head and next is used to save the next pointer when the relinking is being done.
void forward_list::reverse() {
if(head) { // must have at least one node
node* curr = head->next; // head + 1
head->next = nullptr; // this will be the new last node
node* next; // for saving next while relinking
while(curr) { // while curr != nullptr
next = curr->next; // save the next pointer
curr->next = head; // relink backwards
head = curr; // move head forward
curr = next; // move curr forward
}
// head now points at the new start of the list automatically
}
}
Your algorithm is functionally correct, but since you are creating an entirely new list instead of reversing the existing nodes in-place, you are using twice the memory. You also have to deal with the cleanup of deleting the old nodes once you have your new list.
I'm stuck on a linked list destructor for my class. This is what I have here:
LinkedList::~LinkedList()
{
LinkedList *forward = nullptr;
LinkedList *current = this;
//iterate through list, deleting each element as we go
while (current != nullptr)
{
//set next pointer to current's next
forward = current->next;
delete current; //delete the current memory
current = forward; //reset current to next's pointer
}
}
When I run it, I get a seg fault. I only want to delete just one node from my linked list. Is that possible? Also, I wasn't given a "head" pointer as I was used to from other lists, so I used "this" instead, does that work?
Aka - the .cpp is finding the spot in the linked list to delete, reorganizing the next pointers around it, and then deleting the node (which calls this destructor)
(when I run my program with an empty destructor, it prints out fine, but of course there are memory leaks)
Any help is appreciated!
by definition delete calls LinkedList::~LinkedList, so you have several (in fact infinite) calls to it because of the loop calling delete, so you access to already deleted element with an undefined behavior
just do
LinkedList::~LinkedList()
{
if (next != nullptr)
delete next;
}
or just
LinkedList::~LinkedList()
{
delete next; // delete on nullptr does nothing
}
even personally I prefer to compare to nullptr first
I argee with bruno.
Besides, when you want to delete a node and you don't have the pointer to head, you need a double-linked list.
The double-linked list has two pointers to its prev and next node.
when you need to delete a node, like this:
ListNode* p;
if(p->pre)
p->pre->nxt = p->nxt;
if(p->nxt)
p->nxt->pre = p->pre;
p->nxt = p->pre = NULL;
delete p;
I wrote a singly linked list implementation using a struct. It is not part of an outer class that manages the operations on the list. Instead all of the operations are handled directly with the Nodes.
I understand that if the struct definition was part of a class, say ListManager, calling the destructor on a ListManager instance would just require one to iterate through the linked list managed by the class and delete each Node.
However, since this linked list is not part of an outer class and manages all of the operations itself I am a bit confused as to how to write the destructor.
Version 1 works well, its a recursive call that goes through the list and frees and memory associated with each Node.
Version 2 caused an infinite loop. I don't understand why, as this is one way that I would implement the destructor for a container class that manages the Node linked list.
Version 3 works well but is too verbose.
I ran all three versions using valgrind and python tutor to check for leaks and other issues.
Any help explaining why Version 2 does not work and why it is incorrect to implement the destructor in such a way is appreciated!
Struct Linked List
#include <iostream>
#include <string>
using namespace std;
struct Node
{
int id;
Node* next;
Node(int newId = 0, Node* newNext = NULL)
: id(newId), next(newNext) { }
};
Destructor Version 1
~Node()
{
if (next != NULL)
delete next;
}
Destructor Version 2
~Node()
{
Node* lead = this;
Node* follow = this;
while (follow != NULL)
{
lead = lead->next;
delete follow;
follow = lead;
}
}
Destructor Version 3
~Node()
{
Node* lead = this;
Node* follow = this;
if (follow != NULL)
{
lead = lead->next;
delete follow;
follow = lead;
}
}
Main
int main()
{
Node* head = NULL;
head = new Node(23, head);
head = new Node(54, head);
head = new Node(81, head);
head = new Node(92, head);
delete head;
return 0;
}
In version 2, you have written a loop that clears up the entire list in one destructor call by looping through the list and deleting every element. However, what happens is not that you have just one destructor call. Every time an element is deleted, that calls the destructor again.
So in the end, the delete follow translates to delete this (because follow = this;) for the first invocation. This then causes the destructor of the first node to be called again, causing the endless loop.
The following nodes would be destroyed multiple times, leading to undefined behavior, but it's not even getting there because of that infinite loop.
You only need each Node to delete (at most) one other Node, to eventually delete all the nodes. You re-assigning of local pointers does not affect the structure of the list.
Both 2 and 3 are delete this, which is suspicious at the best of times, plus some irrelevant ceremony, in the destructor. They are both undefined behaviour, deleting the same object (at least) twice.
Your first attempt is close.
Instead of confusing yourself with copying around pointer values, just use an owning pointer type, like std::unique_ptr.
struct Node
{
int id;
std::unique_ptr<Node> next;
Node(int id = 0, std::unique_ptr<Node> next = {})
: id(id), next(std::move(next)) { }
// correct destructor is implicit
};
I am trying to reverse a linked list using recursion. I made the reverse() function to reverse the list. I created a linked list in main() and also defined print() method.
I don't know what mistake I am making. Please help me correct it. The code snippets are given below.
struct node
{
int data;
struct node *next;
}*head;
void reverse(node **firstnode,node *n)
{
if(n==NULL)
{
head=n;
return;
}
reverse(&head,n->next);
struct node *q=n->next;
n->next=q;
q->next=NULL;
}
void main()
{
......
head=first;
reverse(&first,first);
print(head);
}
It may not address your question directly. However, you mentioned C++11 in the tags. So, take look at std::forward_list. It is a standard container that is based on single linked-list.
List* recur_rlist(List* head)
{
List* result;
if(!(head && head->next))
return head;
result = recur_rlist(head->next);
head->next->next = head;
head->next = NULL;
return result;
}
void printList(List* head)
{
while(head != NULL) {
std::cout<<head->data<<" ";
head = head->next;
}
}
void main()
{
List* list = createNode(2);
append(list, createNode(3));
append(list, createNode(4));
append(list, createNode(5));
append(list, createNode(6));
List* revlist = recur_rlist(list);
printList(revlist);
}
I think you mixed up your addressing at the end of the reverse function, it should probably look like:
q->next=n;
n->next=NULL;
Also, I am not sure if you need the "firstnode" argument.
Since you want to understand the code, and you have several great resources with finished code already, more finished code examples aren't needed. I'll just answer with some concepts and point you at the errors you need to fix.
First, some background concepts.
Linked lists: first and rest
Any linked list is either empty, or can be broken down into first (a node) and rest (a smaller linked list, or empty). This makes recursion much easier.
if (head){
node * first = head;
node * rest = head->next;
}
Invariant (simplified): A guarantee that is always true at the start and end of your function.
In a linked list, you expect that head points to a node, which points to another node, and so forth, until you get to the end, which is signaled by a nullptr. All of the nodes are different. All of the nodes are valid. These are the guarantees that must be true before you call your function and when your function returns.
In a recursive function, the invariants must hold on the sublist you are reversing at every step, because you return from the function at every step. But this makes recursion much easier because all you have to do is make sure that if the input is good, then your function will return a good value at the current step.
End of recursion:
You can prove that your recursive function never gets in an infinite loop by combining the previous concepts. If the invariants hold, then each step will work, and because each recursive call will take rest, which is guaranteed to be either nullptr or a shorter list, eventually we have to reach the end. And of course show that you handle the end.
Okay, on to the actual problems:
You don't handle end of recursion correctly. You just set head=nullptr at the end, and I'm pretty sure that's not what you want for head. You may want to handle the end if (nullptr == n->next), because then you know that is the last node. Of course, you still have to correctly handle the trivial case where nullptr==head.
You don't preserve invariants. You tried, but it looks like your bookkeeping is just all wrong. I suggest using the debugger or 3x5 notecards to step through what you're actually doing to fix the actual work of swapping things around. For example, it looks like you just confused which node is which in this code snippet:
struct node *q=n->next; // n is first, q is rest
// what if nullptr == q?
n->next=q; // n->next = n->next doesn't actually change anything
q->next=NULL; // this must already be true if reverse(rest) did its job
// q and n were swapped?
Also, your function takes "firstnode" but does not use it, but instead sets the global variable "head" as a side effect.
I have a class called "node". I link a bunch of node objects together to form a linked list. When the "node" destructor is called, it only deletes the first node. How do I iterate through the entire linked list of nodes and delete each node object?
Here is the class definition:
class Node
{
private:
double coeff;
int exponent;
Node *next;
public:
Node(double c, int e, Node *nodeobjectPtr)
{
coeff = c;
exponent = e;
next = nodeobjectPtr;
}
~Node()
{
printf("Node Destroyed");
}
The destructor is called by invoking delete on the pointer to the first node of the linked node list.
Since you don't know how many nodes there are in a list, if you do not have firm bounds on that it's not a good idea to invoke destructors recursively, because each call uses some stack space, and when available stack space is exhausted you get Undefined Behavior, like a crash.
So if you absolutely want to do deallocate following nodes in a node's destructor, then it has to first unlink each node before destroying it.
It can go like this:
Node* unlink( Node*& p )
{
Node* result = p;
p = p->next;
result->next = nullptr;
return result;
}
Node::~Node()
{
while( next != nullptr )
{
delete unlink( next );
}
}
But better, make a List object that has ownership of the nodes in a linked list.
Of course, unless this is for learning purposes or there is a really good reason to roll your own linked list, just use a std::vector (and yes I mean that, not std::list).
How do I iterate through the entire linked list of nodes and delete each node object?
It would be cleaner if you had a separate class to manage the entire list, so that nodes can be simple data structures. Then you just need a simple loop in the list's destructor:
while (head) {
Node * victim = head;
head = victim->next; // Careful: read this before deleting
delete victim;
}
If you really want to delegate list management to the nodes themselves, you'll need to be a bit more careful:
while (next) {
Node * victim = next;
next = victim->next;
victim->next = nullptr; // Careful: avoid recursion
delete victim;
}
Under this scheme, you'll also need to be careful when deleting a node after removing it from the list - again, make sure you reset its pointer so it doesn't delete the rest of the list. That's another reason to favour a separate "list" class.