Oct 26, 2019 at 12:46am Oct 26, 2019 at 12:46am UTC
In the Queue Class, I have head and rear pointer for the first and last node in the list.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
template <typename T>
class Queue
{
public :
//CTOR
Queue();
//Big Three
~Queue();
Queue(const Queue<T> ©This);
Queue& operator = (const Queue&RHS);
//other functions..
private :
int _size;
node<T>* head;
node<T>* rear;
Here is the problem, my assignment operator (=) works perfect, but the copy constructor doesn't work.
which means:
1 2 3 4 5
Queue <int > new_node (old_node); //doesn't work.
Queue <int > new_node;
new_node = old_node; //works.
Below is the code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
template <typename T>
Queue<T>::Queue(const Queue<T> ©This) //copy constructor
{
_size = copyThis._size;
this ->rear = _CopyList(copyThis.head,this ->head);
}
template <typename T>
Queue<T>& Queue<T>::operator = (const Queue&RHS) //assignment operator
{
_size = RHS._size;
this ->rear = _CopyList(RHS.head,this ->head);
return *this ;
}
The _CopyList function is passed by reference,so it will update the head.
Just in case, here is the _CopyList function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
template <typename T>
node<T>* _CopyList(node<T>* old_head,node<T>* &new_head) //return the last node of the destination
{
_insert_head(new_head,old_head->_item); //copy the old head to new head
node<T>* walker = new_head; //walker for the new head
old_head = old_head->_next; //starts from the node after head
while (old_head!= nullptr ) //insert after each node
{
_insert_after(walker,old_head->_item);
old_head = old_head->_next;
walker = walker->_next;
}
return walker; //return the last node of the destination
}
I'm so confused why only the copy constructor doesn't work, what could be the problem? Thanks!!!
Last edited on Oct 27, 2019 at 4:15am Oct 27, 2019 at 4:15am UTC
Oct 26, 2019 at 10:21am Oct 26, 2019 at 10:21am UTC
_CopyList_h
is not _CopyList
Nevertheless, I don't understand how the assignment could possibly work. You don't handle the existing nodes.
Oct 27, 2019 at 4:16am Oct 27, 2019 at 4:16am UTC
Sorry.. the wrong function, I edit to the right function now.
Oct 27, 2019 at 6:13am Oct 27, 2019 at 6:13am UTC
We can write the destructor, copy and move constructors/assignment operators in terms of
push() and
pop()
This is not the most efficient way of writing these foundation operators;
this ought to be fine here (a linked list implementation of the queue is not the most efficient implementation).
For example:
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
#include <iostream>
#include <cassert>
template < typename T > struct queue
{
queue() = default ;
~queue() { assert( valid() ) ; while ( !empty() ) pop() ; }
queue( const queue& that ) // note: first and last are initialised (nullptr)
{
assert( that.valid() ) ;
for ( const node* n = that.first ; n != nullptr ; n = n->next ) push(n->value) ;
assert( valid() ) ;
}
// move constructor: you may want to ignore this for now
queue( queue&& that ) noexcept // note: first and last are initialised (nullptr)
{
assert( that.valid() ) ;
using std::swap ;
swap( first, that.first ) ;
swap( last, that.last ) ;
assert( valid() ) ;
}
queue& operator = ( const queue& that )
{
// EDIT: added check for self-assignment
if ( first == that.first ) return *this ;
assert( that.valid() ) ;
while ( !empty() ) pop() ;
for ( const node* n = that.first ; n != nullptr ; n = n->next ) push(n->value) ;
assert( valid() ) ;
return *this ;
}
// move assignment operator: you may want to ignore this for now
queue& operator = ( queue&& that ) noexcept
{
assert( that.valid() ) ;
using std::swap ;
swap( first, that.first ) ;
swap( last, that.last ) ;
assert( valid() ) ;
return *this ;
}
bool empty() const noexcept { return first == nullptr ; }
T& top() { if ( empty() ) throw "queue is empty" ; return first->value ; }
const T& top() const { if ( empty() ) throw "queue is empty" ; return first->value ; }
void push( const T& v )
{
if ( empty() ) first = last = new node {v} ;
else
{
last->next = new node {v} ;
last = last->next ;
}
assert( valid() ) ;
}
void pop()
{
if ( empty() ) throw "queue is empty" ;
const node* old_first = first ;
first = first->next ;
delete old_first ;
if ( first == nullptr ) last = nullptr ;
assert( valid() ) ;
}
void debug_dump() const
{
#ifndef NDEBUG
assert( valid() ) ;
if ( !empty() )
{
std::cout << "top == " << top() << " : " ;
for ( const node* n = first ; n != nullptr ; n = n->next )
std::cout << n->value << " => " ;
}
std::cout << "nullptr\n" ;
#endif // NDEBUG
}
private :
struct node { T value ; node* next = nullptr ; };
node* first = nullptr ;
node* last = nullptr ;
bool valid() const
{
if ( first == nullptr ) return last == nullptr ;
return last->next == nullptr ;
}
};
int main()
{
queue<std::string> q ;
q.debug_dump() ;
for ( std::string str : { "abc" , "def" , "ghi" , "jkl" , "mno" , "pqr" } )
{
std::cout << "\npush " << str << '\n' ;
q.push(str) ;
q.debug_dump() ;
}
std::cout << "\n----------------\n" ;
q.debug_dump();
const queue another = q ; // copy construct
another.debug_dump() ;
std::cout << "----------------\n" ;
while ( !q.empty() )
{
std::cout << "\npop\n" ;
q.pop() ;
q.debug_dump() ;
}
std::cout << "\n----------------\n" ;
another.debug_dump() ;
q = another ; // assign
q.debug_dump();
}
http://coliru.stacked-crooked.com/a/8ec3fffa4efdbe1b
Last edited on Oct 27, 2019 at 10:28am Oct 27, 2019 at 10:28am UTC
Oct 27, 2019 at 10:29am Oct 27, 2019 at 10:29am UTC
Thanks! I've added a check for self-assignment now.
Oct 27, 2019 at 11:45am Oct 27, 2019 at 11:45am UTC
@robbinn :
Can you spot differences in the logic between JLBorges' push()
and your _CopyList(), _insert_head(), _insert_after()?
(Yours is obviously different, but where in there do the fatal errors lurk?
Oct 29, 2019 at 6:45am Oct 29, 2019 at 6:45am UTC
@JLBorges @keskiverto Thank you soooo much! Even though the logic is different, I can see you are using a better way to solve this. I like your empty() function so much, that way I don't have to write while(walker!=nullptr) anymore.
btw: The for loop in the copy constructor is very interesting :)