Поп-метод связанного списка с использованием unique_ptr

Aug 20 2020

Я смотрю на реализации однократно связанный список , используя unique_ptrнаhttps://solarianprogrammer.com/2019/02/22/cpp-17-implementing-singly-linked-list-smart-pointers/. Мой вопрос относится к следующему методу:

 3 struct List {
 4     List() : head{nullptr} {};
 5 
 6     // ...
 7 
 8     void pop() {
 9         if(head == nullptr) {
10             return;
11         }
12 
13         std::unique_ptr<Node> temp = std::move(head);
14         head = std::move(temp->next);
15     }
16 
17     // ...
18 };

Интересно, зачем тут временное? Почему ты просто не мог сделать head = std::move(head->next)? Это потому, что это приведет к утечке памяти? При headпереназначении unique_ptrавтоматически освобождает ли текущая память, на которую он указывает?

У меня создалось впечатление, что умные указатели надежно защищают от утечек памяти. Кажется, в этом случае может быть утечка памяти для оригинала, headпотому что на него больше не будет указывать умный указатель?

Ответы

4 RemyLebeau Aug 20 2020 at 07:36

Интересно, зачем тут временное?

На самом деле он не нужен , но и использовать его тоже неплохо .

Почему ты просто не мог сделать head = std::move(head->next)? Это потому, что это приведет к утечке памяти?

Ты можешь. В этом примере утечки не будет.

При headпереназначении unique_ptrавтоматически освобождает ли текущая память, на которую он указывает?

Да. Однако старый указатель не изменится deleteдо тех пор, пока право собственности на новый указатель не будет передано первым. По ссылке:

https://en.cppreference.com/w/cpp/memory/unique_ptr/operator%3D

Передает право собственности от rк, *thisкак если бы путем вызова с reset(r.release())последующим назначением get_deleter()от std::forward<E>(r.get_deleter()).

https://en.cppreference.com/w/cpp/memory/unique_ptr/reset

Заменяет управляемый объект.

  1. Учитывая current_ptr, что указатель, которым управлял *this, выполняет следующие действия в следующем порядке:

    1. Сохраняет копию текущего указателя old_ptr = current_ptr
    2. Заменяет текущий указатель аргументом current_ptr = ptr
    3. Если старый указатель был непустым, удаляет ранее управляемый объект
      if(old_ptr) get_deleter()(old_ptr).

Так:

В случае, когда tempиспользуется, указатель на старый узел в headсначала будет перемещен tempчерез его конструктор перемещения, сбрасывая его headдля хранения nullptr. Затем head.operator=вызовет next.release()и получит этот указатель. А затем tempвыйдет за рамки deleteстарого узла.

В случае, когда tempне используется, head.operator=вызовет next.release(), сохранит свой старый указатель и заменит его освобожденным указателем, а затем deleteсохраненным указателем.

В любом случае утечки нет.

У меня создалось впечатление, что умные указатели надежно защищают от утечек памяти.

При правильном использовании - да.

Кажется, в этом случае может быть утечка памяти для оригинала, headпотому что на него больше не будет указывать умный указатель?

Утечки нет, так как всегда есть unique_ptrссылка на старый узел, пока он не pop()завершится и не tempбудет уничтожен вместе deleteсо старым узлом. Даже если tempон опущен, старый узел все равно будет уничтожен должным образом после передачи права собственности на его nextуказатель.