Вопросы

Связанный список: как реализовать деструктор, конструктор копирования и оператор присваивания копирования?

Это мой код на С++:

#include <iostream>

using namespace std;

typedef struct Node
{   
    int data;
    Node* next;
}Node;

class LinkedList
{
private:
    Node* first;
    Node* last;
public:
    LinkedList() {first = last = NULL;};
    LinkedList(int A[], int num);
    ~LinkedList();

    void Display();
    void Merge(LinkedList& b);
  
};

// Create Linked List using Array
LinkedList::LinkedList(int A[], int n)
{   
    Node* t = new Node;
    if (t == NULL)
    {
        cout << "Failed allocating memory!" << endl;
        exit(1);
    }
    t->data = A[0];
    t->next = NULL;
    first = last = t;

    for (int i = 1; i < n; i++)
    {
        t = new Node;
        if (t == NULL)
        {
            cout << "Failed allocating memory!" << endl;
            exit(1);
        }
        t->data = A[i];
        t->next = NULL;
        
        last->next = t;
        last = t;
    }
}

// Deleting all Node in Linked List
LinkedList::~LinkedList()
{
    Node* p = first;
    Node* tmp;

    while (p != NULL)
    {
        tmp = p;
        p = p->next;
        delete tmp;
    }
}

// Displaying Linked List
void LinkedList::Display()
{
    Node* tmp;

    for (tmp = first; tmp != NULL; tmp = tmp->next)
        cout << tmp->data << " ";
    cout << endl;    
}

// Merge two linked list
void LinkedList::Merge(LinkedList& b)
{
    // Store first pointer of Second Linked List
    Node* second = b.first;
    Node* third = NULL, *tmp = NULL;

    // We find first Node outside loop, smaller number, so Third pointer will store the first Node
    // Then, we can only use tmp pointer for repeating process inside While loop
    if (first->data < second->data)
    {
        third = tmp = first;
        first = first->next;
        tmp->next = NULL;
    }
    else
    {
        third = tmp = second;
        second = second->next;
        tmp->next = NULL;
    }

    // Use while loop for repeating process until First or Second hit NULL
    while (first != NULL && second != NULL)
    {
        // If first Node data is smaller than second Node data
        if (first->data < second->data)
        {
            tmp->next = first;
            tmp = first;
            first = first->next;
            tmp->next = NULL;
        }
        // If first Node data is greater than second Node data
        else
        {
            tmp->next = second;
            tmp = second;
            second = second->next;
            tmp->next = NULL;
        }
    }

    // Handle remaining Node that hasn't pointed by Last after while loop
    if (first != NULL)
        tmp->next = first;
    else
        tmp->next = second;

    // Change first to what Third pointing at, which is First Node
    first = third;    

    // Change last pointer from old first linked list to new last Node, after Merge
    Node* p = first;
    while (p->next != NULL)
    {
        p = p->next;
    }    
    last = p;
    
    // Destroy second linked list because every Node it's now connect with first linked list
    // This also prevent from Double free()
    b.last = NULL;
    b.first = NULL;
}

int main()
{
    int arr1[] = {4, 8, 12, 14, 15, 20, 26, 28, 30};
    int arr2[] = {2, 6, 10, 16, 18, 22, 24};
    int size1 = sizeof(arr1) / sizeof(arr1[0]);
    int size2 = sizeof(arr2) / sizeof(arr2[0]);
    
    LinkedList l1(arr1, size1);
    LinkedList l2(arr2, size2);

    l1.Display();
    l2.Display();
    
    // Merge two linked list, pass l2 as reference
    l1.Merge(l2);
    l1.Display();

    return 0;
}

Я новичок в C++, и в этом коде я практикую объединение двух связанных списков. Это на самом деле отлично работает. Я успешно объединил два связанных списка в отсортированном порядке.

Но есть люди, которые говорят, что я должен следовать Правилу трех в C++. Какие реализуют: Деструктор, Конструктор копирования и Оператор присваивания копирования.

Я смотрел много видео об этом. Я понимаю, что это в основном обработка Shallow Copy, особенно когда мы не хотим, чтобы два разных объекта указывали на один и тот же адрес памяти. Но моя проблема в том, что я до сих пор не знаю, как реализовать это в классе, который работает со связанным списком, как мой код выше.

Читать:
Как добавить тестовые примеры в Microsoft Test Manager через программу

Кто-то сказал, что в моем main() этот код: l1.Merge(l2); как-то неверен, потому что у меня нет явного конструктора копирования.

И если вы посмотрите на мою функцию Merge() в последней строке, если бы я этого не сделал: b.last = NULL; и b.first = NULL; , которые просто уничтожают указатель второго связанного списка, компилятор выдает мне предупреждение: Double free() обнаружено.

Итак, я думаю, что мой вопрос:

  1. Как этот код: l1.Merge(l2); может иметь какое-то отношение к конструктору копирования?
  2. Произошло ли Double free() из-за того, что я не применяю правило трех? Если да, то как их решить?
  3. Как написать правило трех на основе моего кода? Когда или как их использовать?
  4. Основываясь на этом Кодексе, что-то не так? Нужна ли мне по-прежнему Правило трех, если моя программа хочет только объединить связанный список?

Спасибо. Я надеюсь, что кто-то может объяснить мне, как будто мне 10 лет. и надеюсь, что кто-то может написать мне код.

Как написать правило трех на основе моего кода? Когда или как их использовать? Каждый раз, когда ваш класс выделяет память, которой он владеет, и использует необработанные указатели, вам нужно будет следовать правилу 3 или 5.   —  person Kevinkun    schedule 16.07.2021

Двойное освобождение() произошло из-за того, что я не применяю правило трех? Да, двойное освобождение является вероятным результатом невыполнения правила трех.   —  person Kevinkun    schedule 16.07.2021

@drescherjm Можете ли вы быть более конкретным? Моего конструктора по умолчанию недостаточно для этого?   —  person Kevinkun    schedule 16.07.2021

@Kevinkun l1 = l2; — и — LinkedList l3 = l1; — Вы пробовали этот простой тест? Ваша программа main, похоже, избегает необходимости проверять копирование и присваивание. Вы увидите, что все разваливается или работает правильно, просто написав эти тестовые строки.   —  person Kevinkun    schedule 16.07.2021

@Kevinkun Кстати, вы должны были внедрить правило трех способов до написания Merge или любой подобной функции. Может быть, вставить и удалить, но все более сложное следует отложить после того, как вы подтвердите, что копирование, присваивание и удаление (правило 3) работают правильно. Прямо сейчас вы добавляете функции поверх неисправного фундамента. В вашем случае, сразу после того, как вы внедрили конструктор для вставки элементов массива в список, следующим делом будет реализация копирования и присваивания (и уничтожения).   —  person Kevinkun    schedule 16.07.2021

Возможная реализация состоит в том, чтобы просто явно удалить оператор копирования и оператор присваивания, если они вам не нужны. Таким образом, если ваш код позже попытается их использовать, вы получите ошибку времени компиляции. И поскольку вы уничтожаете l2 в merge, я бы использовал ссылку &&, чтобы сделать это явным.   —  person Kevinkun    schedule 16.07.2021

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

@SergeBallesta Можете ли вы объяснить, почему я должен использовать &&? и как его использовать?   —  person Kevinkun    schedule 16.07.2021

stackoverflow.com/questions/12606574/   —  person Kevinkun    schedule 16.07.2021

Похожие записи

Зависит от cloud_firestore 0.14.4 и http 0.13.3, решение версии не удалось

admin

ВНИМАНИЕ: индекс RubyGems 1.2+ не найден для: RubyGems вернется к устаревшим индексам, что снизит производительность

admin

Почему в двоичных файлах ELF байты инвертируются группами по два

admin

Как определить неудачные приведения с помощью оператора dynamic_cast?

admin

composeEnhancers не является функцией в ReactJS

admin

Окно PowerShell при запуске из пакетного файла

admin