[英]Linked List: How to implement Destructor, Copy Constructor, and Copy Assignment Operator?
This is my C++ code:这是我的 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;
}
I'm beginner on C++ and in this code, I practice how to Merge two linked list.我是 C++ 的初学者,在这段代码中,我练习了如何合并两个链表。 This actually works perfectly.
这实际上工作得很好。 I've successfully Merge the two Linked List in sorted order.
我已经成功地按排序顺序合并了两个链表。
But, there's people said that I should've follow the Rule of Three on C++.但是,有人说我应该遵循 C++的三原则。 Which implement: Destructor , Copy Constructor , and Copy Assignment Operator .
其中实现: Destructor 、 Copy Constructor和Copy Assignment Operator 。
I've watched many videos about that.我看过很多关于这个的视频。 I do understand that is basically handle Shallow Copy especially when we don't want two different object point to the same address of memory.
我确实理解这基本上是处理浅复制,尤其是当我们不希望两个不同的对象指向相同的内存地址时。 But, for my problem is, I still don't know how to Implement it on a Class that working on a Linked List just like my code above.
但是,对于我的问题是,我仍然不知道如何在处理链表的类上实现它,就像我上面的代码一样。
Someone said, in my main()
, this code: l1.Merge(l2);
有人说,在我的
main()
,这段代码是: l1.Merge(l2);
is somehow incorrect because I don't have explicit Copy Constructor.不知何故不正确,因为我没有明确的复制构造函数。
And if you look at my Merge()
function, in Last line, if I didn't to this: b.last = NULL;
如果你看看我的
Merge()
函数,在最后一行,如果我没有这样做: b.last = NULL;
and b.first = NULL;
和
b.first = NULL;
, which simply destroy pointer of Second Linked list, the Compiler give me warning: Double free() detected. ,它只是破坏第二个链表的指针,编译器给我警告:检测到双空闲() 。
So, I think my question is:所以,我想我的问题是:
l1.Merge(l2);
l1.Merge(l2);
is have something to do with Copy Constructor?Double free()
happened because I don't implement the Rule of Three? Double free()
是不是因为我没有执行三规则而发生的? If yes, how to address them? Thank You.谢谢你。 I hope someone can explain to me like I'm 10 years old.
我希望有人能像我 10 岁一样向我解释。 and hope someone can write me some Code.
并希望有人能给我写一些代码。
There are several questionable practices applied in this code, and there is also a bug.这段代码中应用了几个有问题的做法,还有一个错误。
First, the bug.首先,错误。 When you create a list, it
new
s all its nodes and keeps track of them using pointers.创建列表时,它会
new
所有节点并使用指针跟踪它们。 When you assign a list to another, you literally copy the pointer values.当您将一个列表分配给另一个列表时,您实际上是复制指针值。 Not only have you now lost the nodes of the assigned list (because you overwrote them) and got a memory leak (because now there's no pointer pointing to the allocated nodes), you also now have the same pointers on two different lists, pointing to the same nodes.
您现在不仅丢失了已分配列表的节点(因为您覆盖了它们)并出现了内存泄漏(因为现在没有指向已分配节点的指针),您现在还在两个不同的列表上拥有相同的指针,指向相同的节点。 When the lists are destroyed, both of them try to
delete
their nodes, and you end up freeing the same memory twice.当列表被销毁时,它们都试图
delete
它们的节点,你最终释放了两次相同的内存。 Yuk.哎呀。
The solution to this bug is to implement the assignment operator.解决此错误的方法是实现赋值运算符。
Then, the questionable practices:然后,有问题的做法:
using namespace std;
( Why is "using namespace std;" considered bad practice? ) LinkedList
in the constructor body, instead of passing the values directly to their constructor in the initialization list.LinkedList
的成员,而不是将值直接传递给它们在初始化列表中的构造函数。 ( What is this weird colon-member (" : ") syntax in the constructor? ) int[]
) is declaring a pointer.int[]
)就是声明一个指针。 Just be aware of it.new
cannot return NULL
! new
不能返回NULL
! It's useless to check its return value.NULL
is the inappropriate constant to use. NULL
是不适合使用的常量。 You can use nullptr
, it's the C++ equivalent of NULL
, except it's type safe.nullptr
,它是NULL
的 C++ 等价物,但它是类型安全的。new
and delete
is tricky to get right (as you figured out yourself).new
和delete
手动内存管理很难做到正确(正如您自己发现的那样)。 You might be interested in using std::unique_ptr
or std::shared_ptr
to alleviate the burden.std::unique_ptr
或std::shared_ptr
来减轻负担感兴趣。 They would have caught the bug. Now, please: do not write in C++ like it's C with classes.现在,请:不要像使用类的 C 那样用 C++ 编写。 I understand that you may not have encountered all of the features I presented here, but anyway now you know about them :)
我知道您可能没有遇到我在这里介绍的所有功能,但无论如何现在您都知道它们了 :)
But, for my problem is, I still don't know how to Implement [Rule of Three] on a Class that working on a Linked List just like my code above.
但是,对于我的问题是,我仍然不知道如何在处理链表的类上实现 [三规则],就像我上面的代码一样。
You simply implement the copy constructor and copy assignment operator to iterate the input list, making a copy of each node and inserting them into your target list.您只需实现复制构造函数和复制赋值运算符来迭代输入列表,制作每个节点的副本并将它们插入到您的目标列表中。 You already have a working destructor.
你已经有了一个有效的析构函数。 In the case of the copy assignment operator, you can usually use the copy-swap idiom to implement it using the copy constructor to avoid repeating yourself .
在复制赋值运算符的情况下,您通常可以使用copy-swap 惯用语来使用复制构造函数来实现它,以避免重复自己。
Someone said, in my
main()
, this code:l1.Merge(l2);
有人说,在我的
main()
,这段代码是:l1.Merge(l2);
is somehow incorrect because I don't have explicit Copy Constructor.不知何故不正确,因为我没有明确的复制构造函数。
Then you were told wrong.然后你被告知错了。 Your
Merge()
code has nothing to do with a copy constructor.您的
Merge()
代码与复制构造函数无关。
And if you look at my
Merge()
function, in Last line, if I didn't to this:b.last = NULL;
如果你看看我的
Merge()
函数,在最后一行,如果我没有这样做:b.last = NULL;
andb.first = NULL;
和
b.first = NULL;
, which simply destroy pointer of Second Linked list, the Compiler give me warning:Double free() detected.
,它只是破坏第二个链表的指针,编译器给我警告:
Double free() detected.
Correct.正确的。 Since you are moving the nodes from the input list to the target list, you need to reset the input list so it doesn't point at the moved nodes anymore.
由于您将节点从输入列表移动到目标列表,您需要重置输入列表,使其不再指向移动的节点。 Otherwise, the destructor of the input list will try to free them, as will the destructor of the target list.
否则,输入列表的析构函数将尝试释放它们,目标列表的析构函数也将尝试释放它们。
How can this code:
l1.Merge(l2);
这段代码如何:
l1.Merge(l2);
is have something to do with Copy Constructor?和复制构造函数有关系吗?
It doesn't have anything to do with it.它与它没有任何关系。
Is
Double free()
happened because I don't implement the Rule of Three?Double free()
是不是因为我没有执行三规则而发生的?
Not in your particular example, as you are not performing any copy operations.不在您的特定示例中,因为您没有执行任何复制操作。 But, in general, not implementing the Rule of Three can lead to double frees, yes.
但是,一般来说,不执行三规则会导致双重释放,是的。
How to write the Rule of Three based on my code?
如何根据我的代码编写三规则?
See the code below.请参阅下面的代码。
Do I still need the Rule of Three if my Program only want to Merge Linked List?
如果我的程序只想合并链表,我还需要三规则吗?
No. Only when you want to make copies of lists.否。仅当您想要制作列表副本时。
With that said, here is an implementation that includes the Rule of Three:话虽如此,这是一个包含三规则的实现:
#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.