Переопределить = оператор связанный связанный глубокий экземпляр С ++

0

Итак, я пытаюсь переопределить operator = в связанном классе списка, который я пишу, но по какой-то причине по-прежнему получаю эту странную проблему.

List& List::operator=(const List& copyList){
if(copyList.head != nullptr){
    makeEmpty();   // clears *this from any previous nodes
    cout << "if statement " << endl;
    head = new Node; // create a new node for head
    head -> data = copyList.head -> data; // copy the first data of copylist
    Node* pnew = head; // a temp node to traverse the new linkedlist
    assert(head != nullptr);
    Node* current2 = copyList.head;
    current2 = current2 -> next;
    while(current2 != NULL && pnew != NULL){
        cout << "entering while loop " << endl;
        pnew-> next = new Node;
        pnew -> next->data = current2 ->data;
        cout << "pnew next data " << *(pnew -> next->data)  << endl;
        assert(pnew-> next != nullptr);
        pnew = pnew -> next;
        current2 = current2 -> next;
        cout << "after current2" << endl;
    }
     pnew -> next = NULL;

}else{
    cout << "else statement " << endl;
    head = nullptr;
}
cout<< "printing out copylist"<< endl << copyList << endl;
cout<< "printing current list: " << endl << *this << endl;
return *this;

}

Итак, это код, который я должен проверить для переопределения оператора:

cout << "mylist:" << endl << mylist << endl;
   cout << "mylist4:" << endl << mylist4 << endl;
   mylist = mylist4;
   cout << "mylist:" << endl;
   cout << mylist << endl;
   cout << "mylist4:" << endl;
   cout << mylist4 << endl;

Это результат:

mylist:
10 f
16 u
20 n
25 !

mylist4:
14 s
15 t
16 u
18 f
19 f
25 !

if statement
entering while loop
pnew next data 15 t

after current2
entering while loop
pnew next data 16 u

after current2
entering while loop
pnew next data 18 f

after current2
entering while loop
pnew next data 19 f

after current2
entering while loop
pnew next data 25 !

after current2
printing out copylist
14 s
15 t
16 u
18 f
19 f
25 !

printing current list:
14 s
15 t
16 u
18 f
19 f
25 !
*crashes right here*

Я пытался выяснить эту проблему около 3 дней. Любая помощь будет высоко оценен. Заранее спасибо!

EDIT: вот конструктор (деструктор по умолчанию является компилятором):

NodeData::NodeData(int n, char c)  { 
    num = n; ch = c; 
} 

EDIT2: Я нашел проблему после тщательного изучения. Проблема заключалась в том, что я не указывал последний узел головы, pnew после цикла while, на null. Это устранило проблему. Благодаря вашей поддержке.

  • 1
    Вы не получите этот вывод с опубликованным кодом. Причина в первую очередь, она даже не скомпилируется . Там будет не if совпадающее else , и есть, по крайней мере , две основные логические вопросы , которые я вижу только в этом коде, который не дотягивает большие надежды для остальной части списка.
  • 0
    О, прости человек. Я редактировал это. Случайно, пропущено утверждение if.
Показать ещё 3 комментария
Теги:
linked-list
operator-keyword

2 ответа

2
Лучший ответ

У вас есть контроль над классом Node, и он должен поддерживать операции копирования и назначения операторов как прозрачно, так и при необходимости своими силами. Учитывая это, я согласен с Дитмаром, что использование механизма copy-ctor/swap/destructor является идеальным подходом.

Если вы настаиваете на том, чтобы сделать это вручную, это один из способов сделать это. Ваша реализация делает это намного сложнее, чем нужно.

List& List::operator=(const List& copyList)
{
    List tmp;
    Node **dst = &tmp.head;
    const Node* src = copyList.head;
    while (src)
    {
        *dst = new Node(*src);     // invoke Node copy-ctor
        src = src->next;           // advance source
        (*dst)->next = nullptr;    // break link to original next
        dst = &(*dst)->next;       // move target to next pointer
    }

    // tmp now has a copy of the source list
    //  swap its head pointer with ours.
    std::swap(tmp.head, head);

    // upon return, the tmp object that now holds our
    //  original list will clean it up. we own the
    //  new list form this point on. 
    return *this;
}

Как это работает

Указатель-на-указатель dst всегда содержит адрес следующего указателя, который будет заполнен новым узлом. Первоначально он содержит адрес локального указателя объекта List tmp объекта List tmp. , Когда мы добавляем узлы, он обновляется, чтобы всегда удерживать адрес next указателя добавленного последнего узла. Поступая таким образом, мы получаем прямую цепочку автоматически. Как только мы закончим копирование, tmp теперь является дубликатом источника. Затем мы меняем указатель на голову собственным. Это, в свою очередь, своп, которому принадлежит список. Когда tmp уничтожается на функции-выходе, он уничтожит наш старый список. Мы сохраняем новый список, на который указывает наш head указатель.

Вышеприведенное предполагает, что вы используете копию-ctor по умолчанию для Node, что означает, что копирование скопирует как значение данных, так и следующую ссылку, последнее вы НЕ хотите, таким образом, встроенный разрыв ссылки. Если вы на самом деле реализуете Node::Node(const Node&) чтобы всегда установить ссылку на NULL после копирования, разрыв ссылки может быть устранен. То есть, ваш экземпляр-конструктор Node должен выглядеть примерно так:

Node::Node(const Node& arg)
    : data(arg.data)
    , next()
{
}

Это гарантирует, что чистая (так же чистая, как data могут быть скопированы, в любом случае) копируется без случайной ссылки на исходный указатель arg next.

Все сказанное, ответ Дитмара является наиболее правильным, и я проголосовал за него соответствующим образом. Если это поможет вам, рассмотрите вопрос о голосовании, но это его идеальное решение.

  • 0
    Большое спасибо за ваши усилия. Это несколько помогло мне понять, что я делаю неправильно.
  • 0
    Я бы поддержал это, если код сначала скопирует список abd, а затем избавится от исходного! Указатель на указатель на следующий трюк аккуратный. Код - это примерно то, что должен делать конструктор копирования ...
Показать ещё 5 комментариев
2

Не смотря на жесткую реализацию (я остановился, когда обнаружил makeEmpty() который гарантирует, что оператор присваивания не будет строго безопасным без каких-либо оснований): самый простой способ реализовать оператор присваивания - использовать копию конструктор, деструктор и вообще тривиальная функция write swap(). Реализация выглядит одинаково для всех классов, за исключением имени класса:

T& T::operator= (T other) {
    other.swap(this);
    return *this;
}

Обратите внимание, что намеренно передавать аргумент по значению: это то, где значение действительно происходит. По сравнению с копированием на T const& имеет то преимущество, что фактическая копия может быть устранена в некоторых случаях.

  • 0
    Как я могу знать, что у T всегда есть метод swap ()? Я имею в виду, что для задания, которое мы даем, у них нет ни методов подкачки, ни конструкторов копирования; мы не можем добавлять методы к NodeData.
  • 0
    @ user2922605: Вы не меняете swap() некоторые данные узла! Вы вызываете swap() для класса, который вы реализуете, и если у него нет такого метода, вы реализуете его! Аналогично с конструктором копирования: в вашем списке обязательно есть конструктор копирования!
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню