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.
Related
i need to code a linked list for university in c++, mostly to practice coding iterators.
I tested it with some basic cases and it works but after i pass it in valgrind and the test server for the program i get a list of different errors. Maybe somebody can help me not to despair.
(At the end i will append the error list)
template <typename T = float>
class ForwardList
{
struct Node
{
/// Constructs a Node from a data value and a link to the next element.
Node(const T &data, Node *next) : data{data}, next{next} {}
/// A Node owns all nodes after it, so it deletes them on destruction
~Node() { delete next; }
//Performs a deep copy of the Node and all Nodes after it. Bad practice but we got it like that
Node *clone() const
{
if (next == nullptr)
{
return new Node{data, nullptr};
}
else
{
return new Node{data, next->clone()};
}
}
T data;
Node *next;
};
public:
ForwardList() : head(nullptr) {}
/// Copy constructor performs a deep copy of the other list's Nodes
ForwardList(const ForwardList &other)
{
head = other.head->clone();
}
/// Destructor makes sure that all Nodes are correctly destroyed
~ForwardList()
{
while (head->next != nullptr)
{
Node *tmp = head;
head = head->next;
delete tmp;
}
delete head;
}
/// Copy assignment operator uses the copy-and-swap idiom to make a safe
/// assignment
ForwardList &operator=(ForwardList other)
{
swap(*this, other);
return *this;
}
/// Add an element to the front of the list.
void push_front(const T &value)
{
std::cout << "Num: " << numberOfNodes << std::endl;
Node *item = new Node(value, nullptr);
if (head==nullptr)
{
head = item;
}else
{
item->next=head;
head = item;
}
numberOfNodes++;
}
/// Remove the first element of the list. Calling this function on an empty
/// list is undefined behavior. When implementing this function, be careful
/// to delete the one and only the one element that is removed.
void pop_front()
{
Node *item;
item = head->next;
delete head;
head = item;
numberOfNodes--;
}
/// Get a reference to the first element of the list
/// (const and non-const version)
T &front()
{
return head->data;
}
const T &front() const
{
return head->data;
}
/// Return true is the list is empty
bool empty() const
{
return numberOfNodes == 0 ? true : false;
}
std::size_t size() const
{
return numberOfNodes;
}
friend void swap(ForwardList &l, ForwardList &r)
{
Node *tmp = l.head;
l.head = r.head;
r.head = tmp;
}
private:
Node *head;
size_t numberOfNodes = 0;
};
And now the fun part (i will put it on pastebin because its pretty long):
https://pastebin.com/4JAKkJtP
Your issue is that ~Node tries to delete its next, and you also try to walk the list in ~ForwardList. By deleting ~Node(), you let ForwardList handle cleanup and everything works.
The clue here is that valgrind reported use after free, meaning something was deleting a pointer twice. That was a clue to look at everything that deletes a Node* (or really, delete in general).
I am writing a simple code, in which I have a list of objects of class Person.
The Person class
class Person
{
private:
std::string name;
std::string surname;
int year;
public:
Person(const std::string& personName,
const std::string& personSurname,
const int& personYear ):
name(personName), surname(personSurname), year(personYear) {}
Person(const Person& p): name(p.name), surname(p.surname), year(p.year) { }
Person(Person &&p) noexcept: name(std::move(p.name)), surname(std::move(p.surname)), year(std::move(p.year)) { }
int getYear()
{
return year;
}
void print()
{
std::cout << name << " " << surname << " " << year << std::endl;
}
};
in which, the move constructor is
Person(Person &&p) noexcept: name(std::move(p.name)), surname(std::move(p.surname)), year(std::move(p.year)) { }
I also have the node structure
struct node
{
Person data;
node *next;
inline node(const std::string& personName,
const std::string& personSurname,
const int& personYear): data(personName, personSurname, personYear), next(nullptr) { }
inline node(const Person& personToInsert): data(personToInsert), next(nullptr) {}
inline node(Person &&personToInsert): data(std::move(personToInsert)), next(nullptr) {}
};
whose move constructor is
inline node(Person &&personToInsert): data(std::move(personToInsert)), next(nullptr) {}
and finally I have the list class
class list
{
private:
node *head;
public:
list();
~list();
void insert(const std::string&, const std::string&, const int& );
void insert(const Person& );
void insert(Person &&);
void print();
};
whose move constructor is
void list::insert(Person &&personToInsert) {
node *new_node = new node(std::move(personToInsert));
if(head == nullptr || (head->data).getYear() >= (new_node->data).getYear())
{
new_node->next = head;
head = new_node;
}
else
{
node *current = head;
while(current->next != nullptr && (current->next->data).getYear() < (new_node->data).getYear())
{
current = current->next;
}
new_node->next = current->next;
current->next = new_node;
}
}
My question is: inside the move constructors, is the use of std::move correct? Particularly in the first line of code of list::insert
inside the move constructors, is the use of std::move correct?
Yes.
Particularly in the first line of code of list::insert
This is correct, too.
Note, however, that there are two minor things I would like to point out. First, there is no need to manually define the move constructor for Person. If your class doesn't do manual resource handling (memory, IO handles etc.), just rely on the special member functions that the compiler generates for you. In this case, I would just remove Person's move and copy constructor.
Second,
inline node(Person &&personToInsert)
is not a move constructor. It's an ordinary constructor that happens to take an rvalue-reference parameter. Move constructors are for constructing an object from an rvalue reference of their own type.
Third,
void list::insert(Person &&personToInsert)
is not a constructor at all - it's an ordinary member function that happens to accept an rvalue parameter.
So i have a Linked list implementation of my own and it can successfully keep integers and call them when needed with overloaded [] operator but when it comes to storing a class in my linked list, it seems that i can't call the class appropriately (using the same [] operator).
Called functions and members of my Linked List;
#include <iostream>
#include <assert.h>
template<typename T>
struct node {
T data;
node<T>* next;
};
template<typename T>
class Vectem {
private:
node<T>* head;
node<T>* last;
int lenght;
public:
void insert(T value) {
last->next = new node<T>;
last = last->next;
last->data = value;
last->next = NULL;
if (isEmpty()) {
head = last;
}
lenght++;
}
node<T>* search(int indx) {
node<T>* current;
current = head;
int count=0;
while (current != NULL) {
if (count == indx) {
break;
}
current = current->next;
count++;
}
return current;
}
T& operator [](int indx) {
assert(indx >= lenght - 1);
T result;
result = search(indx)->data;
return result;
}
};
And here is the main function and the class that i try to store;
#include <iostream>
#include <fstream>
#include <string>
#include "VectemLibrary.h"
class word {
public:
std::string value;
int count;
word(std::string value, int count): value(value),count(count) {
}
word() {
value = "NOT ASSIGNED";
count = 0;
}
word(const word& w1) {
value = w1.value;
count = w1.count;
}
~word() {
std::cout << "Word Destroyed" << std::endl;
}
};
int main()
{
Vectem<word> wordContainer;
word newWord("hello", 1);
wordContainer.insert(newWord);
std::cout << wordContainer[0].value;
}
Visual studio gave me the expection with this message at the last line where i call the first member of linked list with [];
Exception thrown at 0x7A0CF3BE (ucrtbased.dll) in Top 10 words.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.
I think that my lack of experience with pointers may have caused the problem but if you see something that i can't, Please enlighten me.
There are other problems with the code you posted as well (e.g. isEmpty() is not declared or defined), but I'll focus on the issue you explicitly mentioned.
In your operator:
T& operator [](int indx) {
assert(indx >= lenght - 1);
// You declare this variable on the stack
T result;
result = search(indx)->data;
// And then you return this variable by reference; this is not okay
return result;
}
As mentioned in my code comments (and by #Johnny Mopp in his comment to your post), you shouldn't (can't) return a reference or pointer to a variable declared within the returning function and constructed on the stack. Anything on the stack will be destroyed once the function call ends, so any returned pointers or references to such variables will be dangling references; using said pointers or references will result in undefined behavior.
So you don't want to return a reference to a stack-allocated variable like result; you want to return a reference to the data within the node itself (which is allocated on the heap by insert()), as it will still be a valid reference after the function returns:
return search(indx)->data;
There are several problems with your code, but the most important is that you are not initializing the head, last, or lenght members of Vectem at all. An Access Violation error at address 0xCCCCCCCC is a good indication that uninitialized memory is being accessed, as some compilers/setups fill uninitialized memory with 0xCC bytes, thus head and last are initially 0xCCCCCCCC in your case.
You need to add appropriate constructors to Vectem (as well as a destructor, a copy constructor, and a copy assignment operator, per the Rule of 3), eg:
template<typename T>
class Vectem {
private:
node<T>* head;
node<T>* last;
int lenght;
public:
Vectem() : head(NULL), last(NULL), lenght(0) {}
Vectem(const Vectem &src) : head(NULL), last(NULL), lenght(0)
{
// copy src's data to *this as needed ...
}
~Vectem()
{
// cleanup *this as needed ...
}
Vectem& operator=(const Vectem &rhs)
{
if (&rhs != this) {
// clear *this, and copy rhs's data to *this, as needed ...
}
return *this;
}
...
};
Or, in C++11 and later, you can initialize the members directly in their declarations (also, be sure to add a move constructor and a move assignment operator, per the Rule of 5), eg:
template<typename T>
class Vectem {
private:
node<T>* head = nullptr;
node<T>* last = nullptr;
int lenght = 0;
public:
Vectem() = default;
Vectem(const Vectem &src)
{
// copy src's data to *this as needed ...
}
Vectem(Vectem &&src) : head(src.head), last(src.last), lenght(src.lenght)
{
src.head = nullptr;
src.last = nullptr;
src.lenght = 0;
}
~Vectem()
{
// cleanup *this as needed ...
}
Vectem& operator=(const Vectem &rhs)
{
if (&rhs != this) {
// clear *this, and copy rhs's data to *this, as needed ...
}
return *this;
}
Vectem& operator=(Vectem &&rhs)
{
// clear *this as needed...
head = rhs.head; rhs.head = nullptr;
last = rhs.last; rhs.last = nullptr;
lenght = rhs.lenght; rhs.lenght = 0;
return *this;
}
...
};
That being said, insert() is also buggy, as it is dereferencing last before checking that last is actually pointing at a valid node. Try something more like this instead:
void insert(T value) {
node<T> *n = new node<T>{value, NULL};
if (!head) head = n;
if (last) last->next = n;
last = n;
++lenght;
}
Alternatively:
void insert(T value) {
node<T> **p = (last) ? &(last->next) : &head;
*p = new node<T>{value, NULL};
last = *p;
++lenght;
}
I am writing an Ordered Linked List class definition (OLList). I have written the assignment operator function, but when I try to test it by chaining assignment operations, the program gets caught in the while loop of the OLList::copy function. I know this because I tested using console prints.
//OLList.h
struct Node {
ListItem item;
Node *next;
};
class OLList {
public:
OLList& OLList::operator =(const OLList& rhs)
{
if (this != &rhs) {
destroy();
copy(rhs);
}
return *this;
}
void OLList::destroy()
{
Node *current_node = this->headM;
Node *next_node;
while(current_node->next != nullptr)
{
next_node = current_node->next;
delete(current_node);
current_node = next_node;
}
return;
}
void OLList::copy(const OLList& source)
{
Node *new_node, *current_node;
Node *current_source_node = source.headM;
this->headM->item = source.headM->item;
current_node = this->headM;
while(current_source_node->next != nullptr)
{
new_node = new(Node);
current_node->next = new_node;
current_node = current_node->next;
current_source_node = current_source_node->next;
current_node->item = current_source_node->item;
}
return;
}
}
Below is the code used to test the class. I have made sure that the print() function works fine so that's definitely not an issue.
//main.cpp
int main()
{
OLList the_list;
the_list.insert(1);
the_list.insert(2);
OLList second_list;
second_list.insert(3);
second_list.insert(4);
OLList third_list;
third_list.insert(5);
third_list.insert(6);
third_list = second_list = the_list;
third_list.print();
}
When it is compiled and run, the program never terminates as it is caught in the loop mentioned above.
Your destroy() method will fail if headM is nullptr. You should be using while(current_node != nullptr) instead of while(current_node->next != nullptr). But more importantly, it doesn't reset headM to nullptr after destroying the list. So after operator= calls destroy(), headM is no longer in a valid state for copy() to use.
Your copy() method is similarly not checking if either source or target headM are nullptr. But more importantly, it assumes the target list is empty beforehand, otherwise it leaks memory, if it does not crash outright (per above). And frankly, it simply is not coded correctly in general to copy one list to another.
So, your code is invoking undefined behavior, this anything could happen.
Like #PaulMcKenzie stated in comments, you really should be using a proper copy constructor instead (and a destructor - and since you are clearly using C++11 or later, a move constructor and move assignment operator, too - see the Rule of 5). Your assignment operator can then be implemented using your copy constructor (and likewise for move assignment).
Try something more like this:
struct Node {
ListItem item;
Node *next = nullptr;
Node(const ListItem &value) : item(value) {}
};
class OLList {
private:
Node *headM = nullptr;
public:
OLList() = default;
OLList(const OLList &src)
{
Node *current_source_node = src.headM;
Node **current_node = &headM;
while (current_source_node)
{
*current_node = new Node(current_source_node->item);
current_node = &((*current_node)->next);
current_source_node = current_source_node->next;
}
/* alternatively:
Node *current_source_node = src.headM;
while (current_source_node) {
insert(current_source_node->item);
}
*/
}
OLList(OLList&& src)
{
src.swap(*this);
}
~OLList()
{
Node *next_node;
while (headM)
{
next_node = headM->next;
delete headM;
headM = next_node;
}
}
void clear() {
OLList().swap(*this);
}
OLList& operator=(const OLList& rhs)
{
if (this != &rhs) {
OLList(rhs).swap(*this);
}
return *this;
}
OLList& OLList::operator=(OLList&& rhs)
{
OLList(std::move(rhs)).swap(*this);
return *this;
}
void swap(OLList &other) {
std::swap(headM, other.headM);
}
void insert(const ListItem &value) {
...
}
void print() const {
...
}
...
};
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