Перегрузка оператора присваивания в односвязном списке - PullRequest
0 голосов
/ 29 октября 2018

Я узнаю о связанном списке. Я создал реализацию шаблона с конструктором, вставкой, деструктором, конструктором копирования и перегруженным оператором присваивания. Проблема в том, что моя тестовая программа ничего не выводит после перегрузки оператора присваивания.

Для моего оператора присваивания я использую функцию Clear(), чтобы полностью очистить список перед копированием. Я положил его в деструктор и проверил, что он работает нормально. Я также проверил свой конструктор копирования, и он работал нормально.

Файл node.h: определяет узел строительный блок

#include <iostream>
using namespace std;

template <typename T>
struct Node{
    T _item;
    Node<T>* _next;

    Node() {
        _item = T();
        _next = NULL;
    }

    Node(T item){
        _item = item;
        _next = NULL;
    }

    // Print the value of a node
    friend std::ostream& operator <<(std::ostream& outs, const Node<T> &printMe){
        outs << "[" << printMe._item << "]";
        return outs;
    }
};

Файл list.h: определяет шаблон связанного списка

#include "node.h"

template <class T>
class List {
public:

    // default constructor
    List();

    // Destructor
    ~List();

    // Copy constructor
    List(const List<T> &copyThis);

    // Overloading assignment operator
    List& operator =(const List& RHS);

    // Insert i to the head of the linked list
    Node<T>* InsertHead(T i);

    // Clear a linked list
    void Clear();

    // Overload the output operator to print the list
    template <class U>
    friend ostream& operator <<(ostream& outs, const List<U>& l);

private:
    Node<T>* head;
};

Этот заголовок также обеспечивает реализацию этих функций-членов:

template <class T>
List<T>::List(){
    head = NULL;
}

template <class T>
List<T>::~List(){
    Clear();
}

template <class T>
List<T>::List(const List<T> &copyThis){
    if (copyThis.head == NULL)
        head = NULL;

    else {
        // Create walker for the original linked list
        Node<T>* walker = copyThis.head->_next;

        // Create new head node for new linked list
        head = new Node<T>(copyThis.head->_item);

        // Create new walker for new linked list
        Node<T>* new_walker = head;

        // Iterate walker and new walker and copy each item in the original list to new linked list
        while (walker!= NULL) {
            new_walker->_next = new Node<T>(walker->_item);
            walker = walker->_next;
            new_walker = new_walker->_next;
        }
    }
}

template <class T>
List<T>& List<T>::operator =(const List<T>& RHS){   // DOESN'T WORK
    if (this != &RHS) {
        this->Clear();
        *this = List<T>(RHS); 

    }
    return *this;
}

template <class T>
Node<T>* List<T>::InsertHead(T i){
    Node<T>* temp = new Node<T>(i);
    temp->_next = head;
    head = temp;
    return head;
}

// Clear a linked list
template <class T>
void List<T>::Clear(){
    Node<T>* current = head;
    Node<T>* next = new Node<T>;

    while (current != NULL) {
        next = current->_next;
        delete current;
        current = next;
    }

   head = NULL;
}

template <class U>
ostream& operator <<(ostream& outs, const List<U>& l){
    Node<U>* walker = l.head;

    while(walker != NULL){
        outs << *walker;
        outs << "->";
        walker = walker->_next;
    }

    outs << "|||";

    return outs;
}

Файл main.cpp: тесты классов

#include <iostream>
#include "list.h"
using namespace std;

int main() {
    List<int> a;

    a.InsertHead(17);
    a.InsertHead(35);
    a.InsertHead(6);
    a.InsertHead(54);
    a.InsertHead(6);
    cout << a <<endl;;

    List<int> b;
    b.InsertHead(3);
    b.InsertHead(2);
    cout << b <<endl;;

    a = b;

    cout << a <<endl;        // PROBLEM: NOTHING IS DISPLAYED
    cout << b <<endl;
}

Проблема, с которой я столкнулся в настоящее время, заключается в функции оператора перегрузки. Ниже показано, когда я копирую все выполнение из функции конструктора копирования, и она запускается.

template <class T>
List<T>& List<T>::operator =(const List<T>& RHS){
    if (this != &RHS) {
        this->Clear();
        if (copyThis.head == NULL)
            head = NULL;

        else {
            // Create walker for the original linked list
            Node<T>* walker = copyThis.head->_next;

            // Create new head node for new linked list
            head = new Node<T>(copyThis.head->_item);

            // Create new walker for new linked list
            Node<T>* new_walker = head;

            // Iterate walker and new walker and copy each item in the original list to new linked list
            while (walker!= NULL) {
                new_walker->_next = new Node<T>(walker->_item);
                walker = walker->_next;
                new_walker = new_walker->_next;
            }
    }
    return *this;
}

Выход для этого:

2->3->|||

Однако, когда я упрощаю код, как показано ниже, он ничего не выводит:

template <class T>
    List<T>& List<T>::operator =(const List<T>& RHS){
        if (this != &RHS) {
            this->Clear();
            *this = List<T>(RHS); 

        }
        return *this;
    }

Может кто-нибудь сказать мне, почему это не работает и как эффективно упростить это? Я действительно ценю это.

1 Ответ

0 голосов
/ 29 октября 2018

проблема

Оператор присваивания останавливает все из-за переполнения стека.

Фактически, ваша реализация оператора присваивания сама использует оператор присваивания, так что он вызывает себя рекурсивно, пока стек не будет исчерпан:

    *this       =    List<T>(RHS);  // OUCH !!
      |         |          |
      V         V          V
    <ListT> operator=   List<T>   ==> cals operator= again !!

Решение

  1. Переписать оператор так, чтобы он не вызывал сам себя.
  2. Возможно, клонировать каждый узел, поэтому во избежание того, что два списка совместно используют один и тот же узел, а первый освобождающий его узел вызывает висячие указатели и UB для другого.
  3. Не связано, но, пожалуйста, избегайте использования пространств имен в заголовках. Это очень плохая привычка на потом.

Дополнительный совет : Эта статья рекомендует некоторые хорошие и элегантные методы для перегрузки оператора. Для оператора присваивания он предлагает использовать конструктор копирования (как вы пытаетесь это сделать), но вместо присваивания (ваша проблема) замена (для проверки, но замена головок в вашем случае, безусловно, сделает свое дело).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...