简体   繁体   中英

Overloading assignment operator in Singly Linked List

I'm learning about linked list. I created a template implementation, with a constructor, an inserter, a destructor, a copy constructor and an overloaded assignment operator. The problem is that my test programme doesn't output anything after overloading assignment operator.

For my assignment operator, I use a Clear() function to clear the list entirely before the copy. I put it in the destructor and checked that it works fine. I also checked my copy constructor and it worked fine as well.

File node.h : defines the node building block

#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;
    }
};

File list.h : defines the linked list template

#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;
};

This header also provides the implementation of these member functions:

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;
}

File main.cpp : tests the classes

#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;
}

The problem I currently have is the overloading assignment operator function. Below is when I copy the whole execution from the copy constructor function and it runs.

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;
}

The output for this is:

2->3->|||

However, when I simplify the code like below, it doesn't output anything:

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

        }
        return *this;
    }

Can anyone tell me why it doesn't work and how to simplify it efficiently? I really appreciate it.

The problem

The assignment operator stops everything because of a stack overflow.

In fact, your implementation of the assignment operator uses itself the assignment operator, so that it calls itself recursively until the stack is exhausted:

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

The solution

  1. Rewrite the operator so that it doesn't call itself.
  2. Possibly clone every node, so to avoid that 2 lists share the same node and tha the first one that frees its node causes dangling pointers and UB for the other.
  3. Not related, but please avoid using namespaces in headers. This is an extremely bad habit for later.

Additional tip : This article recommends some good and elegant practices for operator overloading. For the assignment operator, it suggests to use the copy constructor (as you attempt to do) but instead of assigning (your problem), swapping (to be verified, but swapping the heads in your case would certainly do the trick).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM