Почему конструктор копирования вызывается перед назначением копирования?
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;
}
Когда LL1 = LL2 выполняется, какой из них должен быть вызван.
Я ожидаю, что назначение копирования произойдет. Но код был выполнен в следующем порядке
- Копировать конструктор
- Copy-Assignemnt
- Destructor
Что я делаю неправильно? и почему был назван деструктор?
3 ответа
LinkedList& operator=(LinkedList byValLinkedList);
Ваш конструктор копирования принимает его параметр по значению. Это означает, что
LL1=LL2;
необходимо сделать копию LL2
, чтобы передать его по значению. Вот что означает "передача по значению". Следовательно, конструктор копирования.
Чтобы избежать создания копирования, оператор присваивания должен взять свой параметр по ссылке, вместо этого:
LinkedList& operator=(const LinkedList &byValLinkedList);
Это означает, что если, конечно, вы не можете реализовать оператор присваивания, используя std::swap
, Но это был бы другой вопрос...
Вкратце, у вас есть два варианта: либо реализовать два конструктора копирования, один из которых принимает const
ссылка и тот, который не, с последним в состоянии использовать std::swap
, Или объявить _head
быть mutable
,
Вы не делаете ничего плохого, именно так должно работать копирование и обмен.
Конструктор копирования вызывается для установки параметра, который передается по значению. Это здорово, потому что в противном случае ваш оператор копирования должен был бы содержать код для создания копии. Таким образом, вы можете повторно использовать логику в конструкторе копирования.
Затем параметр выходит из области видимости и уничтожается в конце функции. Из-за вызова подкачки параметр теперь содержит ресурсы, которые раньше удерживались *this
, Также очень желательно, потому что деструктор позаботится об их освобождении - в противном случае вам придется написать код очистки для оператора присваивания копии, чтобы правильно избавиться от данных, которые заменяются присваиванием.
В дополнение к повторному использованию кода, копирование и замена дает вам исключительную безопасность. Если вы сделали копию прямо в левый объект (*this
), то если что-то пошло не так, вы уже потеряли старое значение и не можете оставить все без изменений. Но используя функцию копирования и замены, конструктор копирования сначала выполняет свою работу - если что-то пойдет не так, как, например, нехватка памяти, *this
сохраняет свое прежнее значение.
Здесь есть очень подробное объяснение идиомы копирования и обмена:
В вашем операторе присваивания byVallinkedList передается по значению. Этот объект LinkedList инициализируется с помощью вашего конструктора копирования