Односвязный список — ошибка сегментации из-за реализации деструктора

#c #pointers #segmentation-fault #nodes #destructor

#c #указатели #ошибка сегментации #узлы #деструктор

Вопрос:

Я пытаюсь выяснить, почему я получаю ошибку seg из моей реализации односвязного списка.

Я создаю объект типа Deque с именем dq1, компилятор вызывает для него деструктор после завершения программы — деструктор вызывает remove_front(), который обрабатывает некоторые move() для head. Я считаю, что в этом и заключается проблема, но я не могу понять, где именно она находится.

Информация об отладчике — Не знаю, что с этим делать?

 #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> > constamp;, 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)
  

Deque.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 amp;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 Dequeamp;);
        ~Deque(); //must use constant space
        Dequeamp; operator=(const Dequeamp;){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 ;

};
  

Node.h

 #include "Node.h"

std::ostreamamp; operator<<(std::ostreamamp; out, const Nodeamp; n) {
  return out << amp;n << ": " << n.val << " -> " << n.next.get();
}
  

Комментарии:

1. Вы никогда не должны вызывать delete указатель, принадлежащий std::unique_pointer . Проверьте свою int Deque::remove_front() реализацию в вашем Deque.cpp

2. Тогда как деструктор освобождает память, используемую уникальным указателем?

3. @TigerCode en.cppreference.com/w/cpp/memory/unique_ptr

4. Спасибо @GMichael when the managing unique_ptr object is assigned another pointer via operator= or reset().

5. К сожалению, я все еще сталкиваюсь с ошибкой seg после удаления delete *old строки.

Ответ №1:

У вас есть UB прямо здесь:

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

вы создаете новый указатель, который инициализирован по умолчанию (указывает на nullptr ), и разыменовываете его. Вы должны инициализировать его с помощью std::make_unique , если у вас C 14 или более поздней версии, или просто инициализировать его с помощью 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 amp;old;
  

эта строка не имеет никакого смысла. Вы получаете адрес самого указателя, который создается как локальная переменная, и пытаетесь удалить его. Если вы пытались удалить данные, на которые old указывает, что это неверно — весь смысл std::unique_ptr в том, чтобы сделать это автоматически.

Этот участник:

 std::unique_ptr<Node> tail ;
  

это неправильно по замыслу, хотя вы, похоже, не используете его в своем коде. Это предполагает, что у вас будет несколько std::unique_ptr , указывающих на один и тот же объект. Но этот указатель предназначен для уникального владения.

Похоже, у вас также есть проблема в Deque::size() , но, не видя источника, невозможно сказать, что там не так.

В вашем деструкторе вам не нужно ничего делать (хотя это не повредит, если другие методы будут реализованы должным образом) — std::unqiue_ptr уничтожит все данные рекурсивно.

Комментарии:

1. Я забыл включить size() в сообщение — я включил его сейчас. Вот куда привел меня мой отладчик. @Slava

2. Я считаю, что это связано с тем, как я создал указатель узла P

3. @TigerCode size() выглядит нормально, кажется, что проблема с использованием nullptr in insert_front() нарушает его.

4. Для хвоста — это должен быть наблюдатель, тогда, поскольку мы не можем заставить head = tail быть двумя уникальными ptr, правильно?

5. Хвост может быть необработанным указателем, в противном случае у вас возникнет проблема, так как Node он принадлежит одному head или предыдущему Node::next , и у вас не может быть более одного владельца с std::unque_ptr . Также вы можете использовать std::shared_ptr , но в этом случае это было бы излишним.