我会看一下 std::list 的接口,它是链表的 C++ 实现。看来您正在接近链接列表类的模板错误。理想情况下,您的链表不应该关心所有权语义(即它是否使用原始 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 类型的节点,请不要将节点包装在智能指针中。 Node 析构函数必须删除底层的原始指针。原始指针可能指向本身指定为 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!使用这种方法,您已经可以看到 cmets 中提到的问题的可能性。如果节点有 shared_ptr next ,它将导致调用该共享节点的析构函数,该析构函数将调用下一个共享节点析构函数,依此类推(由于递归可能导致堆栈溢出)。所以这就是我不太关心这种方法的原因。