Я бы посмотрел интерфейс std :: list, который является реализацией связанных списков C++. Кажется, что вы неправильно подходите к шаблону вашего списка Linked list. В идеале ваш связанный список не должен заботиться о семантике владения (то есть, будет ли он создан с помощью raw ptrs, интеллектуальных указателей или переменных, выделенных стеком). Ниже приведен пример семантики собственности с контейнерами STL. Тем не менее, есть лучшие примеры STL и права собственности из более авторитетных источников.
#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.
Хорошее упражнение для понимания собственности, распределение памяти/открепления и общих указателей, чтобы сделать учебник, где реализовать свои собственные смарт-указатели. Затем вы точно поймете, как использовать интеллектуальные указатели, и у вас будет один из тех моментов xen, где вы поймете, как почти все на C++ возвращается в RAII (владение ресурсами).
Итак, вернемся к сути вашего вопроса. Если вы хотите придерживаться Узлов типа T, не переносите узел в интеллектуальный указатель. Деструктор узла должен удалить основной необработанный указатель. Необработанный указатель может указывать на сам смарт-указатель, указанный как T. Когда ваш деструктор класса «LinkedList» называется, он выполняет итерацию через все узлы с узлом Node :: next и вызывает delete node;
после того, как он получил указатель на следующий узел.
Вы можете создать список, где узлы являются интеллектуальными указателями ... но это очень специализированный связанный список, который, вероятно, называется SharedLinkedList или UniqueLinkedList с очень разными семантическими свойствами для создания объекта, выскакивания и т. Д. Как пример, UniqueLinkedList переместите узел в возвращаемое значение при появлении значения вызывающему. Для выполнения метапрограммирования для этой задачи потребуется использование частичной специализации для разных типов передаваемых T. Пример: что-то вроде:
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;
};
Теперь вы начинаете внедрять свои собственные STL! Вы уже можете увидеть потенциальные проблемы, упомянутые в комментариях к вашему вопросу при таком подходе. Если узлы имеют shared_ptr next, это приведет к вызову деструктора этого общего узла, который вызовет следующий общий деструктор узла и т. Д. (Возможен переполнение стека из-за рекурсии). Поэтому я не очень люблю этот подход.
Не знаете, почему вы хотите использовать общий ptr вместо уникального ptr, но да. Кроме того, если вы используете C++ 14, вы должны использовать std :: make_unique/make_shared. Вы должны быть очень осторожны при добавлении/удалении элементов, которые вы случайно не удалите из списка. – xaxxon
Будьте осторожны с помощью интеллектуальных указателей для связанных списков, поскольку деструкторы в конечном итоге будут вызываться рекурсивно при удалении любого узла в списке, который содержит другой узел; это может привести к переполнению стека, если удаленная часть списка связывает достаточное количество элементов. –
@xaxxon Является ли уникальный ptr лучше использовать здесь? Если я его изменю, мне просто нужно будет изменить все части 'shared_ptr', чтобы стать' unique_ptr'? – Mark