试图引用已删除函数的唯一指针

Unique Pointer attempting to reference a deleted function

本文关键字:唯一 指针 函数 删除 引用      更新时间:2023-10-16

你好,我正在尝试使用指针,并学习C++中唯一指针的基础知识。下面是我的代码,我在主函数中评论了这行代码。调试问题但是,我无法这样做。我缺少什么?insertNode()中的move()是否不正确?我得到的错误在代码下面:

#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>:unique_pr(const std::unique_ptr<_Ty,std::default_delete<_Ty>&)':试图引用已删除的函数

除了其他小问题外,主要问题是这一行:

return std::make_unique<node>(newNode);

您正试图构造一个指向新节点的唯一指针,将newNode传递给node的复制构造函数。但是,node的复制构造函数被删除,因为node包含不可复制的类型(即std::unique_ptr<node>)。

您应该传递一个std::move(newNode),但这是有问题的,因为您在堆栈上创建了节点,它将在函数的出口处被销毁。

在我看来,使用std::unique_ptr是个坏主意,因为例如,要打印列表(或插入列表),你需要std::movehead(所以你会丢失它)等等。我认为使用std::shared_ptr会更好。

我也遇到了同样的问题,确实使用了shared_ptr

在函数中使用智能指针作为参数会复制指针(而不是它指向的数据),这会导致unique_ptr重置并删除它以前指向的数据——因此我们会出现"试图引用已删除的函数"错误。如果您使用shared_ptr,这将简单地增加引用计数,并在超出该函数的范围后取消增量。

上面答案中的评论表明,使用shared_ptr是毫无根据的。这些答案是在C++17标准之前编写的,据我所知,我们应该使用该语言的最新版本,因此shared_ptr在这里是合适的。

我不知道为什么我们必须在任何情况下向用户公开节点类型。正如我的一位导师所说,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;
}  

实际上,我们需要一个析构函数,若列表真的很大,那个么它就不会是递归的。我们的堆栈可能会爆炸,因为节点的析构函数会调用unique_ptr的析构因子,然后会调用托管节点的析构函数,后者会调用unique _ptr的destructor。。。令人作呕。

void list::clear() { while(head) head = std::move(head->next); }
list::~list() { clear(); }

在此之后,默认的析构函数将只为head ping unique_ptr析构函数一次,而不进行递归迭代。

如果我们想在不弹出节点的情况下遍历列表,我们会在一些设计用于处理该任务的方法中使用get()

Node *head = list.head.get();
/* ... */
head = head->next.get();

get()在不中断管理的情况下返回原始指针。

这个例子怎么样,除了示例代码,他还提到了一些原则:

当你需要";分配"——使用std::move,当您只需要遍历时,使用get()