【问题标题】:Singly Linked List - Segmentation Error due to Destructor implementation单链表 - 由于析构函数实现导致的分段错误
【发布时间】:2017-02-28 17:00:40
【问题描述】:

我试图弄清楚为什么我的单链表实现会出现段错误。

我创建了一个名为 dq1 的 Deque 类型的对象,编译器为它调用析构函数,因为程序已经完成 - 析构函数调用 remove_front() 来处理头部的一些 move()。我相信这就是问题所在,但我似乎无法弄清楚它到底在哪里。

调试器信息 - 不知道该怎么做?

#0 0x4013ea std::unique_ptr<Node, std::default_delete<Node> >::get(this=0x8) (/usr/include/c++/6/bits/unique_ptr.h:305)
#1 0x401586 std::unique_ptr<Node, std::default_delete<Node> >::operator bool(this=0x8) (/usr/include/c++/6/bits/unique_ptr.h:319)
#2 0x40140b std::operator!=<Node, std::default_delete<Node> >(std::unique_ptr<Node, std::default_delete<Node> > const&, decltype(nullptr))(__x=<error reading variable: Cannot access memory at address 0x8>) (/usr/include/c++/6/bits/unique_ptr.h:670)
#3 0x401132 Deque::size(this=0x7fffffffe520) (Deque.cpp:75)
#4 0x4010f2 Deque::empty(this=0x7fffffffe520) (Deque.cpp:66)
#5 0x4016dd main() (/test.cpp:12)

双端队列.cpp

#include "Deque.h"
#include <iostream>
#include <memory>
#include <utility>
#include <stdexcept>

using std::cout;
using std::endl;
using std::move;

Deque::~Deque()
{
    while (!empty()) remove_front();
}


void Deque::insert_front(int a)
{
    std::unique_ptr<Node> new_node;
    new_node->val = move(a);
    new_node->next = move(head); // head is wiped.
    head = move(new_node); //head is init. with new_node val*/
}


int Deque::remove_front()
{
    if (empty()) {throw std::runtime_error(std::string("Empty"));};

    std::unique_ptr<Node> old;
    int return_value = head->val;
    old = move(head);
    head = move(old->next);
    delete &old;
    return return_value;
}


bool Deque::empty() const
{
return (size() == 0);
}
int Deque::size() const
{

int size_val = 0;
const Node* p = head.get();

while ( p != NULL)
    {
        size_val++;
        p = p->next.get();
    }
    return size_val;
}

test.cpp

#include <iostream>
#include "Deque.h"



using std::cout;
using std::endl;

int main()
{

     Deque dq1;
      return 0;
}

deque.h

#include "Node.h"
#include <memory>

class Deque{
    public:
        Deque() = default;
        Deque(const Deque&);
        ~Deque(); //must use constant space
        Deque& operator=(const Deque&){return *this;};
        void insert_front(int); 
        int remove_front();
        bool empty() const;

    private:
    friend Node;
        std::unique_ptr<Node> head ;
        std::unique_ptr<Node> tail ;

};

节点.h

#include "Node.h"

std::ostream& operator<<(std::ostream& out, const Node& n) {
  return out << &n << ": " << n.val << " -> " << n.next.get();
}

【问题讨论】:

  • 您应该永远std::unique_pointer 拥有的指针上调用 delete。在您的 Deque.cpp 中检查您的 int Deque::remove_front() 实现
  • 那么析构函数如何释放唯一指针使用的内存呢?
  • 谢谢@GMichael when the managing unique_ptr object is assigned another pointer via operator= or reset().
  • 不幸的是,删除delete *old 行后,我仍然遇到段错误。

标签: c++ pointers segmentation-fault nodes destructor


【解决方案1】:

你这里有 UB:

std::unique_ptr<Node> new_node;
new_node->val = move(a);

您创建一个默认初始化的新指针(指向nullptr)并取消引用它。如果你有 C++14 或更高版本,你应该用 std::make_unique 初始化它,或者只用 new 初始化它:

std::unique_ptr<Node> new_node = std::make_unique<Node>(); // C++14 or later
std::unique_ptr<Node> new_node( new Node ); // pre C++14

这一行也有问题:

delete &old;

这行没有任何意义。你得到指针本身的地址,它被创建为局部变量并尝试删除它。如果您尝试删除 old 指向的数据,那就错了 - std::unique_ptr 的全部意义在于自动执行此操作。

此会员:

std::unique_ptr<Node> tail ;

这是设计错误的,尽管您似乎没有在代码中使用它。这假设您将有多个 std::unique_ptr 指向同一个对象。但是这个指针是唯一的所有权。

Deque::size() 似乎也有问题,但如果没有看到源代码,就不可能说出那里出了什么问题。

在你的析构函数中你不需要做任何事情(尽管如果其他方法被正确实现也不会有害)-std::unqiue_ptr 将递归地销毁所有数据。

【讨论】:

  • 我忘记在帖子中包含 size() - 我现在已经包含了它。那就是我的调试器引导我的地方。 @Slava
  • 我相信这与我创建节点指针 P 的方式有关
  • @TigerCode size() 看起来不错,似乎在insert_front() 中使用nullptr 的问题会破坏它。
  • 对于尾部 - 它需要成为观察者,因为我们不能让 head = tail 成为两个唯一的 ptr 正确吗?
  • Tail 可以是原始指针,否则您会遇到问题,因为 Nodehead 或以前的 Node::next 拥有,并且您不能拥有多个拥有 std::unque_ptr 的所有者。您也可以使用std::shared_ptr,但在这种情况下,它会是矫枉过正。
猜你喜欢
  • 2013-04-30
  • 2012-03-09
  • 1970-01-01
  • 1970-01-01
  • 2018-06-28
  • 2018-12-09
  • 2018-01-02
  • 1970-01-01
  • 2014-02-17
相关资源
最近更新 更多