Queue Implementation

This is my implementation of Queue using singly-linked list.

I've been testing this and I quite do not understand why the program crashes when I try using qualified swap in the driver.

For example, this crashes the program. Shouldn't it look for the customized swap and use that function to swap the two objects?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main()
{
    my::Queue<int> test1;

    test1.push( 4 );
    test1.push( 5 );
    test1.push( 6 );
    test1.push( 7 );

    my::Queue<int> test2;

    test2.push( 1 );
    test2.push( 2 );
    test2.push( 3 );
    test2.push( 4 );


    std::swap( test1 , test2 );
}



Implementation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
Queue Implementation - FIFO (First In, First Out)

Supported Operations
1. empty
2. size
3. front
4. back
5. push_back
6. pop_front
7. swap
*/

#ifndef QUEUE_H_INCLUDED
#define QUEUE_H_INCLUDED

#include <utility>
namespace my
{
    /*------------------------------------------------------------------------
                              CLASS DECLARATION
    ------------------------------------------------------------------------*/
    template<typename Type>
    class Queue
    {
    private:
        // Node Structure
        struct QueueNode
        {
            Type data;
            QueueNode* next;

            QueueNode( const Type &x ): data{ x } , next{ nullptr } {}
            QueueNode( Type &x , QueueNode* n ): data{ x } , next{ n } {}
        };

    public:

        // Default Constructor
        Queue();

        // Copy Constructor
        Queue( const Queue &rhs );

        // Move Constructor
        Queue( Queue &&rhs );

        // Copy/Move Assignment
        Queue & operator=( Queue rhs );

        // Destructor
        ~Queue();

        // Public Function to add an element
        void push( const Type &x );

        // Public Function to remove an element
        void pop();

        // Member-wise Swap Function
        Queue & swap( Queue &that );

        // Return theSize
        int size() const;

        // Test whether the container is empty
        bool empty() const;

        // Return modifiable reference to the first element
        Type & front();

        // Return modifiable reference to the last element
        Type & back();

    private:
        // Internal Function to clear all contents - Called by Destructor
        void clear();

        // Internal Function to add an element
        void push_back( const Type &x , QueueNode* t );

        // Internal Function to remove an element
        void pop_front( QueueNode* h );

        QueueNode* head;
        QueueNode* tail;
        int theSize;
    };

    /*------------------------------------------------------------------------
                              FUNCTION DEFINITION
    ------------------------------------------------------------------------*/
    // Default Constructor
    template<typename Type>
    Queue<Type>::Queue(): head{ nullptr } , tail{ nullptr } , theSize{ 0 }
    { }

    // Copy Constructor
    template<typename Type>
    Queue<Type>::Queue( const Queue &rhs )
    : head{ nullptr } , tail{ nullptr } , theSize{ 0 }
    {
        for( auto n = rhs.head ; n != nullptr ; n = n->next ) {
            push_back( n->data , tail );
        }
    }

    // Move Constructor
    template<typename Type>
    Queue<Type>::Queue( Queue &&rhs )
    {
        this->swap( rhs );
    }

    // Copy/Move Assignment
    template<typename Type>
    Queue<Type> & Queue<Type>::operator=( Queue rhs )
    {
        return this->swap( rhs );
    }

    // Destructor
    template<typename Type>
    Queue<Type>::~Queue()
    {
        clear();
    }

    // Public Function to add an element
    template<typename Type>
    void Queue<Type>::push( const Type &x )
    {
        push_back( x , tail );
    }

    // Internal Function to add an element
    template<typename Type>
    void Queue<Type>::push_back( const Type &x , QueueNode* t )
    {
        if( t == nullptr ) {
            tail = new QueueNode{ x };
            head = tail;
        }
        else {
            tail->next = new QueueNode{ x };
            tail = tail->next;
        }

        theSize ++;
    }

    // Public Function to remove an element
    template<typename Type>
    void Queue<Type>::pop()
    {
        pop_front( head );
    }

    // Internal Function to remove an element
    template<typename Type>
    void Queue<Type>::pop_front( QueueNode* h )
    {
        if( h == nullptr ) {
            ; // If the container is empty, do nothing
        }
        else if( h->next == nullptr ) {
            tail = nullptr;
            head = nullptr;
            delete h;
            theSize --;
        }
        else {
            head = h->next;
            delete h;
            theSize --;
        }
    }

    // Return modifiable reference to the front
    template<typename Type>
    Type & Queue<Type>::front()
    {
        return head->data;
    }

    // Return modifiable reference to the back
    template<typename Type>
    Type & Queue<Type>::back()
    {
        return tail->data;
    }

    // Return theSize
    template<typename Type>
    int Queue<Type>::size() const
    {
        return theSize;
    }

    // Test whether the container is empty
    template<typename Type>
    bool Queue<Type>::empty() const
    {
        return theSize == 0;
    }

    // Member-wise Swap Function
    template<typename Type>
    Queue<Type> & Queue<Type>::swap( Queue<Type> &that )
    {
        using std::swap;
        swap( head , that.head );
        swap( tail , that.tail );
        swap( theSize , that.theSize );

        return *this;
    }

    // Internal Function to delete all contents - Called by Destructor
    template<typename Type>
    void Queue<Type>::clear()
    {
        while( head != nullptr ) {
            pop_front( head );
        }
    }

    template<typename Type>
    void swap( Queue<Type> &lhs , Queue<Type> &rhs )
    {
        lhs.swap( rhs );
    }
}
#endif // QUEUE_H_INCLUDED
Last edited on
> I quite do not understand why the program crashes when I try using qualified swap

The move constructor does not initialise the members.
1
2
3
4
5
// Queue<Type>::Queue( Queue &&rhs ) 
Queue<Type>::Queue( Queue &&rhs ) : head { nullptr }, tail { nullptr }, theSize { 0 }
{
        this->swap( rhs );
}



> Shouldn't it look for the customized Tswap and use that function to swap the two objects?

To use the customised swap, use unqualified swap.

1
2
// std::swap( test1 , test2 );
using std::swap ; swap( test1 , test2 ); // canonical; ADL would find my::swap 
Thank you both for the replies.

@gentleguy
Queue::swap() is utilized in the move constructor and the assignment operator. This swap function is more efficient than the qualified swap and that's why I made it - http://www.cplusplus.com/forum/beginner/196459/. Also, std::queue offers a public swap() function - http://www.cplusplus.com/reference/queue/queue/swap/. The goal in this exercise was to provide similar operations as the Standard Template Library, so I made Queue::swap() a public function and allow users to call it and swap objects as they would with std::queue.

 
test1.swap( test2 );


One operation I did not implement here is the emplace function and I will probably do that later.
Did you find any problems with how the Queue::swap() is coded?


@JLBorges
That fixed the issue.

The funny thing is I made the same mistake with the copy constructor. After testing, I fixed the copy constructor and included the initialization list but totally forgot about the move constructor. Spent 3-4 hours trying to figure this out before posting here and now I feel like bashing my head against the monitor.

In the last thread, you said
To protect uninformed programmers who may write std::swap(a,b) ;
where they should have written using std::swap ; swap(a,b) ;.
If swap in namespace std is spcialised, the specialisation would call the custom swap.

So, for a minute, I thought using std::swap would still call the customized swap that I defined outside the class declaration. Now that I think about it.. I did not really specialize the std::swap, did I? I defined the non-member swap inside my namespace.


> The funny thing is I made the same mistake with the copy constructor.

Specifying in-class default member initialisers would mitigate this kind of error.

1
2
3
4
    private:
        QueueNode* head = nullptr ;
        QueueNode* tail = nullptr ;
        int theSize = 0 ;



> I did not really specialize the std::swap, did I?

No. To specialise std::swap for my::Queue
1
2
3
4
5
namespace std
{
    template< typename T > 
    void swap( my::Queue<T>& first, my::Queue<T>& second ) { first.swap(second) ; } 
}
Thanks again, JLBorges!
Topic archived. No new replies allowed.