Search code examples
c++copy-constructordoubly-linked-listclass-designmove-constructor

Move and copy constructors in a linked list


I am still trying to learn more about copy and move constructors. I have a linked list class that I want to deep copy using copy and move constructors but I'm having issues. First, to deep copy List class, do I only copy head_ and tail_ in the constructors. I know the code is horrendous, maybe I shouldn't jump into high-level stuff right away.

Any help is appreciated!

template<typename T>
class List
{
public:

    class Node {
    public:
        Node(T value) : value_(value) {}

        T value_;
        Node* next_;
        Node* prev_;
    };

    Node* head_;
    Node* tail_;


    //! Default constructor
    List() :tail_(nullptr) {}

    //! Copy constructor
    List(const List& lst) : head_(nullptr) {

        //not sure what goes in here
        }
    }

    //! Move constructor
    List(List&& move) {
        head_ = move.head_;
        move.head_ = nullptr;
        tail_ = move.tail_;
        move.tail_ = nullptr;
    }


//! Copy assignment operator
    List& operator= (const List& list) {
        
        tail_ = nullptr;
        head_ = tail_;
    
        Node* current = list.head_;
        Node* next = list.head_->next_;
        Node* replace = head_;
        while (next != list.tail_) {
            current = current->next_;
            next = next->next_;
            replace->next_ = tail_;
            replace->next_->value_;
            replace = replace->next_;
        }
        return *this;
    }
    

    //! Move assignment operator
    List& operator= (List&& other) {        
        
        tail_ = nullptr;
        head_ = tail_;

        head_->next_ = other.head_->next_;
        Node* current = other.head_;
        Node* next = other.head_->next_;

        while (next != other.tail_) {

            current = current->next_;
            next = next->next_;
        }
        current->next_ = tail_;
        other.head_->next_ = other.tail_;

        return *this;
    }


Solution

  • Here is my five cents.:)

    The demonstrative program below shows how the copy constructor, move constructor, copy assignment operator, move assignment operator, and the destructor can be implemented including some other auxiliary functions.

    #include <iostream>
    #include <utility>
    #include <functional>
    #include <iterator>
    
    template<typename T>
    class List
    {
    private:
        struct Node 
        {
            T value;
            Node *prev;
            Node *next;
        } *head = nullptr, *tail = nullptr;
    
        void copy( const List &list )
        {
            if ( list.head )
            {
                head = tail = new Node { list.head->value, nullptr, nullptr };
    
                for ( Node *current = list.head->next; current; current = current->next )
                {
                    tail = tail->next = new Node { current->value, tail, nullptr };             
                }
            }           
        }
        
    public:
        //! Default constructor
        List() = default;
    
        //! Copy constructor
        List( const List &list )
        {
            copy( list );
        }
    
        //  Constructor with iterators
        template <typename InputIterator>
        List( InputIterator first, InputIterator last )
        {
            if ( first != last )
            {
                head = tail = new Node { *first, nullptr, nullptr };
    
                while ( ++first != last )
                {
                    tail = tail->next = new Node { *first, tail, nullptr };             
                }
            }
        }
    
        //  Destructor
        ~List()
        {
            clear();
        }
        
        //! Move constructor
        List( List &&list ) 
        {
            std::swap( head, list.head );
            std::swap( tail, list.tail );
        }
    
        //! Copy assignment operator
        List & operator =( const List &list ) 
        {
            clear();
            copy( list );
            
            return *this;
        }
        
        //! Move assignment operator
        List & operator =( List &&list ) 
        {
            std::swap( head, list.head );
            std::swap( tail, list.tail );
            
            return *this;
        }
        
        void clear()
        {
            while ( head )
            {
                delete std::exchange( head, head->next );
            }
            
            tail = head;
        }
        
        void push_front( const T &value )
        {
            head = new Node{ value, nullptr, head };
    
            if ( !tail )
            {
                tail = head;
            }
            else
            {
                head->next->prev = head;
            }
        }
    
        void push_back( const T &value )
        {
            Node *new_node = new Node{ value, tail, nullptr };
    
            if ( tail )
            {
                tail = tail->next = new_node;
            }
            else
            {
                head = tail = new_node;
            }
        }
        
        friend std::ostream & operator <<( std::ostream &os, const List &list )
        {
            for ( Node *current = list.head; current; current = current->next )
            {
                os << current->value << " -> ";
            }
            
            return os << "null";
        }
    };
    
    int main()
    {
        int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        
        List<int> list1( std::begin( a ), std::end( a ) );
        
        std::cout << list1 << '\n';
        
        list1 = List<int>( std::rbegin( a ), std::rend( a ) );
    
        std::cout << list1 << '\n';
        
    } 
    

    The program output is

    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
    

    For example in this statement

    list1 = List<int>( std::rbegin( a ), std::rend( a ) );
    

    there is used the move assignment operator.