【问题标题】:Segmentation fault (core dumped) when I delete pointer当我删除指针时出现分段错误(核心转储)
【发布时间】:2016-01-11 17:03:13
【问题描述】:

我正在尝试从链接列表中删除重复项,但遇到了一个问题,这可能很明显且很简单,但我已经很多年没有使用过C++ 并且我无法找出我做错了什么阅读关于 SO 的类似问题。

以下是我的部分代码。我删除了不相关的部分(例如构造函数、其他方法等)。

template<class T>
class Node {
  Node() : data(NULL), next(NULL), prev(NULL) {}
  explicit Node(T d) : data(d), next(NULL), prev(NULL) {}
  explicit Node(T d, Node<T> *nxt, Node<T> *prv) : data(d), next(nxt),    prev(prv) {}
  ~Node() { delete next; delete prev; }

  T data;
  Node *next;
  Node *prev;
};

template<class T>
class LinkedList {
  LinkedList() : head(NULL) {}
  explicit LinkedList(Node<T> *head_node) : head(head_node) {}
  LinkedList& operator=(const LinkedList &copy_list);
  ~LinkedList(); //didn't implement this but I guess delete head is all?

  Node<T>* head;
};


template<class T>
LinkedList<T> RemoveDuplicates(const LinkedList<T> &linked_list) {
  //my = overload creates a whole new list with the same data as the original one
  LinkedList<T> result = linked_list; 

  Node<T> *cur = result.head;
  Node<T> *prev = NULL;

  while (cur) {
    if (...) { //duplicate found  
      Node<T> *temp = cur;
      prev->next = cur->next;
      cur = cur->next;
      cur->prev = prev;
      free(temp); //Here is my problem!!!
    }
    else {
      prev = cur;
      cur = cur->next;
    }
  }
  return result;
}

首先,我做了delete temp,然后我得到了Segmentation fault。然后我意识到你只有deletenew。很公平,但是当我在main 中构建整个列表时,我是newing 每个Node

Node<char> *h = new Node<char>('H'); //I have a constructor to handle this
Node<char> *e = new Node<char>('E');
Node<char> *l1 = new Node<char>('L');
Node<char> *l2 = new Node<char>('L');
Node<char> *o = new Node<char>('O');

h->next = e;
h->prev = NULL;

e->next = l1;
e->prev = h;

//and so on

那么为什么我不允许 delete 一些在其他地方被 newed 的东西?是不是因为newed 超出了当前范围?

其次,freeing 空间工作正常,但显然不是正确的做法,因为我没有 malloc 而是 newed!

我做错了什么?如何正确杀死已删除的节点?

Edit1:根据对我帖子的回复使其更具描述性 Edit2:添加了 3 种方法的规则

【问题讨论】:

  • ~Node() { delete next; delete prev; } 将杀死它周围的节点。例如,您从节点 X 和 Z 之间删除节点 Y,链接 X 和 Z。然后您 delete Y 以取回它的资源。然后 Y 的析构函数将销毁 X 和 Z。X 和 Z 的析构函数将销毁它们接触的所有内容,包括正在被销毁的 X 和 Z。这就是我们过去所说的非常糟糕的场景。如果我是你,我会重新考虑析构函数的逻辑。

标签: c++ pointers linked-list segmentation-fault duplicate-removal


【解决方案1】:

如果你用new分配内存,你就不能使用free()

您可以将malloc()free()newdelete 一起使用。您也不应该将malloc()free() 与对象一起使用,因为它们不像newdelete 那样调用对象的构造函数和析构函数

所以既然你用new分配了节点,我们可以改变

free(temp); //Here is my problem!!!

delete temp;

【讨论】:

  • 我收到Segmentation fault' when I do delete temp`
  • 那么你在代码的某个地方有问题。请提供minimal reproducible example
  • @Yalda Rule of Three 很可能有问题。
  • 谢谢@πάντα ῥεῖ!我认为我的代码中可能存在明显错误,但似乎并非如此。
  • 是的。删除我的评论,因为你已经涵盖了整个事情。
【解决方案2】:

这是正确识别和解决 OP 的直接问题的其他答案的附录。需要快速了解一下接下来会发生什么。

  Node<T> *temp = cur;
  prev->next = cur->next;
  cur = cur->next;
  cur->prev = prev;

此时 temp 还没有被清除,所以 next 和 prev 仍然指向曾经包围 cur 并且现在链接在一起的两个节点。如果不是,这将导致严重的问题:

  free(temp); //Here is my problem!!!

free 失败,因为 temp 指向的值是由 new 分配的。 NathanOliver 的回答已经涵盖了这一点,但隐藏在下面的是

delete temp;

这将调用析构函数像一个好的小对象一样进行清理。不幸的是,Node 析构函数看起来像这样:

~Node() { delete next; delete prev; }

temp-&gt;next 仍然指向一个活动节点。 temp-&gt;prev 也是如此。

下一个和上一个得到deleted。它调用它们的析构函数,deleteing 他们接触的两个节点,并调用将销毁列表中所有节点的死亡风暴。

如果双重删除没有首先杀死程序。每个链接的节点都会尝试delete刚刚deleted他们的节点。不好。

很有可能Node 析构函数应该只照顾自己并将其他Nodes 的销毁留给LinkedList 类。

【讨论】:

    【解决方案3】:

    那么为什么我不允许删除某处新的东西 别的?是不是因为它是在当前范围之外新增的?

    您可以在其他地方delete 事情newed。只要您指向有效地址,这不是问题。当您尝试使用指向 now-deleteed 地址的指针之一时,会出现 seg-fault。

    其次,释放空间也可以,但我觉得这就像作弊!

    您应该只在使用malloc 时使用free,在使用new 时使用delete。你应该永远把它们混在一起。另外,在delete 之后,养成将NULL 分配给指针变量的习惯。

    因为我可能需要在我的节点的析构函数上做一些事情。

    封装数据的类也应该在内部进行自己的内存管理。例如,LinkedList 应该管理节点的内存。

    这意味着,如果您有类似LinkedList&lt;T&gt;::add(Node&lt;T&gt; *data) 的内容,则应该考虑类似LinkedList&lt;T&gt;::add(T data) 的内容;然后列表类自己负责处理节点。

    否则,您将冒着将newed 节点发送到列表中的风险,然后列表deletes 在内部某个时间点将其发送到内部,但在其他地方,列表的客户端仍然持有对现在无效的引用节点。当客户端尝试使用它时,再次 bam!: seg-fault。

    我做错了什么?如何正确杀死已删除的节点?

    除了将news 与frees 混合之外,段错误很可能是由无效的内存访问触发的,因此您应该找到不再指向有效(即undeleted)内存的指针。

    【讨论】:

      【解决方案4】:

      在这种情况下,拥有构造函数和析构函数的代码非常重要。

      默认情况下,您应该在构造函数中使用“new”,在析构函数中使用“delete”。更具体地说,在构造函数中分配所需的所有资源并在析构函数中释放非常重要,这样可以确保没有任何内存泄漏。

      free(temp);//You don't need this, also you don't need delete.
      

      请发布您的构造函数和析构函数代码。

      L.E:

      我认为你应该这样做:

      template<class T>
      class LinkedList
      {
        private:
          struct Node {
              T m_data;
              Node *m_next;
              Node *m_prev;
          };
          Node* m_first;
          Node* m_last;
          int m_count;
        public:
          LinkedList();
          ~LinkedList();
          T GetFirst();
          T GetLast();
          void AddNode(T node);
          void RemoveAt(int index);
          T GetAt(int index);
          int Count();
      };
      
      //constructor
      template <typename T>
      LinkedList<T>::LinkedList(){
          m_count = -1;//-1 == "The list is empty!
      }
      
      template <typename T>
      void LinkedList<T>::AddNode(T data){
          Node* temp = new Node; //new is called only here -> delete is called in destructor or in RemoveAt
          temp->m_data = data;
          if (m_count > -1){//If the list contains one or more items
              temp->m_next = m_first;
              temp->m_prev = m_last;
              m_last->m_next = temp;
              m_last = temp;
              m_count++;
          }
          else if (m_count == -1)
          {//If no items are present in the list
              m_first = temp;
              m_first->m_next = temp;
              m_first->m_prev = temp;
              m_last = temp;
              m_last->m_next = temp;
              m_last->m_prev = temp;
              m_count = 0;
          }
      }
      
      template <typename T>
      void LinkedList<T>::RemoveAt(int index){
          if (index <= m_count)//verify this request is valid
          {
              Node* temp = m_last;
              for (int i = 0; i <= index; ++i){
                  temp = temp->m_next;
              }
              temp->m_prev->m_next = temp->m_next;
              temp->m_next->m_prev = temp->m_prev;
              delete temp;
              m_count--;
          }
      }
      
      template <typename T>
      T LinkedList<T>::GetAt(int index)
      {
          Node* temp = m_first;
          if (index <= m_count && index > -1){
              int i = 0;
              while(i < index){
                  temp = temp->m_next;
                  i++;
              }
          }
          return temp->m_data;
      }
      
      template <typename T>
      T LinkedList<T>::GetFirst() {
          return m_first->data;
      }
      
      template <typename T>
      T LinkedList<T>::GetLast(){
          return m_last->data;
      }
      
      template <typename T>
      int LinkedList<T>::Count(){
          return m_count;
      }
      
      template <typename T>
      LinkedList<T>::~LinkedList(){
         while(m_count > -1){
              Node* temp = m_first;
              m_first = temp->m_next;
              delete temp;//delete in destructor
              m_count--;
          }
      }
      

      【讨论】:

      • 谢谢,刚刚添加了这些!但是内存中有一些空间不再需要(或指向)。我怎样才能不需要删除它?
      • 我看到你的问题,我正在为你准备一些示例代码。
      • 我添加了一些关于我认为应该如何完成的代码。
      • 感谢@Heto,你的代码有很多要学习的地方,我发现我的代码有很多错误:)
      【解决方案5】:

      有很多答案说new/deletemalloc()/free()应该总是配对,但我觉得应该说为什么准确。

      在你的初始化中,

      Node<char> *h = new Node<char>('H');
      

      new 关键字返回一个完全类型化的对象指针,在语句本身中定义,而malloc() 只是分配给定的内存空间而不分配类型,因此返回一个void*。此外,new/delete 处理来自 Free Store 的内存,而malloc()/free() 在堆上分配/释放内存,尽管在某些情况下,一些编译器可能会根据malloc()/free 实现new/free

      同样重要的是newdelete分别调用构造函数和析构函数,而malloc()free()只是简单地分配和释放堆内存,而不考虑内存本身的状态。

      【讨论】:

        猜你喜欢
        • 2021-05-04
        • 2015-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多