Spojrzałbym na interfejs std :: list, który jest implementacją C++ połączonych list. Wygląda na to, że podchodzisz do templatowania swojej klasy listy powiązanej źle. Najlepiej jest, jeśli twoja połączona lista nie powinna przejmować się semantyką własności (tj. Czy jest tworzona z surowymi pikselami, inteligentnymi wskaźnikami lub zmiennymi przydzielonymi do stosu). Poniżej przedstawiono przykład własnościowych semicryków z kontenerami STL. Istnieją jednak lepsze przykłady STL i prawa własności z bardziej autorytatywnych źródeł.
#include <iostream>
#include <list>
#include <memory>
using namespace std;
int main()
{
// Unique ownership.
unique_ptr<int> int_ptr = make_unique<int>(5);
{
// list of uniquely owned integers.
list<unique_ptr<int>> list_unique_integers;
// Transfer of ownership from my parent stack frame to the
// unique_ptr list.
list_unique_integers.push_back(move(int_ptr));
} // list is destroyed and the integers it owns.
// Accessing the integer here is not a good idea.
// cout << *int_ptr << endl;
// You can make a new one though.
int_ptr.reset(new int(6));
// Shared ownership.
// Create a pointer we intend to share.
shared_ptr<int> a_shared_int = make_shared<int>(5);
{
// A list that shares ownership of integers with anyone that has
// copied the shared pointer.
list<shared_ptr<int>> list_shared_integers;
list_shared_integers.push_back(a_shared_int);
// Editing and reading obviously works.
const shared_ptr<int> a_ref_to_int = list_shared_integers.back();
(*a_ref_to_int)++;
cout << *a_ref_to_int << endl;
} // list_shared_integers goes out of scope, but the integer is not as a
// "reference" to it still exists.
// a_shared_int is still accessible.
(*a_shared_int)++;
cout << (*a_shared_int) << endl;
} // now the integer is deallocated because the shared_ptr goes
// out of scope.
Dobrym ćwiczeniem zrozumieć własności, przydział pamięci/dealokacji i wspólnych wskaźników jest zrobić tutorial, w którym zaimplementować własną inteligentne kursory. Wtedy zrozumiesz dokładnie, jak używać inteligentnych wskaźników, a będziesz miał jedną z tych xen momentów, gdy zdasz sobie sprawę, jak prawie wszystko w C++ wraca do RAII (własność zasobów).
Wracam do sedna Twojego pytania. Jeśli chcesz trzymać się węzłów typu T, nie owijaj węzła w inteligentny wskaźnik. Węzeł destruktora musi usunąć bazowy surowy wskaźnik. Surowy wskaźnik może wskazywać na sam wskaźnik inteligentny określony jako T. Gdy wywoływany jest destruktor klasy "LinkedList", iteruje on przez wszystkie węzły z węzłem :: next i wywołuje delete node;
po uzyskaniu wskaźnika do następnego węzła.
Można utworzyć listę, w której węzły są inteligentnymi wskaźnikami ... ale jest to bardzo wyspecjalizowana lista połączona, prawdopodobnie o nazwie SharedLinkedList lub UniqueLinkedList z bardzo różnymi semantami do tworzenia obiektów, popping itp. Tak jak przykład, UniqueLinkedList przesuń węzeł w wartości zwracanej po wywołaniu wartości dla osoby wywołującej. Wykonanie metaprogramowania dla tego problemu wymagałoby zastosowania częściowej specjalizacji dla różnych typów T. Przykład: coś takiego:
template<class T>
struct LinkedList
{
Node<T> *head;
};
// The very start of a LinkedList with shared ownership. In all your access
// methods, etc... you will be returning copies of the appropriate pointer,
// therefore creating another reference to the underlying data.
template<class T>
struct LinkedList<std::shared_ptr<T>>
{
shared_ptr<Node<T>> head;
};
Teraz możesz rozpocząć wdrażanie własnego STL! Możesz już dostrzec potencjalne problemy, o których mowa w komentarzach do twojego pytania z tym podejściem. Jeśli węzły mają shared_ptr next, spowoduje to wywołanie destruktora tego węzła, który wywoła następny współdzielony destruktor węzła i tak dalej (możliwe jest przepełnienie stosu z powodu rekurencji). Dlatego nie dbam zbytnio o to podejście.
Nie wiem, dlaczego chcesz mieć wspólny ptr zamiast unikalnego ptr, ale tak. Zakładając, że używasz C++ 14, powinieneś użyć std :: make_unique/make_shared. Musisz jednak być bardzo ostrożnym przy dodawaniu/usuwaniu elementów, które nie powodują przypadkowego usunięcia ogona z listy. – xaxxon
Uważaj, używając inteligentnych wskaźników dla połączonych list, ponieważ destruktory będą nazywane rekurencyjnie, gdy usuniesz dowolny węzeł z listy zawierającej inny węzeł; może to spowodować przepełnienie stosu, jeśli usunięta część listy łączy wystarczającą liczbę elementów. –
@xaxxon Czy unikalny ptr jest lepszy w użyciu? Jeśli go zmienię, czy po prostu trzeba zmienić wszystkie części 'shared_ptr', aby stały się' unique_ptr'? – Mark