Работа с двусвязным списком на C++, классы

217
10 августа 2021, 05:40

Нужно создать двусвязный список и проделать с ним некоторые действия - a) Добавить новый элемент в начало, b) Убрать первый элемент, c) Добавить новый элемент в конец, d) Убрать последний элемент. Синтаксических ошибок не выявлено, компилируется но в ходе работы крашется. Подозреваю что накосячил с передачей данных в методы класса. (Новичок, качество кода сам знаю что говно). Прошу указать на ошибки с возможным решением, заранее спасибо.

Visual Studio выдает это: Поток 0x1f20 завершился с кодом 0 (0x0). HEAP[LAB_1_AiStrD.exe]: Invalid address specified to RtlValidateHeap( 01290000, 012AC5F8 ) LAB_1_AiStrD.exe вызвал срабатывание точки останова.

Вот нужный кусок кода: main.cpp

#include <iostream>
#include "List.h"
typedef unsigned int uint;
using namespace std;
int* createArray(int*& array, uint num);
int* fillArray(int*& array, uint num);
void printArray(int* array, uint num);
void deleteArray(int* array);
int* addNewElement(int*& array, uint num, int newData);
int* deleteLastElement(int*& array, uint num);
List fillDeque(List deQue, uint num);
int main()
{
    setlocale(LC_ALL, "rus");
    uint num;
    int t, actType;
    startPoint:
    cout << "Выберите действие: \n 1. Работа с динамическим массивом \n 2. Работа с двухсвязным списком \n";
    cout << " 3. Перевод из инфиксной формы в постфиксную (обратная польская нотация)\n";
    cin >> actType;
    switch (actType)
    {
        case (1):
        {
            cout << "Ввдите целое положительное число для определения длины массива..." << endl;
            std::cin >> num;
            if ((num >= 1) && (num <= 4294967295))
            {
                int* array = NULL;
                createArray(array, num);
                fillArray(array, num);
                cout << "Динамический массив успешно создан и заполнен случайными данными!" << endl;
                cout << "Для продолжения выберите дейстие\n a. Добавить новый элемент в конец. \n b. Убрать последний элемент" << endl;
                char mode;
                std::cin >> mode;
                if ((mode == 97) || (mode == 65)) // 97 is a, 65 is A
                {
                    cout << "Данные в массиве: " << endl;
                    printArray(array, num);
                    int newData = (rand() % 100);
                    array = addNewElement(array, num, newData);
                    cout << "Новый сгенерированный элемент " << newData << " был добавлен в конец динамического массива" << endl;
                    cout << "Данные в массиве: " << endl;
                    printArray(array, num+1);
                }
                else if ((mode == 98) || (mode == 66)) // 98 is b, 66 is B
                {
                    cout << "Данные в массиве: " << endl;
                    printArray(array, num);
                    uint Last_Elm = array[sizeof(array) / sizeof(array[0])]; // копируем значение последнего элемента для вывода
                    array = deleteLastElement(array, num);
                    cout << "Последний элемент " << array[Last_Elm] << " был удален из динамического массива" << endl;
                    cout << "Данные в массиве: " << endl;
                    printArray(array, num-1);
                }
                else
                {
                    cout << endl << "Ошибка! " << endl;
                }
            }
            else
            {
                cout << endl << "Ошибка! " << endl;
            }
            cout << "\n Хотите продолжить? да - 1; нет - 0" << endl;
            std::cin >> t;
            if (t == 1)
            {
                std::system("cls");
                goto startPoint;
            }
            break;
        }
    case (2):
    {
        cout << "Ввдите целое положительное число для определения длины списка..." << endl;
        cin >> num;
        if (num >= 1)
        {
            List deQue;                     //Объявляем переменную, тип которой есть список
            deQue = fillDeque(deQue, num);  //Добавляем в список элементы
            cout << "Данные в списке. Вывод сначала списка: " << endl;
            deQue.ShowStart();              //Отображаем список на экране
            char listmode;
            cout << "Выберите действие: \n a. Добавить новый элемент в начало. \n b. Убрать первый элемент.\n";
            cout << " c. Добавить новый элемент в конец.\n d. Убрать последний элемент. \n";
            cin >> listmode;
            switch (listmode)
            {
            case ('a'): //Добавить новый элемент в начало
            {
                cout << "Данные в списке. Вывод сначала списка: " << endl;
                deQue.ShowStart();
                deQue.AddFirst(rand() % 100);
                cout << "Новый сгенерированный элемент ";
                deQue.ShowFirst();
                cout << " был добавлен в начало списка\n";
                cout << "Данные в списке. Вывод сначала списка: " << endl;
                deQue.ShowStart();
            }
            case ('b'): //Убрать первый элемент
            {
                cout << "Данные в списке. Вывод сначала списка: " << endl;
                deQue.ShowStart();
                deQue.DeleteFirst(1); // ?? what is argument ??
                cout << "Первый элемент был удален\n";
                cout << "Данные в списке. Вывод сначала списка: " << endl;
                deQue.ShowStart();
            }
            case ('c'): //Добавить новый элемент в конец
            {
                cout << "Данные в списке. Вывод с конца списка: " << endl;
                deQue.ShowEnd();
                deQue.AddLast(rand() % 100);
                cout << "Новый сгенерированный элемент ";
                deQue.ShowLast();
                cout << " был добавлен в конец списка\n";
                cout << "Данные в списке. Вывод с конца списка: " << endl;
                deQue.ShowEnd();
            }
            case ('d'): //Убрать последний элемент
            {
                cout << "Данные в списке. Вывод с конца списка: " << endl;
                deQue.ShowEnd();
                deQue.DeleteLast(1); // ?? what is argument ??
                cout << "Последний элемент был удален\n";
                cout << "Данные в списке. Вывод с конца списка: " << endl;
                deQue.ShowEnd();
            }
            default:
            {
                cout << "\n Хотите продолжить? да - 1; нет - 0" << endl;
                cin >> t;
                if (t == 1)
                {
                    std::system("cls");
                    goto startPoint;
                }
                else break;
            }
            }
            return 0;
        }
        else
        {
            cout << endl << "Ошибка! " << endl;
        }
        cout << "\n Хотите продолжить? да - 1; нет - 0" << endl;
        std::cin >> t;
        if (t == 1)
        {
            std::system("cls");
            goto startPoint;
        }
        break;
    }
    case (3):
    {
        // МЕСТО ДЛЯ ПОЛЬСКОЙ ОБРАТНОЙ чего-то там....
    }
    default:
    {
        cout << "\n Хотите продолжить? да - 1; нет - 0" << endl;
        std::cin >> t;
        if (t == 1)
        {
            std::system("cls");
            goto startPoint;
        }
        else break;
    }
    }
    return 0;
}

array.cpp

#include <iostream>
typedef unsigned int uint;
using namespace std; 
int* createArray(int*& array, uint num)
{
    array = new int[num];
    return array;
}
int* fillArray(int*& array, uint num)
{
    for (uint i = 0; i < num; i++)
    {
        array[i] = rand() % 100 + 1;
    }
    return array;
}
void printArray(int* array, uint num)
{
    for (uint i = 0; i < num; i++)
    {
        cout << array[i] << "\t";
    }
    cout << endl;
}
void deleteArray(int* array)
{
    delete[] array;
}
int* addNewElement(int*& array, uint num, int newData)
{
    int *temp = NULL;
    uint numTemp = num + 1;
    temp = createArray(temp, numTemp); // создаем временный массив
    for (uint i = 0; i < num; i++) temp[i] = array[i]; // копируем данные со старого массива
    temp[numTemp-1] = newData; // добавляем новый элемент в конец нового массива 
    deleteArray(array);
    return temp;
}
int* deleteLastElement(int*& array, uint num)
{
    int* temp = NULL;
    int numTemp = num - 1;
    temp = createArray(temp, numTemp);
    for (int i = 0; i < numTemp; i++) temp[i] = array[i]; // копируем данные со старого массива
    deleteArray(array); // удаляем старый массив
    return temp;
}

List.h

#pragma once
//  #pragma once препроцессорная директива, разработанная для контроля за тем
//  чтобы конкретный исходный файл при компиляции подключался строго один раз
struct Node                             //Структура, являющаяся звеном списка
{
    int x;                              //Значение x будет передаваться в список
    Node* Next, * Prev;                 //Указатели на адреса следующего и предыдущего элементов списка
};
class List                                  //Создание типа данных "Список"
{
    Node* Head, * Tail;                     //Указатели на адреса начала списка и его конца
public:
    List() :Head(NULL), Tail(NULL) {};      //Инициализируем адреса как пустые
    ~List();                                //Прототип деструктора
    void ShowStart();                       //Прототип функции отображения списка на экране сначала
    void ShowEnd();                         //Прототип функции отображения списка на экране с конца
    void ShowLast();
    void ShowFirst();
    void AddLast(int x);       //Прототип функции добавления элементов в конец списка
    void AddFirst(int x);                   //Прототип функции добавления элементов в начало списка
    void DeleteFirst(int x);                //Прототип функции удаления элементов сначала списка
    void DeleteLast(int x);                 //Прототип функции удаления элементов с конца списка
    void DeleteAllList(int x);              //Прототип функции удаления всего списка
};

List.cpp

#include <iostream>
#include <stdlib.h>
#include "List.h"
using namespace std;
/* Деструктор — это функция-член, которая вызывается автоматически,
когда объект выходит из области действия или явно уничтожается вызовом Delete */
List::~List()                               //Деструктор  
{
    while (Head)                            //Пока по адресу на начало списка что-то есть
    {
        Tail = Head->Next;                  //Резервная копия адреса следующего звена списка
        delete Head;                        //Очистка памяти от первого звена
        Head = Tail;                        //Смена адреса начала на адрес следующего элемента
    }
}
void List::AddLast(int x)
{
    Node* temp = new Node;                  //Выделение памяти под новый элемент структуры
    temp->Next = NULL;                      //Указываем, что изначально по следующему адресу пусто
    temp->x = x;                            //Записываем значение в структуру
    if (Head != NULL)                       //Если список не пуст
    {
        temp->Prev = Tail;                  //Указываем адрес на предыдущий элемент в соотв. поле
        Tail->Next = temp;                  //Указываем адрес следующего за хвостом элемента
        Tail = temp;                        //Меняем адрес хвоста
    }
    else                                    //Если список пустой
    {
        temp->Prev = NULL;                  //Предыдущий элемент указывает в пустоту
        Head = Tail = temp;                 //Голова=Хвост=тот элемент, что сейчас добавили
    }
}
void List::AddFirst(int x)
{
    Node* temp = new Node;                  //Выделение памяти под новый элемент структуры
    temp->Prev = NULL;                      //Указываем, что изначально по следующему адресу начало списка
    temp->x = x;                            //Записываем значение в структуру
    if (Head != NULL)                       //Если список не пуст
    {
        temp->Next = Head;                  //Указываем адрес на следующий элемент в соотв. поле
        Head->Prev = temp;                  //Указываем адрес педыдущего за началом элемента
        Head = temp;                        //Меняем адрес начала
    }
    else                                    //Если список пустой
    {
        temp->Next = NULL;                  //Cледующий элемент указывает в пустоту
        Head = Tail = temp;                 //Голова=Хвост=тот элемент, что сейчас добавили
    }
}
void List::DeleteFirst(int x)
{
    //Если удаляем первый элемент, то могут быть такие варианты
    //В списке есть только первый, в списке есть несколько элементов
    //Поэтому разбиваем логику выполнения
    if ((x == 1) and (Head->Next))          //Если удаляем первый, но есть и другие, то
    {
        Node* temp = Head;                  //Указываем, что нам нужно начало списка
        Head = Head->Next;                  //Сдвигаем начало на следующий за началом элемент
        Head->Prev = NULL;                  //Делаем так, чтоб предыдущий началу элемент был пустым
        delete temp;                        //Удаляем удаляемое начало
        return;
    }
    else if ((x == 1) and (Head == Tail))   //Если удаляем первый, но в списке только 1 элемент
    {
        Head->Next = NULL;                  //обнуляем все что нужно
        Head = NULL;
        delete Head;                        //Удаляем указатель на начало
        return;
    }
}
void List::DeleteLast(int x)
{
    //удаляемый элемент является последним элементом списка
    Node* temp = Tail;                      //Указываем, что нам нужен хвост
    Tail = Tail->Prev;                      //Отодвигаем хвост немного назад
    Tail->Next = NULL;                      //Обозначаем, что впереди за хвостом пусто
    delete temp;                            //Очищаем память от бывшего хвоста                               
}
void List::DeleteAllList(int x)
{
    while (Head != NULL)                    // пока не указываем на хвост
    {
        Node* temp = Head;                  // создаем временный элемент
        Head = Head->Next;                  // присваиваем ему указатель на следующий
        delete temp;                        // и удаляем его
    }
    Head = NULL;
    Tail = NULL;
}

void List::ShowStart()
{
    //ВЫВОДИМ СПИСОК С НАЧАЛА
    Node* temp = Head;                      //Временно указываем на адрес первого элемента
    while (temp != NULL)                    //Пока не встретим пустое значение
    {
        cout << temp->x << " ";             //Выводим каждое считанное значение на экран
        temp = temp->Next;                  //Смена адреса на адрес следующего элемента
    }
    cout << "\n";
}
void List::ShowEnd()
{
    //ВЫВОДИМ СПИСОК С КОНЦА
    Node* temp = Tail;                      //Временный указатель на адрес последнего элемента
    while (temp != NULL)                    //Пока не встретится пустое значение
    {
        cout << temp->x << " ";             //Выводить значение на экран
        temp = temp->Prev;                  //Указываем, что нужен адрес предыдущего элемента
    }
    cout << "\n";
}
void List::ShowLast()
{
    Node* temp = Tail;              //Временный указатель на адрес последнего элемента
    cout << temp->x;                //Выводить значение на экран
    delete temp;
}
void List::ShowFirst()
{
    Node* temp = Head;              //Временный указатель на адрес последнего элемента
    cout << temp->x;                //Выводить значение на экран
    delete temp;
}

fillDeque.cpp

#include <iostream>
#include "List.h"
typedef unsigned int uint;
using namespace std;
List fillDeque(List deQue, uint num)
{
    for (uint i = 0; i < num; i++)
    {
        deQue.AddLast(rand() % 100 + 1);
    }
    return deQue;
}
Answer 1

Начнем отсюда:

void List::AddLast(int x)
{
    Node* temp = new Node;                
    temp->Next = NULL;                      
    temp->x = x; 
    if (Head != NULL)                       
        Tail->Next = temp;               
        Tail = temp; 
   //... 

Когда вы добавляете второй элемент, то выполняется Head != NULL . На что будут указывать Tail->prev, Tail->next и Head->next после выполнения кода в условии?..

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

Answer 2

Проблема была в методе ShowFirst(), строчка delete temp

 void List::ShowFirst()
    {
        Node* temp = Head;              //Временный указатель на адрес последнего элемента
        cout << temp->x;                //Выводить значение на экран
        // delete temp;       // не надо удалить
    }
READ ALSO
Множественное обьявление

Множественное обьявление

Вот, что выдает компилятор, все функции и переменные объявлены один раз

99
Какая разница между a.b и a-&gt;b?

Какая разница между a.b и a->b?

У меня есть код, с двумя вариантами, первый который работает с оператором afirst и второй который работает с оператором a->first для меня разницы...

253
Конфликт обьявлений mySQL connector + C++

Конфликт обьявлений mySQL connector + C++

Пытаюсь использовать коннектер MySQL и C++ среда : Windows 10 Code::Blocks 1712 MySQL 8

346