Overloading operator= in a singly linked list - c++

I'm currently having a problem copying the contents from one list to another. The values all appear as 0 when compiled. I thought that there might be something wrong when overloading the assignment operator as the copy constructor is using its code.
Copy Constructor
ListOfDoubles::ListOfDoubles(const ListOfDoubles&e):head(NULL) {
if (this != &e) {
*this = e;
}
}
Overloaded operator=
const ListOfDoubles& ListOfDoubles::operator=(const ListOfDoubles
&doubleslist)
{
DoubleListNode *cpyPtr = NULL;
DoubleListNode* orgPtr = doubleslist.head;
if (this != &doubleslist)
{
while (head != NULL)
{
ListOfDoubles::~ListOfDoubles();
}
while (orgPtr != NULL)
{
if (head == NULL)
{
head = cpyPtr = new DoubleListNode(orgPtr->data);
}
else
{
cpyPtr->next = new DoubleListNode(orgPtr->data);
cpyPtr = cpyPtr->next;
}
orgPtr = orgPtr->next;
}
}
return *this;
}

General-purpose copy logic looks something like:
DoubleListNode * from = source.head; // copying from
DoubleListNode ** to = &head; // pointer to where we want to copy to
while (from != nullptr) // keep going until end of list. You did mark
// the end of the list, didn't you?
{
*to = new node(*from); //copy construct a new node around from and store it at to
to = &(*to)->next; // advance to
from = from.next; // advance from
}
*to = nullptr; // all done. Terminate list.
The real magic is going on up here at the double pointer: DoubleListNode ** to By having a pointer to a pointer, we don't care whether we're pointing at head, another node's next or what have you. It's just another node, so there are no special cases to cover.
You can do the above in both the copy constructor and assignment operator, though you are better off not repeating yourself and putting it in a function called by the copy constructor and assignment operator. Note that there are different assumptions about pre-existing data in the copy constructor (eg, list will be empty) and the assignment operator (eg, list may not be empty, so clear it and free all of the nodes before you begin) that need to be taken into account.
The primary alternative, as discussed in the comments above, is to use the Copy and Swap Idiom. For this, the above copy loop only exists in the copy constructor.

The input parameter of a copy constructor will never be the object being constructed, so checking for this != &e in the copy constructor is redundant.
Also, manually calling a destructor directly is illegal unless the memory was allocated with placement-new, which you are not using. You need to use delete to destroy your node instances.
Typically, you shouldn't implement the copy constructor in terms of operator=, you should do it the other way around. Let the copy constructor do its job of copying the source values, and then have operator= make a copy of the source list and take ownership of the copied data. This is commonly known as the "copy and swap" idiom.
Try this instead:
ListOfDoubles::ListOfDoubles()
: head(NULL)
{
}
ListOfDoubles::ListOfDoubles(const ListOfDoubles &e)
: head(NULL)
{
DoubleListNode *cpyPtr = NULL;
DoubleListNode *prevPtr = NULL;
DoubleListNode *orgPtr = e.head;
while (orgPtr)
{
cpyPtr = new DoubleListNode(orgPtr->data);
if (!head)
head = cpyPtr;
if (prevPtr)
prevPtr->next = cpyPtr;
prevPtr = cpyPtr;
orgPtr = orgPtr->next;
}
/* alternatively:
DoubleListNode **cpyPtr = &head;
DoubleListNode *orgPtr = e.head;
while (orgPtr)
{
*cpyPtr = new DoubleListNode(orgPtr->data);
cpyPtr = &((*cpyPtr)->next);
orgPtr = orgPtr->next;
}
*cpyPtr = NULL;
*/
}
ListOfDoubles::~ListOfDoubles()
{
DoubleListNode *orgPtr = head;
DoubleListNode *nextPtr;
while (orgPtr)
{
nextPtr = orgPtr->next;
delete orgPtr;
orgPtr = nextPtr;
}
}
ListOfDoubles& ListOfDoubles::operator=(const ListOfDoubles &doubleslist)
{
if (this != &doubleslist)
{
ListOfDouble tmp(doubleslist);
std::swap(head, tmp.head);
}
return *this;
}

Related

C++ - Should you delete allocated memory in the copy assignment operator of a class that uses raw pointers?

I am still somewhat new at c++, and I am a little confused about this.
Say we have struct struct_x that uses raw pointers as attributes. Should the raw pointers then be deleted in the copy assignment operator, when they are already allocated? Or should you just assign the new pointee to the pointer?
(I am aware that it is advised to use smart/unique/shared pointers).
Code example:
struct struct_x {
public:
// Some attributes.
char* m_arr nullptr;
size_t* m_len = nullptr;
size_t* m_alloc_len = nullptr;
// Default constructor.
struct_x() {
m_len = new size_t(0);
m_alloc_len = new size_t(0);
}
// Copy constructor.
// Treat "struct_x" as a unique pointer for this example.
struct_x(const struct_x& x) {
// Assign.
m_arr = new char[*x.m_alloc_len + 1];
memcpy(m_arr, x.m_arr,*x.m_len);
m_len = new size_t(*x.m_len);
m_alloc_len = new size_t(*x.m_alloc_len);
}
// Copy assignment operator.
void operator =(const struct_x& x) {
//
//
// OVER HERE
//
// Should the pointers be deleted when they are already allocated?
// Like:
if (m_arr != nullptr) { delete[] m_arr; }
if (m_len != nullptr) { delete m_len; }
if (m_alloc_len != nullptr) { delete m_alloc_len; }
// Or not?
// Assign.
...
}
}
Second question:
Do I need to delete the old m_arr after using memmove?
// Resize.
void resize(const len_t& req_len) {
using T = char; // only for this example.
if (req_len > m_alloc_len) {
if (m_alloc_len == 0) { m_alloc_len = req_len; }
m_alloc_len *= 16;
T* l_arr = new T [m_alloc_len + 1];
memmove(l_arr, m_arr, m_len);
// if (m_arr != nullptr && m_arr) { delete [] m_arr; } // Do i need to delete here, no right?
m_arr = l_arr;
}
}
If you are implementing a string-like class as an exercise, then m_len and m_alloc_len should not be pointers at all. Only m_arr should be a pointer. If you are not doing an exercise, you should be using std::string or perhaps std::vector<char>.
Having said that...
It is perfectly fine and necessary to delete owning raw pointers in assignment operators of resource-managing classes.
There is a caveat though. The assignment operator should be protected against self-assignment. If you do
my_object = my_object;
then without such protection your program will access deleted memory area. You want this:
void operator =(const struct_x& x) {
if (this != &x) {
// contents of your assignment operator
}
}
my_object = my_object is an unlikely assignment to appear in a program, but such things can and do happen, especially if there is indirection involved. Say when doing a[i] = a[j], it is perfectly reasonable to have i == j.
There are other (better) ways to protect against self-assignment. You will encounter them in due course. You probably need to learn about move semantics first.before

Cloning a singly linked list using overloaded assignment operator

I'm trying to implement a Singly linked list class in C++. I've overloaded the assignment operator to clone the list. The cloning itself seems to work fine, but the program crashes during deletion of the cloned list, which leads me to suspect if something's wrong during the copy procedure.
Any help is highly appreciated.
Here's the code for the overloaded = operator:
DLList& DLList::operator=(const DLList& iList)
{
Node *t = iList.head();
while(t != NULL)
{
push(t->data);
t = t->next;
}
return *this;
}
Here's the code for the push operation:
void DLList::push(int data)
{
Node *n = new Node;
n->data = data;
n->next = NULL;
//n->random = NULL;
n->next = _head;
_head = n;
//cout << "_head" <<_head->data<< "head->next" << (_head->next ? _head->next->data : 0)<<endl;
}
Here's the main code (where the crash happens) :
DLList *d = new DLList();
d->push(10);
d->push(20);
d->push(30);
d->push(40);
d->setRandom();
d->display("Original list"); // Displays the original list fine
DLList *d1 = new DLList();
d1 = d;
d1->display("Cloned list"); //Displays the cloned list fine
delete d; // works fine
delete d1; // Crashes here due to segmentation fault, invalid pointer access during delete called as part of destructor)
Code for destructor (if it helps):
~DLList()
{
while(_head != NULL)
{
Node *temp = _head;
_head = _head->next;
delete temp; // crashes here during first iteration for the cloned list
}
}
you are not using the assignment operator actually! You are just copying the pointer to the other pointer.
Debugging your code (or just add traces) would have showed that
you were not calling your copy code
the addresses of d and d1 are the same
So when you delete the second list you delete the same address twice.
You don't really need new here:
DLList d;
d.push(10);
DLList d1 = d; // assignment is REALLY called
d1.display("Cloned list");
the objects will be deleted when you go out of scope of your variables
If you want to test in your context, keeping the new, change
d1 = d;
by
*d1 = *d;
to activate assignment operator.
Another advice: factorize your copy code so it is shared between assignment operator and copy constructor, and the deletion code should be shared between destructor and assignment operator (to avoid memory leaks when assigning twice): Not tested, don't flame me if it doesn't compile, just tell me I'll fix it, I'm already overanswering here :)
DLList& DLList::operator=(const DLList& iList)
{
destroy(); // or you'll get memory leaks if assignment done twice
copy(iList);
return *this;
}
DLList& DLList::DLList(const DLList& iList)
{
_head = NULL; // set _head to NULL in all your constructors!!
copy(iList);
}
~DLList()
{
destroy();
}
void DLList::copy(const DLList& iList) // should be private
{
Node *t = iList.head();
while(t != NULL)
{
push(t->data);
t = t->next;
}
}
void DLList::destroy() // private
{
while(_head != NULL)
{
Node *temp = _head;
_head = _head->next;
delete temp; // crashes here during first iteration for the cloned list
}
_head = NULL; // calling destroy twice is harmless
}
d1 = d; is NOT copying DLList. Instead, you just make d1 point to the list that d pointing to. So, both d1 and d are pointing to the same list. At the end of your program, you will delete the list twice, and make your program crash.
If you wish to use the assignment operator, you need to dereference your pointers. See here:
DLList *d1 = new DLList();
d1 = d; // <------------- This line
d1->display("Cloned list"); //Displays the cloned list fine
On the highlighted line, you're setting the pointer d1 to point to the same location as d. This means that when you call delete at the end of your code, you're trying to delete the same object (d in this case) twice.
To fix your code, you should dereference your pointers:
DLList *d1 = new DLList();
*d1 = *d; // <------------- Note the dereference applied to BOTH lists.
d1->display("Cloned list"); //Displays the cloned list fine
Alternatively, you should avoid using pointers entirely if you can. For your trivial example you could simply create your objects directly.
Your problem is the statement
d1 = d;
which does a pointer assignment, and makes d1 point to the same object as d. The subsequent delete statements cause the same object (*d) to be released twice. That is undefined behaviour (of which one symptom is a crash like you describe).
The above also does not invoke DLLists copy constructor. If that is your intent, you need to do
*d1 = *d;
which makes the object pointed to by d1 a copy of the one pointed to by d. The two delete statements are also appropriate in this case.

Copy Constructor not working for Circular Queue?

Im trying to make a copy of an object, which is a circular queue. My Enqueue and Dequeue work correctly, but whenever I do this, I get a runtime error.
CQUEUE j = k;
The output window says that my copy constructor is recursive on all control paths? Can someone help me figure out what I am doing wrong please? Here is my copy constructor, along with the overloaded assignment operator.
CQUEUE::CQUEUE(const CQUEUE& original)
{
(*this) = original;
}
void CQUEUE::operator=(CQUEUE w)
{
qnode *p = w.front;
(*this).front = new qnode;
(*this).back = front;
while(p -> next != w.back)
{
back -> num = p -> num;
p = p -> next;
back -> next = new qnode;
back = back -> next;
}
back -> num = p -> num;
p = p -> next;
back -> next = new qnode;
back = back -> next;
back -> num = p -> num;
back -> next = front;
front -> prev = back;
}
(*this) = original;
The compiler is making this line call the copy constructor instead of your = operator, because your = operator currently takes a non-const, by-value CQUEUE. Instead, your = operator should look like this:
void CQUEUE::operator=(const CQUEUE& w)
Then your copy constructor will call your operator = instead. Alternatively, make it more explicit:
CQUEUE::CQUEUE(const CQUEUE& original)
{
Copy(original);
}
void CQUEUE::operator=(const CQUEUE& w)
{
Copy(w);
}
void CQUEUE::Copy(const CQUEUE& other)
{
// All your copy code here
}
EDIT: Yes, you're correct, you still do need a copy constructor.
Change the assignment operator function to and be careful about self-assignment
void CQUEUE::operator=(const CQUEUE& w)
{
if (this == &w)
return *this;
//Paste your code
}
You're doing it backward, somewhat.
In general, the assignment operator is more complex than the copy constructor because it needs to properly dispose of the held resources on top of moving to the new value.
It is normally easier to compose a complex system out of simple parts rather than create a do-it-all part and express simple systems on top of it. In your context, it means than it is easier to create an assignment operator from a copy constructor.
Therefore, just write the copy constructor fully:
CQUEUE(CQUEUE const& c) { ... }
Then write a routine to swap (exchange the contents) of two CQUEUE instances:
void swap(CQUEUE& c) {
using std::swap;
swap(front, c.front);
swap(back, c.back);
}
And finally, build the assignment operator out of them:
CQUEUE& operator=(CQUEUE c) { // copy
this->swap(c);
return *this;
}
This idiom is known as copy-and-swap (yes, very creative!).
Note: bonus note, if you have a move constructor, then it becomes a move-and-swap for free.

Operator Overloading for Queue C++

I was trying to use the overload operator method to copy the entries of one queue into another, but I am going wrong with my function. I don't know how else to access the values of the queue "original" any other way than what I have below:
struct Node
{
int item;
Node* next;
};
class Queue
{
public:
// Extra code here
void operator = (const Queue &original);
protected:
Node *front, *end;
};
void Queue::operator=(const Queue &original)
{
//THIS IS WHERE IM GOING WRONG
while(original.front->next != NULL) {
front->item = original.front->item;
front->next = new Node;
front = front->next;
original.front = original.front->next;
}
}
Do you have a functional copy constructor? If so, I'd implement your assignment operator in terms of your copy constructor like so:
#include <algorithm> // <utility> for C++11
void Queue::operator=(const Queue &other)
{
// Assumes your only field is the "front" pointer.
Queue tmp(other); // May throw.
std::swap(front, tmp.front); // Will not throw.
}
The idea is that you perform any operations that can throw an exception (like your call to operator new()) on the side in a temporary object that will clean up resources, and then "commit" your changes by swapping the contents in a non-throwing operation so that the state of your Queue is sane even if an exception is thrown in during the construction of tmp. Pointer assignment is guaranteed not to throw, which is why the call to std::swap() is non-throwing in this case. Upon leaving the scope of your assignment operator tmp's destructor should clean up your old link list since its front was swapped with your old front.
See GotW #59 for details about this "copy-to-temporary-and-swap" idiom, and how it relates to the strong exception safety guarantee.
void Queue::operator=(const Queue &original)
{
Node* tmp = original.front;
//THIS IS WHERE IM GOING WRONG
while(tmp->next != NULL) {
front->item = tmp->item;
front->next = new Node;
front = front->next;
tmp = tmp->next;
}
}

C++ Linked List Segmentation Fault

I'm writing a program as an assignment for school and I though I had worked out all the bugs until I decided to call my copy constructor. The program is interactive (CLI) which basically has two moduals: a .h and .cpp file for the LList class and a .h and .cpp file for the structure of the program and also a 3rd cpp file just for main(). It is suppose to be a program for a hydropower engineering company (fake company) in which the LList nodes hold data for their annual water flow in a river(year and flow). Here is some insight on the class:
//list.h
struct ListItem {
int year;
double flow;
};
struct Node {
ListItem item;
Node *next;
};
class FlowList {
public:
FlowList();
// PROMISES: Creates empty list
FlowList(const FlowList& source);
// REQUIRES: source refers to an existing List object
// PROMISES: create the copy of source
~FlowList();
// PROMISES: Destroys an existing list.
FlowList& operator =(const FlowList& rhs);
// REQUIRES: rhs refers to an existing FlowList object
// PROMISES: the left hand side object becomes the copy of rhs
//....Other member functions
private:
// always points to the first node in the list.
Node *headM;
// Initially is set to NULL, but it may point to any node.
Node *cursorM;
//For node manipulation within interactive CLI
void copy(const FlowList& source);
void destroy();
I belive the memory leak or collision is taking place somewhere within the copy function but cant pin point where.
//list.cpp
FlowList::FlowList() : headM(0), cursorM(0) {}
FlowList::FlowList(const FlowList& source)
{
copy(source);
}
FlowList::~FlowList()
{
destroy();
}
FlowList& FlowList::operator =(const FlowList& rhs)
{
if (this != &rhs)
{
destroy();
copy(rhs);
}
return (*this);
}
//...more function definitions
void FlowList::copy(const FlowList& source)
{
if (source.headM == NULL)
{
headM = NULL;
cursorM = NULL;
return;
}
Node* new_node = new Node;
assert(new_node);
new_node->item.year = source.headM->item.year;
new_node->item.flow = source.headM->item.flow;
new_node->next = NULL;
headM = new_node;
Node* thisptr = new_node;
for(Node* srcptr = source.headM->next; srcptr != NULL; srcptr = srcptr->next)
{
new_node = new Node;
assert(new_node);
new_node->item.year = srcptr->item.year;
new_node->item.flow = srcptr->item.flow;
new_node->next = NULL;
thisptr->next = new_node;
thisptr = thisptr->next;
}
}
void FlowList::destroy()
{
Node* ptr = headM;
Node* post = headM->next;
while (ptr != NULL)
{
delete ptr;
ptr = post;
if (post)
post = ptr->next;
}
headM = NULL;
}
The program works fine if I create a FlowList object, fill it with data from a .dat file; i can then manipulate the data within the program (display, perform calculations, add to the list, remove from list and save data to file) but program crashes (segmentation fault) if I create another FlowList object (within main.cpp).
Any help would be really appreciated.
The initial thing I spot is that it looks like your destroy() function will always segmentation fault if the list is empty:
void FlowList::destroy()
{
Node* ptr = headM;
Node* post = headM->next;
//...
}
If the list is empty, headM is NULL and then you're trying to do headM->next which will always produce a segmentation fault in that case.
I think you might also have a memory leak in your copy constructor if you pass in an empty list. If you look at this code:
void FlowList::copy(const FlowList& source)
{
if (source.headM == NULL)
{
headM = NULL;
cursorM = NULL;
return;
}
//...
}
What if the current list contained 20 items and source is an empty list? You set the current list's headM and cursorM to NULL, but you never call delete on any of the nodes in the current list that you originally used new to create. You probably want to work your destroy() function somewhere into your copy constructor too (you did it for the operator= function).
The last thing I noticed is that you don't initialize cursorM for a non-empty list in your copy() function (#Akusete pointed that out as well). I think I'd recommend that at the beginning of your copy constructor, just initialize cursorM and headM to NULL just to cover your bases.
It looks like you're really close, I think you just really need to think through the boundary case of dealing with empty lists (both on the LHS and RHS) and you'll probably find most of these bugs. Good luck!