Metodo pop dell'elenco collegato utilizzando unique_ptr

Aug 20 2020

Sto esaminando l'implementazione di un elenco collegato singolarmente utilizzando unique_ptronhttps://solarianprogrammer.com/2019/02/22/cpp-17-implementing-singly-linked-list-smart-pointers/. La mia domanda riguarda il seguente metodo:

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

Mi chiedo perché il temporaneo è necessario qui? Perché non potresti semplicemente farlo head = std::move(head->next)? È perché si tradurrà in una perdita di memoria? Quando headviene riassegnato, unique_ptrlibera automaticamente la memoria corrente a cui punta?

Avevo l'impressione che i puntatori intelligenti fossero a prova di errore dalle perdite di memoria. Sembra che in questo caso potrebbe esserci una perdita di memoria per l'originale headperché non ci sarebbe più un puntatore intelligente che punta ad esso?

Risposte

4 RemyLebeau Aug 20 2020 at 07:36

Mi chiedo perché il temporaneo è necessario qui?

Non è realmente necessario , ma non è neanche male da usare.

Perché non potresti semplicemente farlo head = std::move(head->next)? È perché si tradurrà in una perdita di memoria?

Puoi. Non ci saranno perdite in questo esempio.

Quando headviene riassegnato, unique_ptrlibera automaticamente la memoria corrente a cui punta?

Sì. Tuttavia, il vecchio puntatore non verrà deletetrasferito fino a quando la proprietà del nuovo puntatore non sarà stata trasferita. Per cppreference:

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

Trasferisce la proprietà da ra *thiscome se chiamasse reset(r.release())seguito da un'assegnazione di get_deleter()da std::forward<E>(r.get_deleter()).

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

Sostituisce l'oggetto gestito.

  1. Dato current_ptr, il puntatore gestito da *this, esegue le seguenti azioni, in questo ordine:

    1. Salva una copia del puntatore correnteold_ptr = current_ptr
    2. Sovrascrive il puntatore corrente con l'argomentocurrent_ptr = ptr
    3. Se il vecchio puntatore non era vuoto, elimina l'oggetto gestito in precedenza
      if(old_ptr) get_deleter()(old_ptr).

Così:

Nel caso in cui tempviene utilizzato, il puntatore al vecchio nodo in headverrà prima spostato temptramite il suo costruttore di movimento, reimpostando headper contenere un nullptr. Quindi head.operator=chiamerà next.release()e acquisirà quel puntatore. E poi tempuscirà dall'ambito, delete'ing il vecchio nodo.

Nel caso in cui tempnon viene utilizzato, head.operator=chiamerà next.release(), salverà il suo vecchio puntatore e lo sostituirà con il puntatore rilasciato, quindi con deleteil puntatore salvato.

Nessuna perdita in entrambi i casi.

Avevo l'impressione che i puntatori intelligenti fossero a prova di errore dalle perdite di memoria.

Se usato correttamente , sì.

Sembra che in questo caso potrebbe esserci una perdita di memoria per l'originale headperché non ci sarebbe più un puntatore intelligente che punta ad esso?

Non c'è perdita, poiché c'è sempre un unique_ptrriferimento al vecchio nodo, finché non pop()esce e tempviene distrutto, deleteportando con sé il vecchio nodo. Anche se tempviene omesso, il vecchio nodo viene comunque distrutto correttamente dopo che la proprietà del suo nextpuntatore è stata trasferita.