Это мой код на С++:
#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() обнаружено.
Итак, я думаю, что мой вопрос:
- Как этот код:
l1.Merge(l2);может иметь какое-то отношение к конструктору копирования? - Произошло ли
Double free()из-за того, что я не применяю правило трех? Если да, то как их решить? - Как написать правило трех на основе моего кода? Когда или как их использовать?
- Основываясь на этом Кодексе, что-то не так? Нужна ли мне по-прежнему Правило трех, если моя программа хочет только объединить связанный список?
Спасибо. Я надеюсь, что кто-то может объяснить мне, как будто мне 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
