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

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

#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, особенно когда мы не хотим, чтобы два разных объекта указывали на один и тот же адрес памяти. Но моя проблема в том, что я до сих пор не знаю, как реализовать это в классе, который работает со связанным списком, как мой код выше.

Кто-то сказал, что в моем 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

Источник: ledsshop.ru

Стиль жизни - Здоровье!