双向链表 std::unique_ptr 类在节点删除时无法按预期工作

Doubly linked list std::unique_ptr class doesn't work as expected on node removal

本文关键字:工作 unique std ptr 双向链表 节点删除      更新时间:2023-10-16

灵感来自Herb Sutter在CppCon2016中的演讲,可以在这个链接中找到
我决定实现一个双链接列表,如视频中所示,带有智能指针
下面的实现几乎是在remove((方法中的一行代码之外工作的
我调试了这段代码,删除后上一个节点不会更新为null(应该是head节点(
就好像智能指针之间的所有权转换是错误的。下面是头文件和测试main((的代码:

链接列表.h

#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include <iostream>
#include <memory>
#include <initializer_list>
namespace DLL {
template <typename T> class LinkedList{
private:
struct ListNode{
std::unique_ptr<ListNode> next; //2 uniq_ptr can't point to one another.
ListNode* prev = nullptr; //weakptr needs to be cast back to a shared_ptr to check its state.
T data{}; //Initialize empty;
ListNode(const T& element){
this->data = element;
}
};
public:
std::unique_ptr<ListNode> head;
ListNode* tail = nullptr;
LinkedList(){}
~LinkedList(){}
void append(const T& element){
ListNode* curr = nullptr;
if (head.get() == nullptr){ //If list is empty.
head = std::make_unique<ListNode>(element);
}
else if(head.get() -> next.get() == nullptr){ //If list has one element
head.get() -> next = std::make_unique<ListNode>(element);
curr = head.get() -> next.get(); //Sets raw pointer to the first element.
curr -> prev = head.get();
tail = curr;
}
else{
tail -> next = std::make_unique<ListNode>(element);
curr = tail -> next.get(); //Sets raw pointer to the last element.
curr -> prev = tail;
tail = curr;// The new last element is the tail.
}
}
int remove(const T& element){
ListNode* curr = nullptr;
if (head.get() == nullptr){ //If list is empty.
return -1; //Error: Can't remove from empty list.
}
//List has one or more elements.
curr = head.get();
while(curr != nullptr){
if(curr -> data == element){ //Found element
if(curr -> prev == nullptr){ //is head
//head.reset(head.get()->next.get()); Doesn't work
//Line below doesn't work too
head = std::move(curr->next); //Head now points to the next element
//New head's previous element doesn't point to nothing, as it should.
}
else if(curr -> next.get() == nullptr){ //is tail
tail = curr -> prev; //Reference the previous element
tail -> next.release(); //Release the old tail element
if(head.get() == tail){
tail = nullptr; //tail and head should not be the same.
} //List contains one element
}
else{//is intermediate
//The next node should point to the previous one
curr -> next -> prev = curr -> prev;
curr -> prev -> next = std::move(curr -> next);
//The prev node now points to the next one of current.
}
return 1; //Element found in list
}
curr = curr -> next.get(); //Traverse the next element
}
return 0; //Element not found in list
}
void print() {
ListNode* curr = head.get(); //Start from the start of the list.
std::cout << "[ ";
while (curr != nullptr) {
std::cout << curr -> data << " ";
curr = curr -> next.get();
}
std::cout << "]" << std::endl;
}
};
}
#endif

main.cpp

int main() { //Temporary Test Main will be split from the implementation file in the future
DLL::LinkedList <int> list; //Empty list
list.append(1);
list.append(4);
list.append(5);
list.append(6);
list.print();
list.remove(5);
list.remove(1); //When 1 is removed the 4 doesn't properly update as head, meaning the previous pointer of 4 is not null
list.remove(4);
list.remove(6);
list.print();
retunn 0;
}

我很抱歉遇到这种问题,我搜索了很多,但找不到类似的东西。我已经调试了好几天,但无法修复所有权线。我试着包含最少量的代码,以重现错误。如果标题是长代码段,我很抱歉。

我使用g++:g++ -std=c++14 main.cpp -o out和VS2015编译器进行编译。make_unique调用需要C++14标志

在检查迭代器上一个指针是否为空的部分(基本上是检查头(,您有以下行:

head = std::move(curr->next);

其将报头指针移动为元素的下一个指针。但是,您无法将新头指针的上一个指针更新为null。所以代码应该读起来像:

if(curr -> data == element){ //Found element
if(curr -> prev == nullptr){ //is head
head = std::move(curr->next); //Head now points to the next element
if (head)
head->prev = nullptr;
else
tail = nullptr;
}
}

由于在成为新头指针的项上使用std::move(这是正确的(,因此基本上保持该节点中包含的数据不变。在这种情况下,您需要明确——std::unique_ptr包装器对其拥有的对象的底层实现一无所知,因此在这种情况中,它不可能知道更新prev指针。