почему конструктор копирования вызывается перед назначением копирования?

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, какой из них должен вызываться.

Я ожидаю, что копирование-присвоение произойдет. Но код выполнялся в следующем порядке

  1. Копировать конструктор
  2. Copy-Assignemnt
  3. Деструктор

Что я делаю не так? и почему был вызван деструктор?


person LotsInLife    schedule 31.05.2016    source источник
comment
Основываясь на std::swap, я думаю, вы действительно захотите изучить конструкторы перемещения и назначение перемещения.   -  person Todd Christensen    schedule 31.05.2016


Ответы (3)


 LinkedList& operator=(LinkedList byValLinkedList);

Ваш конструктор копирования принимает свой параметр по значению. Это значит, что

 LL1=LL2;

необходимо сделать копию LL2, чтобы передать его по значению. Вот что значит "передача по значению". Следовательно, конструктор копирования.

Чтобы избежать создания копии, оператор присваивания должен вместо этого брать свой параметр по ссылке:

 LinkedList& operator=(const LinkedList &byValLinkedList);

Это означает, что вы, конечно, не можете реализовать оператор присваивания, используя std::swap. Но это будет уже другой вопрос...

Вкратце, у вас есть два варианта: либо реализовать два конструктора копирования, один из которых принимает ссылку const, а другой — нет, при этом последний может использовать std::swap. Или объявите _head как mutable.

person Sam Varshavchik    schedule 31.05.2016
comment
При копировании и обмене вы не хотите избегать вызова конструктора копирования. Вы хотите, чтобы обе операции копирования (копирование присваивания и копирование) были реализованы только один раз в конструкторе копирования. - person Ben Voigt; 31.05.2016

Вы не делаете ничего плохого, именно так и должно работать копирование и замена.

Конструктор копирования вызывается для установки параметра, который передается по значению. Это здорово, потому что в противном случае ваш оператор присваивания копии должен был бы содержать код для создания копии. Таким образом, вы можете повторно использовать логику в конструкторе копирования.

Затем параметр выходит за пределы области видимости и уничтожается в конце функции. Из-за вызова подкачки параметр теперь содержит ресурсы, которые раньше удерживались *this. Также очень желательно, потому что деструктор заботится об их освобождении - в противном случае вам пришлось бы писать код очистки для оператора присваивания копирования, чтобы правильно избавиться от данных, которые заменяются присваиванием.

В дополнение к повторному использованию кода копирование и подкачка обеспечивает безопасность исключений. Если вы сделали копию прямо в левый объект (*this), то если что-то пошло не так, вы уже потеряли старое значение и не можете оставить все без изменений. Но при использовании копирования и подкачки сначала выполняет свою работу конструктор копирования — если что-то пойдет не так, например, из-за нехватки памяти, *this сохранит свое предыдущее значение.

Здесь есть очень подробное объяснение идиомы копирования и замены:

person Ben Voigt    schedule 31.05.2016

В вашем операторе присваивания byVallinkedList передается по значению. Этот объект LinkedList инициализируется с помощью вашего конструктора копирования

person Stan Holodnak    schedule 31.05.2016