How to "completely" delete all nodes in a linked list? - c++

I got curious about how pointers and deleting pointers worked in C++ so I set up an experiment. I made a very simple singly linked list and the following recursive function that deletes all nodes in a list:
void deleteList(Node *node) {
if (!node)
return;
deleteList(node->next);
cout << "deleting node " << node->data << endl;
delete node;
node = nullptr;
}
I suppose that this successfully deletes all nodes in a list. However, after calling this function in main, I check to see if the head node still exists:
List list;
// appending a bunch of numbers to the list...
list.deleteList(list.head);
if (list.head)
cout << true;
this will print 1 to the console, meaning that the head does indeed still exist. I would expect the head, and all other nodes after it, to be null and hence that the if condition fails, since setting pointers to null is the last thing I do in the recursive function. So why does the program report that the head still exists?
edit: changed List list(); to List list;

You freed the memory, but the assignment to nullptr only affected the copy of the pointer passed to the function, not the original pointer in the caller.
If you declared the function as receiving the pointer by reference:
void deleteList(Node *&node) {
then the assignment of node = nullptr; would affect the caller as well.
Mind you, since you tagged this C++11, it's usually much simpler to just define the linked list as a series of std::unique_ptr<Node>s in the forward direction (raw pointers in the reverse direction if it's bidirectional to avoid reference cycles), so you can avoid the need for a special deleter function, and just set the head pointer to nullptr and let C++ do the work of cascading the deletion.
Edit: There is a flaw to letting std::unique_ptr do the work; as pointed out in the comments, this means the list size is effectively limited by the stack, and too large lists will cause stack overflow when you delete them. So explicitly clearing one by one (the simplest approach being to implement popping properly, and have clearing simply be popping until head is converted to a nullptr by the popleft method) would be safer. I left the original suggestion in place for posterity, so this explanation makes sense.

Related

Can we implement a link list without using the head pointer means by using a simple variable of the head instead of the pointer of the head?

Can we implement a link list without using the head pointer means by using a simple variable of the head instead of the pointer of the head ?
Yes. If you are implementing a circular linked list with a sentinel node, the sentinel node can be the simple variable that also serves as the head.
Alternatively, you could use a std::optional instance to serve as the head.
In specific cases you could, but in general not. And why would you want to? Here are some reasons, I could think of now. Take for example this code:
template<class T>
class Node
{
private:
T value;
Node<T> *next;
};
class MyLinkedList
{
private:
bool isEmpty; // indicates wether the list is empty or not
Node head; // Head as member
};
But there are several major flaws with this code:
You would always need to care about isEmpty when adding or deleting, or doing anything with the list
You can't initialize head if T has no default constructor
When deleting the last element you have to call the destructor of object that technically remains in scope.
When deleting the last element and then deleting the empty list the destructor of Node::value will be called twice
Don't know if those are all reasons, but I think, just #2 is a big enough problem to not consider this.
Of course you could use std::optional, but that's just a pointer with a wrapper. which even works, without a default constructor, so could be an alternative. Alltough it would be used in the same way as a (smart) pointer, so it's not "a simple variable of the head".

Stack overflow with unique_ptr linked list [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I've converted the following linked list struct
struct node {
node* next;
int v;
};
into a c++11 version - that is not using the pointers.
struct node {
unique_ptr<node> next;
int v;
};
Adding, removing elements and traversing works fine, however when I insert roughly 1mil elements, I get a stack overflow when when the destructor of the head node is called.
I'm not sure what I'm doing wrong.
{
node n;
... add 10mill elements
} <-- crash here
As explained in the other answers, you segfault because of the recursive implicit destructor. It is possible to fix this without resorting to raw pointers, having to trust the compiler or writing a custom allocator:
~node() {
for (std::unique_ptr<node> current = std::move(next);
current;
current = std::move(current->next));
}
Here you iteratively go through the chain of pointers. This will, one at a time, unchain one pointer and change ownership std::move(current->next) to current. At the same time the previous unchained pointer, owned by current, will be released while being overwritten by the move assignment.
You may find the explicit variant more straightforward:
current.reset(current->next.release()));
Is effectively the same as:
current = std::move(current->next));
I prefer the move version, because it does at no time leave you with a raw pointer. But in that case it does not make a difference.
You are making nothing wrong here.
When you create your list of 10 millions elements, allocation each node with make_unique everything is fine (Of course the data is not on the stack, except perhaps the first node !).
The problem is when you you get rid of the head of your list: unique_ptr will take care of the deleting the next node that it owns, which also contains a unique_ptr that will take care of deleting the next node... etc...
So that in the end the 10 millions elements get deleted recursively, each recursive call taking some space on the stack.
By default std::unique_ptr calls the operator function of structure std::default_delete that just executes operator delete.
So each operator function of the structure std::default_delete recursively calls itself for data member next of structure node.
As result you get the stack overflow.
You would get the same result if you used ordinary pointers instead of the pointers of type std::unique_ptr but added a destructor to the structure node the following way
struct node {
node* next;
int v;
~node() { delete next; }
};
Or even like
struct node {
node* next;
int v;
~node() { if ( next ) delete next; }
};
for a list with a big number of nodes because the destructor will be called recursively
Because when you destroy the head node element, it calls destructor oа unique_ptr, which destroys the second element that calls destructor of the 3rd element which calls ... etс 1mil times.
So, you have 1 mil nested funtion calls (of destructors). Each function call takes memory in stack at least to store return address (and parameters and local variables as well, if needed). Naturally, stack can not provide such amount of memory. You should redesing code to resolve it. For instance, rewrite destructor of Node class so that it finds the last list element and then destroys it and all other nodes from the end in a cicle, not recursievely.

Implementing a templated doubly linked list of pointers to objects

I'm a little confused about implementing a doubly linked list where the data in the list are pointers.
The private part of my linked list class looks like:
private:
struct node {
node* next;
node* prev;
T* o;
};
node* first; // The pointer to the first node (NULL if none)
node* last; // The pointer to the last node (NULL if none)
unsigned int size_;
As you can see, the list is full of pointers to objects rather than just plain old objects, which makes it a little more confusing to me.
The following is the description in the spec:
Note that while this list is templated across the contained type, T, it inserts and removes only pointers to T, not instances of T. This ensures that the Dlist implementation knows that it owns inserted objects, it is responsible for copying them if the list is copied, and it must destroy them if the list is destroyed.
Here is my current implementation of insertFront(T* o):
void Dlist::insertFront(T* o) {
node* insert = new node();
insert->o = new T(*o);
insert->next = first;
insert->prev = last;
first = insert;
}
This seems wrong though. What if T doesn't have a copy constructor? And how does this ensure sole ownership of the object in the list?
Could I just do:
insert->o = o;
It seems like this is not safe, because if you had:
Object* item = new Object();
dlist.insertFront(item);
delete item;
Then the item would be also be destroyed for the list. Is this correct? Is my understanding off anywhere?
Thanks for reading.
Note: While this looks like homework, it is not. I am actually a java dev just brushing up my pointer skills by doing an old school project.
When you have a container of pointers, you have one of the two following usage scenarios:
A pointer is given to the container and the container takes responsibility for deleting the pointer when the containing structure is deleted.
A pointer is given to the container but owned by the caller. The caller takes responsibility for deleting the pointer when it is no longer needed.
Number 1 above is quite straight-forward.
In the case of number 2, it is expected that the owner of the container (presumably also the caller) will remove the item from the container prior to deleting the item.
I have purposely left out a third option, which is actually the option you took in your first code example. That is to allocate a new item and copy it. The reason I left it out is because the caller can do that.
The other reason for leaving it out is that you may want a container that can take non-pointer types. Requiring it to be a pointer by always using T* instead of T may not be as flexible as you want. There are times when you should force it to be a pointer, but I can't think of any use (off the top of my head) for doing this for a container.
If you allow the user to declare Dlist<MyClass*> instead of Dlist<MyClass> then the owner of that list is implicitly aware that it is using pointers and this forces them to assume scenario Number 2 from above.
Anyway, here are your examples with some commentary:
1. Do not allocate a new T item unless you have a very good reason. That reason may simply be encapsulation. Although I mentioned above that you shouldn't do this, there are times when you may want to. If there is no copy constructor, then your class is probably plain-old-data. If copying is non-trivial, you should follow the Rule of Three.
void Dlist::insertFront(T* o) {
node* insert = new node();
insert->o = new T(*o); //<-- Follow rule of three
insert->next = first;
insert->prev = last;
first = insert;
}
2. This is what you would normally do
insert->o = o;
3. You must not delete your item after inserting. Either pass ownership to your container, or delete the item when neither you nor the container requires it anymore.
Object* item = new Object();
dlist.insertFront(item);
delete item; //<-- The item in the list is now invalid

C++ vector implementation - removing elements

I'm implementing a vector type. I'm not troubled by the algorithms or the data structure at all but I am unsure about a remove method. for instance:
bool Remove(Node* node)
{
/* rearrange all the links and extract the node */
delete node;
}
where node is a pointer to the current node that we are at. But if I delete node then how do I prevent this from happening:
Node* currentNode = MoveToRandNode();
Remove(currentNode);
cout << currentNode->value;
If currentNode were a pointer to a pointer it would be easier but...it's not.
You could add another level of abstraction to your iterator (which now is a raw pointer)
If you do not handle raw pointers, but create some sort of iterator class instead of a pointer, it is possible to invalidate the iterator, and thus failing controlled if anyone tries to access the iterator after it has been removed.
class Iterator {
Node operator*() {
if (node) return *node;
else throw Something();}
private:
Node* node;
}
Of course this wrapping of a pointer will come at a cost of some overhead (checking the pointer on each deref). So you will have to decide how safe you want to play. Either document as suggested by others or wrap for safety.
Step back first. You need to define who "owns" the memory pointed to by the vector. Is it the vector itself, or the code that uses the vector? Once you define this, the answer will be easy - either Remove() method should always delete it or never.
Note that you've just scratched the surface of the possible bugs and you answer to "who owns it" will help with other possible issues like:
If you copy a vector, do you need to copy the items within it, or just the pointers (e.g. do a shallow or deep copy
When you destroy a vector, should you destroy the items within it?
When you insert an item, should you make a copy of the item, or does the vector take ownership of it?
well, you cannot do that, but some modifications to your code can improve safety.
Add ref
bool Remove(Node*& node)
{
/* rearrange all the links and extract the node */
delete node;
node = nullptr;
}
check for nullptr
if(currentNode)
cout << currentNode->value;
probably you need to try std::shared_ptr
This is similar to "iterator invalidation". E.g., if you have a std::list l and a std::list::iterator it pointing into that list, and you call l.erase(it), then the iterator it is invalidated -- i.e., if you use it in any way then you get undefined behavior.
So following that example, you should include in your documentation of the Remove method something along the lines: "the pointer node is invalidated, and may not be used or dereferenced after this method returns."
(Of course, you could also just use std::list, and not bother to re-invent the wheel.)
For more info on iterator invalidation, see: http://www.angelikalanger.com/Conferences/Slides/CppInvalidIterators-DevConnections-2002.pdf
In addition what innochenti wrote.
I think you have to decide what is expected/desired behavior of cout << currentNode->value;:
Error - (as innochenti wrote node = nullptr)
Default Value - create node devault_value (which has some default value for its value), and after delete node; do node=default_value

Correct way of erasing a linked list

Suppose, I have a singly linked list and its basic building block is,
struct Node {
Data d;
Node *pNext;
// methods
~Node();
};
The head of the linked list is stored as,
Node *m_Head; // member of some class
When, I am done with the list, I will clean it by deleting each node as,
void Erase()
{
Node *pIter, *pTemp = m_Head;
while((pIter = pTemp) != 0)
{
pTemp = pIter->pNext;
delete pIter;
pIter = pTemp;
}
}
I thought, if I can simplify this. So I came up with an idea where I can clean this whole linked list with just a single instruction !
delete m_Head;
and destructor will look like:
Node::~Node() { delete this->pNext; }
Here my concern is, will it cause recursion (implicitly due to delete) ? If yes, then it's definitely a concern for bigger linked lists. Will compiler be able to help in any way for optimizing that ?
[Note: Not using any library facility like std::list or other.]
I think the question that you have to ask is, does each Node in the list own its pNext Node? If not, then it has no business deleting its pNext node in its destructor.
In most linked list implementations all the nodes are owned by the list, a node doesn't own all the nodes after it in the list. It makes more sense to keep the nodes as dumb (POD-structs) and let all of the logic reside in the list.
It's definitely a design "smell" that your node has a destructor but no copy constructor or copy assignment operator. I think this approach will cause more complexity when you come to code implementing insert, splice and erase single element functions as you will have to manually manage the pNext pointers in any case to avoid unintentional destruction of the entire tail of a list.
Of course: Only do this for learning purposes or when you are sure that your own List is really better for your use case
It depends. Possibly your compiler will detect a tail recursion and emit code that is conceptually equivalent to using a loop.
If not, then yes, it will recurse. Usually, some thousands of recursions should be possible on commodity boxes, if stack pressure is small (like in your case). However, there is no guarantee, and indeed, for really large lists, this can be a problem.
Also, I think that recursion is indeed not entirely appropriate for the concept of sibling nodes. A node hierarchy, like with quadtrees, cries for recursion, but I have a not so good time thinking in recursion (which forms a call hierarchy) when the list-concept is about sibling-nodes.
You may also consider the manual loop as a easy-to-achieve optimization over the recursion that will make your code more robust in a guaranteed way.
Btw, you could also should rip out the deletion of nodes into a holder class:
class List {
public:
~List() {
for-each-node
delete-node
}
private:
class Node {
Node *node_;
...
};
...
};
This is basically how the standard library's list is usually implemented. It makes the whole implementation easier to achieve and conceptually more correct (Nodes don't own their sibling logically)
Most compiler do tail call elimination in default setting. Some smarter one can convert non-tail calls to tail calls.
So, this method okay as long as you have some optimization turned on.
Raw pointers ? Manual calls to delete ?
Why not, simply:
struct Node {
Data d;
std::unique_ptr<Node> next;
};
Then you don't even have to worry about memory management at all, it's automatic!