[英]Linked List: How to implement Destructor, Copy Constructor, and Copy Assignment Operator?
這是我的 C++ 代碼:
#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++的三原則。 其中實現: Destructor 、 Copy Constructor和Copy Assignment Operator 。
我看過很多關於這個的視頻。 我確實理解這基本上是處理淺復制,尤其是當我們不希望兩個不同的對象指向相同的內存地址時。 但是,對於我的問題是,我仍然不知道如何在處理鏈表的類上實現它,就像我上面的代碼一樣。
有人說,在我的main()
,這段代碼是: l1.Merge(l2);
不知何故不正確,因為我沒有明確的復制構造函數。
如果你看看我的Merge()
函數,在最后一行,如果我沒有這樣做: b.last = NULL;
和b.first = NULL;
,它只是破壞第二個鏈表的指針,編譯器給我警告:檢測到雙空閑() 。
所以,我想我的問題是:
l1.Merge(l2);
和復制構造函數有關系嗎?Double free()
是不是因為我沒有執行三規則而發生的? 如果是,如何解決它們?謝謝你。 我希望有人能像我 10 歲一樣向我解釋。 並希望有人能給我寫一些代碼。
這段代碼中應用了幾個有問題的做法,還有一個錯誤。
首先,錯誤。 創建列表時,它會new
所有節點並使用指針跟蹤它們。 當您將一個列表分配給另一個列表時,您實際上是復制指針值。 您現在不僅丟失了已分配列表的節點(因為您覆蓋了它們)並出現了內存泄漏(因為現在沒有指向已分配節點的指針),您現在還在兩個不同的列表上擁有相同的指針,指向相同的節點。 當列表被銷毀時,它們都試圖delete
它們的節點,你最終釋放了兩次相同的內存。 哎呀。
解決此錯誤的方法是實現賦值運算符。
然后,有問題的做法:
using namespace std;
( 為什么“使用命名空間標准;”被認為是不好的做法? )LinkedList
的成員,而不是將值直接傳遞給它們在初始化列表中的構造函數。 ( 構造函數中這個奇怪的冒號成員(“:”)語法是什么? )int[]
)就是聲明一個指針。 請注意它。new
不能返回NULL
! 檢查它的返回值是沒有用的。 如果它不能分配,它只會拋出一個異常。NULL
是不適合使用的常量。 您可以使用nullptr
,它是NULL
的 C++ 等價物,但它是類型安全的。new
和delete
手動內存管理很難做到正確(正如您自己發現的那樣)。 您可能對使用std::unique_ptr
或std::shared_ptr
來減輕負擔感興趣。 他們會發現錯誤。現在,請:不要像使用類的 C 那樣用 C++ 編寫。 我知道您可能沒有遇到我在這里介紹的所有功能,但無論如何現在您都知道它們了 :)
但是,對於我的問題是,我仍然不知道如何在處理鏈表的類上實現 [三規則],就像我上面的代碼一樣。
您只需實現復制構造函數和復制賦值運算符來迭代輸入列表,制作每個節點的副本並將它們插入到您的目標列表中。 你已經有了一個有效的析構函數。 在復制賦值運算符的情況下,您通常可以使用copy-swap 慣用語來使用復制構造函數來實現它,以避免重復自己。
有人說,在我的
main()
,這段代碼是:l1.Merge(l2);
不知何故不正確,因為我沒有明確的復制構造函數。
然后你被告知錯了。 您的Merge()
代碼與復制構造函數無關。
如果你看看我的
Merge()
函數,在最后一行,如果我沒有這樣做:b.last = NULL;
和b.first = NULL;
,它只是破壞第二個鏈表的指針,編譯器給我警告:Double free() detected.
正確的。 由於您將節點從輸入列表移動到目標列表,您需要重置輸入列表,使其不再指向移動的節點。 否則,輸入列表的析構函數將嘗試釋放它們,目標列表的析構函數也將嘗試釋放它們。
這段代碼如何:
l1.Merge(l2);
和復制構造函數有關系嗎?
它與它沒有任何關系。
Double free()
是不是因為我沒有執行三規則而發生的?
不在您的特定示例中,因為您沒有執行任何復制操作。 但是,一般來說,不執行三規則會導致雙重釋放,是的。
如何根據我的代碼編寫三規則?
請參閱下面的代碼。
如果我的程序只想合並鏈表,我還需要三規則嗎?
否。僅當您想要制作列表副本時。
話雖如此,這是一個包含三規則的實現:
#include <iostream>
#include <utility>
struct Node
{
int data;
Node *next;
};
class LinkedList
{
private:
Node *first;
Node *last;
public:
LinkedList();
LinkedList(const LinkedList &src);
LinkedList(int A[], int num);
~LinkedList();
LinkedList& operator=(const LinkedList &rhs);
void Display() const;
void Merge(LinkedList &b);
};
// Create Linked List using default values
LinkedList::LinkedList()
: first(NULL), last(NULL)
{
}
// Create Linked List using Array
LinkedList::LinkedList(int A[], int n)
: first(NULL), last(NULL)
{
Node **p = &first;
for (int i = 0; i < n; ++i)
{
Node *t = new Node;
t->data = A[i];
t->next = NULL;
*p = t;
p = &(t->next);
last = t;
}
}
// Create Linked List by copying another Linked List
LinkedList::LinkedList(const LinkedList &src)
: first(NULL), last(NULL)
{
Node **p = &first;
for (Node *tmp = src.first; tmp; tmp = tmp->next)
{
Node* t = new Node;
t->data = tmp->data;
t->next = NULL;
*p = t;
p = &(t->next);
last = t;
}
}
// Deleting all Node in Linked List
LinkedList::~LinkedList()
{
Node *p = first;
while (p)
{
Node *tmp = p;
p = p->next;
delete tmp;
}
}
// Update Linked List by copying another Linked List
LinkedList& LinkedList::operator=(const LinkedList &rhs)
{
if (&rhs != this)
{
LinkedList tmp(rhs);
std::swap(tmp.first, first);
std::swap(tmp.last, last);
}
return *this;
}
// Displaying Linked List
void LinkedList::Display() const
{
for (Node *tmp = first; tmp; tmp = tmp->next)
std::cout << tmp->data << " ";
std::cout << std::endl;
}
// Merge two linked list
void LinkedList::Merge(LinkedList& b)
{
if ((&b == this) || (!b.first))
return;
if (!first)
{
first = b.first; b.first = NULL;
last = b.last; b.last = NULL;
return;
}
// Store first pointer of Second Linked List
Node *second = b.first;
Node *third, **tmp = &third;
// 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
// Use while loop for repeating process until First or Second hit NULL
do
{
// If first Node data is smaller than second Node data
if (first->data < second->data)
{
*tmp = first;
tmp = &(first->next);
first = first->next;
}
// If first Node data is greater than second Node data
else
{
*tmp = second;
tmp = &(second->next);
second = second->next;
}
*tmp = NULL;
}
while (first && second);
// Handle remaining Node that hasn't pointed by Last after while loop
*tmp = (first) ? first : 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)
{
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.first = b.last = 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);
LinkedList l3(l1);
LinkedList l4;
l1.Display();
l2.Display();
l3.Display();
l4.Display();
// Merge two linked list, pass l2 as reference
l3.Merge(l2);
l4 = l3;
l1.Display();
l2.Display();
l3.Display();
l4.Display();
return 0;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.