I have already created a BST class, with a root node, and left and right node. It is working perfectly. I am trying to create a ++ operator Iterator, which can just go through each node and increment its. This is what I get so far, I am still thinking it gets something to do with my constructor. Below is just the nested Iterator class that I included in the BST class. I just cout to see if it is working but it keeps printing out 0.
class Iterator
{
private:
// private iterator state...
nodeptr root;
public:
Iterator(nodeptr roots_) : root(roots_) {};
~Iterator() {}
bool operator!=(const Iterator& rhs) const { return (this-> root != rhs.root); }
bool operator==(const Iterator& rhs) const {
return (this->root == rhs.root);
}
Iterator operator++(T) {
nodeptr ptr = root;
if (root == NULL)
{
cout << "The tree is empty" << endl;
}
else
{
if (ptr->left != NULL)
{
ptr = ptr->left;
}
cout << ptr->data << " ";
if (ptr->right != NULL)
{
ptr = ptr->right;
}
else
{
cout << "_";
}
}
return *this;
}
}
I don't know what a iterator should do in a BST class, anyway...
(1) I don't know what is a operator++ (T); as far I know, as method of a class, operator++ can be with signature operator++() (pre-increment) or operator++(int) (post-increment); I suppose you're trying to define a post-increment operator (because you add a dummy argument and because you returning a copy of Iterator, not a reference) but the dummy argument should be of type int, not of type T
(2) pre-increment or post-increment, the operator++() should modify the object; your operator doesn't modify it: it create a local copy of the lonely member (root) and modify the local copy; sequential calls to your operator++(T) should give the same effect because are doing the same thing; I suppose your intention was to modify the member root, instead of the local variable ptr
(3) supposing (presence of the dummy argument and no reference in return type) your intention was the make a post-increment operator, you have to create a copy of the object, modify the object and return the copy; you're (not) modifying the object and returning the object itself (return *this;) and this is the typical behavior (adding some change in object and return by reference) of a pre-increment operator.
To make it simple, I suppose you should write something as
// v <--- observe the return by reference
Iterator & operator++ ()
{
// something that modify the Iterator object
// return the object itself as reference
return *this;
}
// no reference here: return a copy
Iterator operator++ (int)
{
// copy of the object **before** the increment
// (but you have also to develop a copy constructor)
Iterator copy { *this };
// pre-increment of the object
++(*this);
// return of the before the increment copy
return copy;
}
Related
i implemented a custom List including an iterator using the example from a textbook, but when i try to iterate over the list using the iterator, i get the error:
Error C2675 unary '++': 'List::iterator' does not define this
operator or a conversion to a type acceptable to the predefined
operator
does anyone know what's wrong with my class? i'm pretty sure i copied it verbatim from the textbook. thanks!
/*
list class itself - contains links to both ends, size, and many other methods
concrete implementation of List ADT. different from vector because it allows
us to add to middle of list in O(1) time, but does not support O(1) indexing[]
*/
template <typename Object>
class List {
private:
/*
private nested Node class. contains data and pointers to prev/next
along with appropriate constructor methods
struct is a relic from C, essentially a class where members default to public
used to signify a type that contains mostly data to be accessed directly rather
than through methods
Node is private, so only List will be able to access it's fields/methods
*/
struct Node {
Object data;
Node* prev;
Node* next;
Node(const Object& d = Object{}, Node* p = nullptr, Node* n = nullptr) :
data{ d }, prev{ p }, next{ n } {}
Node(Object&& d, Node* p = nullptr, Node* n = nullptr) :
data{ std::move(d) }, prev{ p }, next{ n }
{}
};
public:
/*
const_iterator class. abstracts notion of position, public nested class
const_iterator stores pointer to current node and provides implementation of
basic iterator operations, all in form of overloaded operators like =, ==, !=, ++
*/
class const_iterator {
public:
const_iterator():current{nullptr}{}
const Object& operator*()const
{
return retrieve();
}
const_iterator& operator++()
{
current = current->next;
return *this;
}
const_iterator operator++(int)
{
const_iterator old = *this;
++(*this);
return old;
}
/*
public methods of const_iterator all use operator overloading. operator==,
operator!=, operator* are straightforward. separate routines are necessary for
prefix and postfix versions of operator++ due to their different semantics. we
distinguish between them by their method signatures (empty parameter for prefix,
int parameter for postfix). int is only used to distinguish between them.
in many cases where there is a choice between using prefix or postfix, prefix
version is faster.
*/
bool operator==(const const_iterator& rhs)const
{
return current == rhs.current;
}
bool operator!=(const const_iterator& rhs)const
{
return !(*this == rhs);
}
/*
protected allows classes that inherit from const_iterator to access these fields
but not other classes
*/
protected:
Node* current;
Object& retrieve()const
{
return current->data;
}
const_iterator(Node* p) : current{ p }
{
}
/*
the friend declaration is needed to grant the List class access to const_iterators
nonpublic members
*/
friend class List<Object>;
};
/*
iterator class. abstracts notion of position. public nested class
same functionality as const_iterator, except operator* returns a reference
to the item being viewed, rather than a const reference
iterator can be used in any routine that requires a const_iterator, but not
vice-versa (in other words, iterator IS A const_iterator)
iterator inherits from const_iterator, meaning it inherits all the data and
methods from const_iterator. it can then add new methods and override existing
methods. here we are not adding any new data or changing the behavior of exising
methods. we do add some new methods (with similar signatures to const_iterator)
*/
class iterator : public const_iterator
//inheritance: iterator has same functionality as const_iterator
//iterator can be used wherever const_iterator is needed
{
public:
iterator() {}
/*
do not have to re-implement operator == and operator != (inherited unchanged)
provide a new pair of operator++ implementations that override the original
implementations from const_iterator.
provide an accessor/mutator pair for operator*.
*/
Object& operator*()
{
return const_iterator::retrieve();
}
const Object& operator*()const
{
return const_iterator::operator*();
}
iterator& operator++(int)
{
iterator old = *this;
++(*this);
return old;
}
protected:
/*
protected constructor uses an initialization list to initialize the inherited
current node.
*/
iterator(Node* p) :const_iterator{ p } {}
friend class List<Object>;
};
public:
/*
constructor and big 5. because zero-parameter constructor and copy constructor
must both allocate the header and tail nodes, we provide a init routine.
init creates an empty list.
the destructor reclaims the header and tail nodes, all other nodes, all other
nodes are reclaimed when the destructor invokes clear. similarly, the
copy-constructor is implemented by invoking public methods rather than attempting
low-level pointer manipulations.
*/
List()
{
init();
}
/*
sentinel nodes - makes sense to create an extra node at each end of list to
represent start/end markers. also referred to as header and tail nodes
advantage of sentinel nodes is that it simplifies code by removing special cases
example: without sentinels, removing the first node becomes a special case
because we must reset the list's link to the first node during the remove
operation, and because the remove algorithm in general needs to access the node
prior to the node being removed (and without header, first node does not have
prior)
*/
void init()
{
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
~List()
{
clear();
delete head;
delete tail;
}
// clear works by repeatedly removing items until the List is empty (uses pop-front)
void clear()
{
while (!empty())
pop_front();
}
List(const List& rhs)
{
init();
for (auto& x : rhs)
{
push_back(x);
}
}
List& operator=(const List& rhs)
{
List copy = rhs;
std::swap(*this, copy);
return *this;
}
List(List&& rhs)
:theSize{ rhs.theSize }, head{ rhs.head }, tail{ rhs.tail }
{
rhs.theSize = 0;
rhs.head = nullptr;
rhs.tail = nullptr;
}
List& operator=(List&& rhs)
{
std::swap(theSize, rhs.theSize);
std::swap(head, rhs.head);
std::swap(tail, rhs.tail);
return *this;
}
// these methods return iterators
iterator begin()
{
return { head->next };
}
const_iterator begin()const
{
return{ head->next };
}
iterator end()
{
return { tail };
}
const_iterator end()const
{
return{ tail };
}
int size()const
{
return theSize;
}
bool empty()const
{
return size() == 0;
}
Object& front()
{
return *begin();
}
const Object& front()const
{
return *begin();
}
Object& back()
{
return *--end();
}
const Object& back() const
{
return *--end();
}
/*
cleverly obtain and use appropriate iterator
insert inserts prior to a position, so push_back inserts prior to the endmarker
pop_back line erase(-end()) creates a temporary iterator corresponding to the
endmarker, retreats the temporary iterator, and uses that iterator to erase.
similar behavior occurs in back
note also that we avoid dealing with node reclamation in pop_front and pop_back
*/
void push_front(const Object& x)
{
insert(begin(), x);
}
void push_front(Object&& x)
{
insert(begin(), std::move(x));
}
void push_back(const Object& x)
{
insert(end(), x);
}
void push_back(Object&& x)
{
insert(end(), std::move(x));
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
/*
inserting a new node between p and p->prev. works by getting a new node
and then changing pointers of p and p-prev in the correct order
also mention usefulness of the sentinels here.
*/
iterator insert(iterator itr, const Object& x)
{
Node* p = itr.current;
theSize++;
Node* newNode = new Node{ x, p->prev,p };
p->prev->next = newNode;
p->prev = newNode;
return newNode;
}
iterator insert(iterator itr, Object&& x)
{
Node* p = itr.current;
theSize++;
p->prev->next = new Node{ std::move(x), p->prev, p };
p->prev = p->prev->next;
return p->prev;
}
/*
erase routines. frst version erases p by rearranging pointers of the nodes just
before and after p, and then returning an iterator representing the item after the
erased element. like insert, erase must also update the size.
second version of erase simply uses an iterator to call the first version of erase,
note - cannot simply use itr++ in 'for' loop and ignore return iterator from erase,
because the value of itr is stale immediately after the call to erase, which is why
erase returns an iterator.
*/
iterator erase(iterator itr)
{
Node* p = itr.current;
iterator retVal{ p->next };
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
theSize--;
return retVal;
}
iterator erase(iterator from, iterator to)
{
for (iterator itr = from; itr != to;)
{
itr = erase(itr);
}
return to;
}
/*
data members for list - pointers to header and tail nodes. also keeps track of
size in a data member so that the size method can be implemented in constant time
*/
private:
int theSize;
Node* head;
Node* tail;
};
int main()
{
List<int> theList{};
for (int i = 0; i < 10; i++)
theList.push_back(i);
List<int>::iterator iter = theList.begin();
for(int i=0;i<5;i++)
std::cout << *(iter++) << std::endl;
}
I am trying to understand move semantics and in particular how std::move() works.
I understood that it's basically a static-cast to an rvalue reference type, but this exercise gets me confused. I have implemented a Node class following the Rule of Five (I know I could have followed the copy and swap idiom for a better implementation).
class Node
{
public:
Node(char data = ' ', Node *left_child = NULL, Node *right_child = NULL) : _data(data), _left_child(left_child), _right_child(right_child)
{
std::cout << "NODE CONSTRUCTOR" << std::endl;
}
Node(const Node& node);
Node(Node&& node) noexcept;
GetData() const;
Node& operator=(const Node &n);
Node& operator=(Node&& node) noexcept;
~Node();
protected:
Node *_left_child;
Node *_right_child;
char _data;
};
char Node::GetData() const
{
return _data;
}
Node::Node(const Node& node)
{
...
}
Node::Node(Node&& node) noexcept
{
std::cout << "MOVE CONSTRUCTOR" << std::endl;
this->_data = node.GetData();
this->_left_child = node._left_child;
this->_right_child = node._right_child;
node._right_child = nullptr;
node._left_child = nullptr;
}
Node& Node::operator=(Node&& node) noexcept
{
std::cout << "MOVE ASSIGNMENT OPERATOR " << std::endl;
if(&node != this)
{
if(this->_right_child != nullptr)
{
delete this->_right_child;
}
if(this->_left_child != nullptr)
{
delete this->_left_child ;
}
}
this->_data = node.GetData();
this->_left_child = node._left_child;
this->_right_child = node._right_child;
node._right_child = nullptr;
node._left_child = nullptr;
return *this;
}
Node::~Node()
{
delete _left_child;
delete _right_child;
}
Node& Node::operator=(const Node &n)
{
...
}
Then in my main() function:
int main() {
Node *NodeOne = new Node{};
Node NodeTwo{};
std::stack<Node*> stack_of_nodes_ptr;
stack_of_nodes_ptr.push(std::move(NodeOne));
delete stack_of_nodes_ptr.top();
stack_of_nodes_ptr.pop();
std::stack<Node> stack_of_nodes;
stack_of_nodes.push(std::move(NodeTwo));
return 0;
}
The output is:
NODE CONSTRUCTOR
NODE CONSTRUCTOR
CALLING NODE BASE DESTRUCTOR
MOVE CONSTRUCTOR
CALLING NODE BASE DESTRUCTOR
CALLING NODE BASE DESTRUCTOR
My doubt arises seeing that the move constructor is not called in the first push_back() but only in the second one. The only difference here is that the first stack is of Node* pointers while the other one is a stack of Node objects.
Could you please tell me why, in case of raw pointer, move constructor is not called?
Could you please tell me why in case of raw pointer std::move is not called?
std::move is called. But std::move doesn't print anything. Remember that std::move is:
basically a static cast to an rvalue reference type
Calling std::move never causes any output.
My doubt arises seeing that the move constructor is not called in the first push_back
You didn't move an object of type Node, so the move constructor of Node was not called.
For all fundamental types, moving is same as copying. Pointers are fundamental types.
I am trying to build a N-ary tree custom iterator. The nodes in the tree (MoveTreeNode*) have member values MoveTreeNode* m_parent to go to the previous node, and vector <MoveTreeNode*> m_children to go to the next nodes.
I want my iterator's postfix operator to take the parameter value int i which represents the index where the iterated node should descend in its m_children vector.
The key of the node is an object of type Move.
MoveTreeNode.hpp
#pragma once
#include "Move.hpp"
using namespace std;
class MoveTreeNode {
public:
MoveTreeNode(Move&);
Move& m_move; // Key
MoveTreeNode* m_parent; // To go to previous move
vector<MoveTreeNode*> m_children; // To to go to next move
};
MoveTree.hpp
#pragma once
#include "Move.hpp"
#include "MoveTreeNode.hpp"
using namespace std;
class MoveTree {
public:
MoveTreeNode* m_root = nullptr; // Root of the tree
// ( ... some member functions here)
struct Iterator {
using iterator_category = bidirectional_iterator_tag;
using difference_type = ptrdiff_t;
Iterator(MoveTreeNode* ptr): m_node(ptr) {};
MoveTreeNode& operator*() const { return *m_node; }
MoveTreeNode* operator->() { return m_node; }
// Prefix increment
// Iterator& operator++(int i) {
// m_node= m_node->m_children.at(i); return *this;
// }
// ^^^^^^error : cannot overload functions distinguished by return type alone
// Postfix increment
Iterator operator++(int i) {
Iterator tmp = m_node; m_node= m_node->m_children.at(i); return tmp;
}
// Prefix decrement
Iterator& operator--() { m_node = m_node->m_parent; return *this; }
// Postfix decrement
Iterator operator--(int) {
Iterator tmp = m_node; m_node = m_node->m_parent; return tmp;
}
friend bool operator== (const Iterator& a, const Iterator& b) { return a.m_node== b.m_node; };
friend bool operator!= (const Iterator& a, const Iterator& b) { return a.m_node!= b.m_node; };
private:
MoveTreeNode* m_node;
};
Iterator begin() { return Iterator(m_root); }
Iterator end() { return Iterator(nullptr); }
};
Issues encountered
Is it possible to have the increment operator overloaded such that it takes in a parameter, in this case an integer representing an index ?
I am not sure whether both prefix and postfix operators have to be specified? If I never use the prefix operator can I just define the postfix operator ? Because in the above snippet of code if I uncomment the prefix definition then I get the compilation error cannot overload functions distinguished by return type alone and I am not sure how to respond to that.
It's my first time trying to build a custom iterator, is my above struct correct, or is there something I am missing and should modify ?
I have the following class:
class list {
private:
struct node {
node() { data=T(); prev=next=this; }
˜node() {}
T data;
node *prev;
node *next;
};
public:
class iterator {
public:
iterator() : p(NULL) {}
T & operator*() { return p->data; }
iterator & operator++()
{ p = p->next; return *this; }
iterator & operator++(int)
{ iterator tmp = *this; ++*this; return *tmp; }
bool operator==(const iterator & rhs) const
{ return p == rhs.p; }
bool operator!=(const iterator & rhs) const
{ return p != rhs.p; }
private:
friend class list<T>;
iterator(node *p) : p(p) {}
node *p;
};
iterator begin() { return iterator(head->next); }
iterator end() { return iterator(head); }
list();
˜list();
etc ...
private:
int N;
node *head;
node *findnode(int);
};
I see that the begin() function returns a constructor for the iterator class. Does this mean that a new iterator is created when it is called? If so, will the memory be recycled after the variable that this iterator is assigned to goes out of scope? I'm slightly confused, for the iterator constructor has no return type. If someone could shed some light on my issues, I would be most appreciative.
Yes a new iterator is created when begin is called. At a high level, yes the memory the iterator occupies will be recycled after it goes out of scope.
Constructors do not have return types as they are called in place on the memory they are to initialize.
At a somewhat lower level, variables that are declared on the stack have their destructor called when they go out of scope, which is one aspect of "recycling". The memory they occupy is on the stack rather than in the heap (as it would be if new were called), so it doesn't get freed in the sense of doing a delete or garbage collection. Rather the memory the variable had occupied may sit idle for a while or be immediately overwritten depending on if stack frames are being removed or added immediately after it goes out of scope (i.e the functions are returning up the stack or new calls are being made).
ALright, pardon my messy code please. Below is my queue class.
#include <iostream>
using namespace std;
#ifndef QUEUE
#define QUEUE
/*----------------------------------------------------------------------------
Student Class
# Methods #
Student() // default constructor
Student(string, int) // constructor
display() // out puts a student
# Data Members #
Name // string name
Id // int id
----------------------------------------------------------------------------*/
class Student {
public:
Student() { }
Student(string iname, int iid) {
name = iname;
id = iid;
}
void display(ostream &out) const {
out << "Student Name: " << name << "\tStudent Id: " << id
<< "\tAddress: " << this << endl;
}
private:
string name;
int id;
};
// define a typedef of a pointer to a student.
typedef Student * StudentPointer;
template <typename T>
class Queue
{
public:
/*------------------------------------------------------------------------
Queue Default Constructor
Preconditions: none
Postconditions: assigns default values for front and back to 0
description: constructs a default empty Queue.
------------------------------------------------------------------------*/
Queue() : myFront(0), myBack(0) {}
/*------------------------------------------------------------------------
Copy Constructor
Preconditions: requres a reference to a value for which you are copying
Postconditions: assigns a copy to the parent Queue.
description: Copys a queue and assigns it to the parent Queue.
------------------------------------------------------------------------*/
Queue(const T & q) {
myFront = myBack = 0;
if(!q.empty()) {
// copy the first node
myFront = myBack = new Node(q.front());
NodePointer qPtr = q.myFront->next;
while(qPtr != NULL) {
myBack->next = new Node(qPtr->data);
myBack = myBack->next;
qPtr = qPtr->next;
}
}
}
/*------------------------------------------------------------------------
Destructor
Preconditions: none
Postconditions: deallocates the dynamic memory for the Queue
description: deletes the memory stored for a Queue.
------------------------------------------------------------------------*/
~Queue() {
NodePointer prev = myFront, ptr;
while(prev != NULL) {
ptr = prev->next;
delete prev;
prev = ptr;
}
}
/*------------------------------------------------------------------------
Empty()
Preconditions: none
Postconditions: returns a boolean value.
description: returns true/false based on if the queue is empty or full.
------------------------------------------------------------------------*/
bool empty() const {
return (myFront == NULL);
}
/*------------------------------------------------------------------------
Enqueue
Preconditions: requires a constant reference
Postconditions: allocates memory and appends a value at the end of a queue
description:
------------------------------------------------------------------------*/
void enqueue(const T & value) {
NodePointer newNodePtr = new Node(value);
if(empty()) {
myFront = myBack = newNodePtr;
newNodePtr->next = NULL;
} else {
myBack->next = newNodePtr;
myBack = newNodePtr;
newNodePtr->next = NULL;
}
}
/*------------------------------------------------------------------------
Display
Preconditions: requires a reference of type ostream
Postconditions: returns the ostream value (for chaining)
description: outputs the contents of a queue.
------------------------------------------------------------------------*/
void display(ostream & out) const {
NodePointer ptr;
ptr = myFront;
while(ptr != NULL) {
out << ptr->data << " ";
ptr = ptr->next;
}
out << endl;
}
/*------------------------------------------------------------------------
Front
Preconditions: none
Postconditions: returns a value of type T
description: returns the first value in the parent Queue.
------------------------------------------------------------------------*/
T front() const {
if ( !empty() )
return (myFront->data);
else
{
cerr << "*** Queue is empty -- returning garbage value ***\n";
T * temp = new(T);
T garbage = * temp;
delete temp;
return garbage;
}
}
/*------------------------------------------------------------------------
Dequeue
Preconditions: none
Postconditions: removes the first value in a queue
------------------------------------------------------------------------*/
void dequeue() {
if ( !empty() ) {
NodePointer ptr = myFront;
myFront = myFront->next;
delete ptr;
if(myFront == NULL)
myBack = NULL;
} else {
cerr << "*** Queue is empty -- "
"can't remove a value ***\n";
exit(1);
}
}
/*------------------------------------------------------------------------
pverloaded = operator
Preconditions: requires a constant reference
Postconditions: returns a const type T
description: this allows assigning of queues to queues
------------------------------------------------------------------------*/
Queue<T> & operator=(const T &q) {
// make sure we arent reassigning ourself
// e.g. thisQueue = thisQueue.
if(this != &q) {
this->~Queue();
if(q.empty()) {
myFront = myBack = NULL;
} else {
myFront = myBack = new Node(q.front());
NodePointer qPtr = q.myFront->next;
while(qPtr != NULL) {
myBack->next = new Node(qPtr->data);
myBack = myBack->next;
qPtr = qPtr->next;
}
}
}
return *this;
}
private:
class Node {
public:
T data;
Node * next;
Node(T value, Node * first = 0) : data(value),
next(first) {}
};
typedef Node * NodePointer;
NodePointer myFront,
myBack,
queueSize;
};
/*------------------------------------------------------------------------
join
Preconditions: requires 2 queue values
Postconditions: appends queue2 to the end of queue1
description: this function joins 2 queues into 1.
------------------------------------------------------------------------*/
template <typename T>
Queue<T> join(Queue<T> q1, Queue<T> q2) {
Queue<T> q1Copy(q1), q2Copy(q2);
Queue<T> jQueue;
while(!q1Copy.empty()) {
jQueue.enqueue(q1Copy.front());
q1Copy.dequeue();
}
while(!q2Copy.empty()) {
jQueue.enqueue(q2Copy.front());
q2Copy.dequeue();
}
cout << jQueue << endl;
return jQueue;
}
/*----------------------------------------------------------------------------
Overloaded << operator
Preconditions: requires a constant reference and a Queue of type T
Postconditions: returns the ostream (for chaining)
description: this function is overloaded for outputing a queue with <<
----------------------------------------------------------------------------*/
template <typename T>
ostream & operator<<(ostream &out, Queue<T> &s) {
s.display(out);
return out;
}
/*----------------------------------------------------------------------------
Overloaded << operator
Preconditions: requires a constant reference and a reference of type Student
Postconditions: none
description: this function is overloaded for outputing an object of type
Student.
----------------------------------------------------------------------------*/
ostream & operator<<(ostream &out, Student &s) {
s.display(out);
}
/*----------------------------------------------------------------------------
Overloaded << operator
Preconditions: requires a constant reference and a reference of a pointer to
a Student object.
Postconditions: none
description: this function is overloaded for outputing pointers to Students
----------------------------------------------------------------------------*/
ostream & operator<<(ostream &out, StudentPointer &s) {
s->display(out);
}
#endif
Now I'm having some issues with it. For one, when I add 0 to a queue and then I output the queue like so..
Queue<double> qdub;
qdub.enqueue(0);
cout << qdub << endl;
That works, it will output 0. But for example, if I modify that queue in any way.. like.. assign it to a different queue..
Queue<double> qdub1;
Queue<double> qdub2;
qdub1.enqueue(0;
qdub2 = qdub1;
cout << qdub2 << endl;
It will give me weird values for 0 like.. 7.86914e-316.
Help on this would be much appreciated!
Your haven't defined a copy constructor or an assignment operator. The ones you have take an instance of the queued type, not another queue. For assigning and copying queues themselves, the compiler will still use the automatically generated ones which do the wrong thing.
(This probably doesn't explain the output of that particular snippet.)
Another thing that is completely wrong (even though, again, the snippet never invokes this function or you'd get compiler errors all over the place):
Queue<T> & operator=(const T &q) {
// make sure we arent reassigning ourself
// e.g. thisQueue = thisQueue.
if(this != &q) {
this->~Queue();
Calling destructor explicitly like that, and then going on to use the instance is not allowed. Explicit destructor calls only go hand in hand with constructing objects with placement new.
operator= is normally implemented in terms of copy constructor and a swap method (which swaps the internal representation between two instances):
void swap(Queue<T>& rhv)
{
std::swap(myFront, rhv.myFront);
std::swap(myBack, rhv.myBack);
std::swap(queueSize, rhv.queueSize);
}
Queue<T>& operator=(const Queue<T>& rhv)
{
Queue<T> copy(rhv);
this->swap(copy);
} //the destructor of copy releases the previous contents of *this
I don't see an assignment operator there, which means you're getting the compiler generated default, which will do a shallow copy. You probably need to supply your own to do a deep copy instead. What your comments call a copy ctor isn't really a copy ctor either. A copy ctor always takes a const reference to the object being copied, so the signature would be:
Queue(Queue const &original);.
You need a proper assignment operator. Your example would probably not compile the way you provided your class.
Even if I am wrong, the main mistake in your code is that your operator= calls it's own destructor. This is horribly wrong. The destructor will later called 'naturally' on as well. This means that your objects will be deleted twice. (Because you don't assign NULL to Queue.myFront in your destructor.)
Don't manually call destructors.
For a basic exercise, I recommend that you place a breakpoint on the line qdub2 = qdub1 and then debug step by step to see what your code really does.
According to general coding standard you should not call
this->~Queue()
through this statement a object is trying to delete itself.
try to copy data into new queue and then delete it if it goes out of scope. otherwise keep it as it is.
another example for understanding C++ template