Операции над связанным списком дают ошибку после завершения - PullRequest
0 голосов
/ 17 апреля 2020

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

Узел hpp:

class Node{
    public:
        int data;
        Node * next;
        Node(int val);
};

Узел cpp:

#include <stdio.h>
#include "node.hpp"

Node::Node(int val)
    : next(NULL)
{
    data = val;
}

Связанный список Класс hpp :

#include "node.cpp"

class LL {
    private:
        Node *head;
        Node *tail;
    public:

        LL();
        ~LL();


        int LL_append(int value);

        void LL_print();

        int LL_search(int target);

        int LL_catenate(LL * list);

        int LL_insert(int x);


};

Связанный список Класс cpp:

#include "LL.hpp"
#include <stdio.h>
#include <iostream>

using std::cout;

LL::LL()
    :
      head(NULL),
      tail(NULL)
{

}
LL::~LL(){
    Node * curr = head;
    while(head != NULL){
        if(head == tail){
            delete head;
            return;
        }
        while(curr->next != tail){
            curr = curr->next;
        }
        delete tail;
        tail = curr;
        curr = head;
    }
}

//returns 1 for success and 0 for fail
int LL::LL_append(int value){
    int ret = 0;
    Node * newNode = new Node(value);
    if(value != NULL){
        if(head == NULL){
            head = newNode;
            tail = newNode;

        }   
        else{
            tail->next = newNode;
            tail = newNode;
        }
        ret = 1;
    }
    return ret;
}

//prints out list
void LL::LL_print(){
    Node * curr = head;
    cout << "[ ";
    while(curr != NULL){
        cout << curr->data << " ";
        curr = curr->next;
    }
    cout << "]\n";
}

//returns the number of times it appears in the list. return -1 if failed
int LL::LL_search(int target){
    int count = 0;
    Node * curr = head;
    while(curr != NULL){
        if(curr->data == target){
            count++;
        }
        curr = curr->next;
    }
    return count;
}

//returns 1 on success
int LL::LL_catenate(LL * list){
    if(list->head == NULL){

    }
    else if(head == NULL){
        head = list->head;
        tail = list->tail;
    }
    else{
        tail->next = list->head;
        tail = list->tail;
    }
    return 1;
}

int LL::LL_insert(int x){
    int ret = 0;
    Node * curr = head;
    Node * newNode = new Node(x);
    if(head == NULL){
        head = newNode;
        tail = newNode;
        ret = 1;
    }
    else if(head->data >= x){
        printf("here\n");
        newNode->next = head;
        head = newNode;
    }
    else{
        Node * curr = head;
        while( curr->next != NULL && curr->next->data < x){
            curr = curr->next;
        }
        newNode->next = curr->next;
        curr->next = newNode;
        if(curr->next == NULL){
            tail = newNode;
        }
        ret = 1;


    }

    return ret;
}

Это то, что я использую для проверки методов:

int main(){
    LL L1 = LL();
    L1.LL_append(1);
    L1.LL_append(3);
    L1.LL_print();
    printf("%d\n", L1.LL_search(12));
    printf("%d\n", L1.LL_search(1));

    LL L2 = LL();
    L2.LL_append(5);
    L2.LL_append(9);
    L1.LL_catenate(&L2);
    L1.LL_print();

    L1.LL_insert(7);
    L1.LL_print();
    L1.~LL();
    L2.~LL();
    printf("done\n");
}

И это вывод программы:

[ 1 3 ]
0
1
[ 1 3 5 9 ]
[ 1 3 5 7 9 ]
done
double free or corruption (fasttop)
Aborted (core dumped)

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

Ответы [ 3 ]

0 голосов
/ 17 апреля 2020
  1. Вы не должны вызывать деструктор, если вы не сконструировали объект явно (то есть, используя placement new).
  2. Если вы определяете деструктор, вы должны также определить конструктор копирования и оператор присваивания копии. Это называется Правило 3

Бонус -

  1. Ваша программа крайне неэффективна. Вы перебираете LinkedList от начала до конца для удаления одного объекта. Рассмотрим это улучшение:
LL::~LL(){
    Node* curr = head;
    while(head != NULL){
        head = curr->next;
        delete curr;
        curr = head;
    }
}
Вы проверяете, что value != NULL во вставке. Это просто проверяет, что value != 0, но это вводит в заблуждение, и я не вижу никакой причины для этой проверки. Если вы это имели в виду, напишите value != 0. В противном случае - удалить.
0 голосов
/ 17 апреля 2020

Явный вызов ваших деструкторов означает, что они запускаются дважды, один раз, когда вы вызываете их, и один раз, когда ваши списки уничтожаются. Просто удалите вызовы к деструкторам, в которых они не нужны.

Ваш деструктор сам по себе кажется излишне сложным и может быть просто:

LL::~LL(){
    while(head){
        Node* next = head->next;
        delete head;
        head = next;
    }
    tail = nullptr;
}

Это не вызывает у вас проблемы, но вероятно скоро, так как у вас есть деструктор, вам нужно следовать правилу из трех и удалить / определить конструктор копирования и оператор присваивания.

0 голосов
/ 17 апреля 2020

Не вызывайте деструктор вручную. Деструктор будет вызываться автоматически, когда связанный список выходит из области видимости. Если вы просто удалите явные вызовы деструктора, он будет работать правильно .

Обратите внимание, что вы получаете предупреждения об использовании NULL. Исправьте это, используя nullptr.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...