您好我正在尝试使用指针并学习C++
中唯一指针的基础知识。下面是我的代码我已经评论了main函数中的代码行。调试问题然而,我无法这样做。我错过了什么? move()
中的insertNode()
是否不正确?我得到的错误低于代码:
#include<memory>
#include<iostream>
struct node{
int data;
std::unique_ptr<node> next;
};
void print(std::unique_ptr<node>head){
while (head)
std::cout << head->data<<std::endl;
}
std::unique_ptr<node> insertNode(std::unique_ptr<node>head, int value){
node newNode;
newNode.data = value;
//head is empty
if (!head){
return std::make_unique<node>(newNode);
}
else{
//head points to an existing list
newNode.next = move(head->next);
return std::make_unique<node>(newNode);
}
}
auto main() -> int
{
//std::unique_ptr<node>head;
//for (int i = 1; i < 10; i++){
// //head = insertNode(head, i);
//}
}
错误 std :: unique_ptr&gt; :: unique_ptr(const std :: unique_ptr&lt; _Ty,std :: default_delete&lt; _Ty&gt;&gt;&amp;)':尝试引用已删除的函数
答案 0 :(得分:3)
除了其他小问题外,主要问题是这一行:
return std::make_unique<node>(newNode);
您正在尝试构建一个指向新节点的唯一指针,将newNode
传递给node
的复制构造函数。但是,node
的复制构造函数已被删除,因为node
包含不可复制的类型(即std::unique_ptr<node>
)。
您应该传递一个std::move(newNode)
,但这是有问题的,因为您在堆栈上创建节点,它将在函数退出时被销毁。
在我看来,使用std::unique_ptr
是一个坏主意,因为,例如,要打印列表(或插入列表),您需要std::move
head
(所以你失去了它)等等。我认为你使用std::shared_ptr
会好得多。
答案 1 :(得分:0)
我遇到了同样的问题,确实使用了shared_ptr
是可行的。
在函数中使用智能指针作为参数会复制该指针(而不是其指向的数据),这会导致unique_ptr
重设并删除其先前指向的数据-因此我们得到“试图引用已删除的功能”错误。如果您使用shared_ptr
,则一旦超出该函数的范围,这只会简单地增加引用计数,并减少该引用计数。
以上答案中的注释表明,使用shared_ptr是毫无根据的。这些答案是在C ++ 17标准之前编写的,据我了解,我们应该使用该语言的最新版本,因此shared_ptr
在这里很合适。
答案 2 :(得分:0)
我不知道为什么无论如何我们都必须向用户公开节点类型。正如我的一位老师说的那样,C ++的整个事物都是 编写更多代码以便以后编写更少的代码 。
我们想封装所有内容,并且不给用户留下节点的头或尾(双关语意味)。非常简单的界面如下所示:
struct list
{
private:
struct node {
int data;
std::unique_ptr<node> next;
node(int data) : data{data}, next{nullptr} {}
};
std::unique_ptr<node> head;
public:
list() : head{nullptr} {};
void push(int data);
int pop();
~list(); // do we need this?
};
该实现做了Ben Voigt提到的事情:
void list::push(int data)
{
auto temp{std::make_unique<node>(data)};
if(head)
{
temp->next = std::move(head);
head = std::move(temp);
} else
{
head = std::move(temp);
}
}
int list::pop()
{
if(head == nullptr) {
return 0; /* Return some default. */
/* Or do unthinkable things to user. Throw things at him or throw exception. */
}
auto temp = std::move(head);
head = std::move(temp->next);
return temp->data;
}
我们实际上需要一个析构函数,如果list真的很大,它将不会是递归的。我们的堆栈可能会爆炸,因为节点的析构函数将调用unique_ptr的析构函数,然后将调用受管节点的析构函数,这将调用unique_ptr的析构函数... ad nauseatum。
void list::clear() { while(head) head = std::move(head->next); }
list::~list() { clear(); }
在该默认析构函数对unique_ptr
一次ping head
析构函数一次之后,没有递归迭代。
如果我们要遍历列表而不弹出节点,则可以在旨在解决该任务的某种方法中使用get()
。
Node *head = list.head.get();
/* ... */
head = head->next.get();
get()
返回原始指针,而不会破坏管理。