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

时间:2015-03-21 18:41:36

标签: c++ pointers unique-ptr

您好我正在尝试使用指针并学习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;)':尝试引用已删除的函数

3 个答案:

答案 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()返回原始指针,而不会破坏管理。