Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stack overflow with unique_ptr linked list [closed]

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
like image 863
HappyKoding Avatar asked Feb 21 '16 11:02

HappyKoding


People also ask

What happens when unique_ptr goes out of scope?

unique_ptr. An​ unique_ptr has exclusive ownership of the object it points to and ​will destroy the object when the pointer goes out of scope.

What happens when you move a unique_ptr?

A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it.

Do I need to delete unique_ptr?

An explicit delete for a unique_ptr would be reset() . But do remember that unique_ptr are there so that you don't have to manage directly the memory they hold. That is, you should know that a unique_ptr will safely delete its underlying raw pointer once it goes out of scope.

When should we use unique_ptr?

Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.


2 Answers

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.

like image 198
Christophe Avatar answered Sep 22 '22 02:09

Christophe


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.

like image 35
Zulan Avatar answered Sep 23 '22 02:09

Zulan