为什么在复制赋值之前调用复制构造函数?
why is the copy constructor being called before copy assignment?
class LinkedList
{
public:
LinkedList() : _head(nullptr) {}
LinkedList(ListElement *newElement) : _head(newElement) {}
~LinkedList() { };
LinkedList(const LinkedList& LL);
LinkedList& operator=(LinkedList byValLinkedList);
private:
ListElement *_head;
}
LinkedList::LinkedList(const LinkedList & LL)
{
ListElement *curr = LL._head;
// If Linked List is empty
if (isEmpty() && curr != nullptr) {
_head = new ListElement(curr->getValue());
curr = curr->getNext();
}
ListElement *newNode = nullptr;
while (curr) {
newNode = new ListElement(curr->getValue());
curr = curr->getNext();
}
}
LinkedList& LinkedList::operator=(LinkedList byValLinkedList)
{
std::swap(_head, byValLinkedList._head);
return *this;
}
int main() {
using namespace std;
LinkedList LL1(new ListElement(7));
//..... some insertions
LinkedList LL2(new ListElement(5));
//..... some insertions
LL1 = LL2; // What is the order ?
// ..... do something else
return 0;
}
When LL1 = LL2 is executed, which one is supposed to be called.
I expect the copy-assignment to happen. But the code was executed in the following order
- Copy Constructor
- Copy-Assignemnt
- Destructor
What am i doing wrong ? and why was the destructor called?
LinkedList& operator=(LinkedList byValLinkedList);
您的复制分配按值获取其参数。这意味着
LL1=LL2;
需要复制 LL2
,以便按值传递它。这就是“按价值传递”的意思。因此,复制构造函数。
为避免复制构造,赋值运算符必须通过引用获取其参数,而不是:
LinkedList& operator=(const LinkedList &byValLinkedList);
这意味着,当然,您不能完全使用 std::swap
实现赋值运算符。但那将是一个不同的问题...
简而言之,您有两个选择:实现两个复制构造函数,一个接受 const
引用,一个不接受,后者可以使用 std::swap
。或者,将 _head
声明为 mutable
。
在您的赋值运算符中,byVallinkedList 是按值传递的。该 LinkedList 对象是使用您的复制构造函数初始化的
您没有做错任何事情,这正是复制和交换的工作原理。
调用复制构造函数来设置按值传递的参数。太好了,否则您的复制赋值运算符将必须包含用于制作副本的代码。这样,你就可以重用拷贝构造函数中的逻辑了。
然后,参数超出范围并在函数结束时被销毁。由于交换调用,参数现在包含以前由 *this
持有的资源。也非常可取,因为析构函数负责释放它们——否则您将不得不为复制赋值运算符编写清理代码,以正确地删除被赋值替换的数据。
除了代码重用之外,复制和交换还为您提供异常安全性。如果您直接将副本复制到左侧对象 (*this
),那么如果出现任何问题,您已经丢失了旧值并且不能保持不变。但是使用复制和交换,复制构造函数首先完成它的工作——如果出现任何问题,例如 运行 内存不足,*this
将保留其先前的值。
这里有对复制和交换习语的非常广泛的解释:
- What is the copy-and-swap idiom?
class LinkedList
{
public:
LinkedList() : _head(nullptr) {}
LinkedList(ListElement *newElement) : _head(newElement) {}
~LinkedList() { };
LinkedList(const LinkedList& LL);
LinkedList& operator=(LinkedList byValLinkedList);
private:
ListElement *_head;
}
LinkedList::LinkedList(const LinkedList & LL)
{
ListElement *curr = LL._head;
// If Linked List is empty
if (isEmpty() && curr != nullptr) {
_head = new ListElement(curr->getValue());
curr = curr->getNext();
}
ListElement *newNode = nullptr;
while (curr) {
newNode = new ListElement(curr->getValue());
curr = curr->getNext();
}
}
LinkedList& LinkedList::operator=(LinkedList byValLinkedList)
{
std::swap(_head, byValLinkedList._head);
return *this;
}
int main() {
using namespace std;
LinkedList LL1(new ListElement(7));
//..... some insertions
LinkedList LL2(new ListElement(5));
//..... some insertions
LL1 = LL2; // What is the order ?
// ..... do something else
return 0;
}
When LL1 = LL2 is executed, which one is supposed to be called.
I expect the copy-assignment to happen. But the code was executed in the following order
- Copy Constructor
- Copy-Assignemnt
- Destructor
What am i doing wrong ? and why was the destructor called?
LinkedList& operator=(LinkedList byValLinkedList);
您的复制分配按值获取其参数。这意味着
LL1=LL2;
需要复制 LL2
,以便按值传递它。这就是“按价值传递”的意思。因此,复制构造函数。
为避免复制构造,赋值运算符必须通过引用获取其参数,而不是:
LinkedList& operator=(const LinkedList &byValLinkedList);
这意味着,当然,您不能完全使用 std::swap
实现赋值运算符。但那将是一个不同的问题...
简而言之,您有两个选择:实现两个复制构造函数,一个接受 const
引用,一个不接受,后者可以使用 std::swap
。或者,将 _head
声明为 mutable
。
在您的赋值运算符中,byVallinkedList 是按值传递的。该 LinkedList 对象是使用您的复制构造函数初始化的
您没有做错任何事情,这正是复制和交换的工作原理。
调用复制构造函数来设置按值传递的参数。太好了,否则您的复制赋值运算符将必须包含用于制作副本的代码。这样,你就可以重用拷贝构造函数中的逻辑了。
然后,参数超出范围并在函数结束时被销毁。由于交换调用,参数现在包含以前由 *this
持有的资源。也非常可取,因为析构函数负责释放它们——否则您将不得不为复制赋值运算符编写清理代码,以正确地删除被赋值替换的数据。
除了代码重用之外,复制和交换还为您提供异常安全性。如果您直接将副本复制到左侧对象 (*this
),那么如果出现任何问题,您已经丢失了旧值并且不能保持不变。但是使用复制和交换,复制构造函数首先完成它的工作——如果出现任何问题,例如 运行 内存不足,*this
将保留其先前的值。
这里有对复制和交换习语的非常广泛的解释:
- What is the copy-and-swap idiom?