Move constructor implementation in linked list - c++

I have the following List class that's supposed to hold a linked list. I'm experimenting and trying to learn more about move semantics.
I was wondering if I have implemented the move constructor and assignment operator properly.
What I want to achieve at the end, is have all contents emptied from an object to another.
template<typename T>
class List
{
public:
class Node {
public:
Node(T value, Node* prev, Node* next) : value_(value), prev_(prev), next_(next) {}
T value_;
Node* next_;
Node* prev_;
};
Node* head_;
Node* tail_;
//! Move constructor
List(List&& list) {
std::swap(head_, list.head_);
std::swap(tail_, list.tail_);
}
//! Move assignment operator
List& operator= (List&& list) {
std::swap(head_, list.head_);
std::swap(tail_, list.tail_);
return *this;
}
};
main.cpp
#include <iostream>
#include "List.h"
#include <string>
int main(){
List<int> l1;
List<int> l2;
//or calling the constructor: List<int> l2(std::move(l1));
l1.push_front(4);
l1.push_front(3);
l2 = std::move(l1); //all contents of l1 object are dumped into l2
}

For the class shown here, there are no members except tail and head, hence I'd say
You must initialize your tail and head before making the moving in the move ctor
List(List&& list) noexcept : head{ nullptr }, tail{ nullptr } {
std::swap(head_, list.head_);
std::swap(tail_, list.tail_);
}
The constructor and the assignment operator should be noexcept because the compiler may ignore them.
You may check for self-assignment in the beginning of the assignment operator, as follows
if (this == &rhs) {
return *this;
}

During a move operation, the object/variable being moved from must be left in a "valid but indeterminate state". However, in your move constructor, head_ and tail_ have not been initialized before swapping them into the source list that is being moved, thus leaving the source list in an invalid state after the move.
You need to change these declarations:
Node* head_;
Node* tail_;
To this:
Node* head_ = nullptr;
Node* tail_ = nullptr;
Also, you need to add a copy constructor, too:
//! Copy constructor
List(const List& list) {
Node **n = &head_;
for (Node *ptr = list.head_; ptr; ptr = ptr->next_) {
*n = new Node(ptr->value_, tail_, nullptr);
if (tail_) tail_->next_ = *n;
tail_ = *n;
n = &(tail_->next_);
}
}
Also, although your move assignment operator will "work", it is generally not a good idea to make the source object being moved to take ownership of the old data, since you don't know how the caller is using the source object. Just because you take an rvalue reference as input does not ensure the caller is passing in an actual temporary object 1 (case in point, your own example does not).
1: Though, the general rule of thumb is to never use an object after it is moved from, other than to reassign or reset it.
A safer option, which will also allow you to support copy assignment and move assignment in a single implementation, is to take the input parameter by value. This way, any assignment will construct a temp object utilizing your existing move constructor or copy constructor as needed, thus leaving the source object in a valid moved-from or copied-from state, and then you swap ownership of data with your temp object:
List& operator= (List list) {
std::swap(head_, list.head_);
std::swap(tail_, list.tail_);
return *this;
}

Related

Read access violation when trying to create copy constructor for linked list

I am having an issue in regards with my linkedlist implementation, where I am trying to create a copy constructor.
// Copy Constructor
List342(const List342& source)
{
*this = source;
}
List342& operator=(const List342& source)
{
Node<T>* s_node = nullptr; // Source node
Node<T>* d_node = nullptr; // Destination node
if (this == &source)
{
return *this;
}
// Empty memory on destination
DeleteList();
// If the source is empty, return the current object.
if (source.head_ == nullptr)
{
return *this;
}
// Copy source node to destination node, then make destination node the head.
d_node = new Node<T>;
d_node->data = (source.head_)->data;
head_ = d_node;
s_node = (source.head_)->next;
// Loop and copy the nodes from source
while (s_node != nullptr)
{
d_node->next = new Node<T>;
d_node = d_node-> next;
d_node->data = s_node->data;
s_node = s_node->next;
}
return *this;
}
For some reason, VS Studio throws a read access violation at me on the line d_node->data = s_node->data despite the while loop trying to prevent this.
The culprit may be lying in DeleteList, but for some reason my other methods, like printing the linkedlist, have no issues after calling DeleteList, as it prints nothing. I'm just wondering if there's any flaws in this DeleteList method.
// Delete all elements in the linked list
// Not only do you need to delete your nodes,
// But because the Node data is a pointer, this must be deleted too
void DeleteList()
{
// Similar to the linkedlist stack pop method except you're running a while
// loop until you the is empty.
Node<T>* temp;
while (head_ != nullptr)
{
temp = head_;
head_ = head_->next;
// For some reason if I try to delete temp->data, I keep getting symbols not loaded or debug
// assertion errors
// delete temp->data;
// What I can do here is set it to null
// Then delete it. This may have to do how uninitialized variables have random memory assigned
temp->data = nullptr;
delete temp->data;
delete temp;
}
}
Here is the Node definition:
template <class T>
struct Node
{
T* data;
//string* data;
Node* next;
}
By having Node::data be declared as a pointer, your code is responsible for following the Rule of 3/5/0 to manage the data pointers properly. But it is not doing so. Your copy assignment operator is shallow-copying the pointers themselves, not deep-copying the objects they point at.
Thus, DeleteList() crashes on the delete temp->data; statement, because you end up with multiple Nodes pointing at the same objects in memory, breaking unique ownership semantics. When one Node is destroyed, deleting its data object, any other Node that was copied from it is now left with a dangling pointer to invalid memory.
If you must use a pointer for Node::data, then you need to copy-construct every data object individually using new so DeleteList() can later delete them individually, eg:
d_node = new Node<T>;
d_node->data = new T(*(source.head_->data)); // <--
...
d_node->next = new Node<T>;
d_node = d_node->next;
d_node->data = new T(*(s_node->data)); // <--
...
However, this is no longer needed if you simply make Node::data not be a pointer in the first place:
template <class T>
struct Node
{
T data; //string data;
Node* next;
}
That being said, your copy constructor is calling your copy assignment operator, but the head_ member has not been initialized yet (unless it is and you simply didn't show that). Calling DeleteList() on an invalid list is undefined behavior. It would be safer to implement the assignment operator using the copy constructor (the so-called copy-swap idiom) , not the other way around, eg:
// Copy Constructor
List342(const List342& source)
: head_(nullptr)
{
Node<T>* s_node = source.head_;
Node<T>** d_node = &head_;
while (s_node)
{
*d_node = new Node<T>;
(*d_node)->data = new T(*(s_node->data));
// or: (*d_node)->data = s_node->data;
// if data is not a pointer anymore...
s_node = s_node->next;
d_node = &((*d_node)->next);
}
}
List342& operator=(const List342& source)
{
if (this != &source)
{
List342 temp(source);
std::swap(head_, temp.head_);
}
return *this;
}
However, the copy constructor you have shown can be made to work safely if you make sure to initialize head_ to nullptr before calling operator=, eg:
// Copy Constructor
List342(const List342& source)
: head_(nullptr) // <--
{
*this = source;
}
Or:
// Default Constructor
List342()
: head_(nullptr) // <--
{
}
// Copy Constructor
List342(const List342& source)
: List342() // <--
{
*this = source;
}
Or, initialize head_ directly in the class declaration, not in the constructor at all:
template<typename T>
class List342
{
...
private:
Node<T> *head_ = nullptr; // <--
...
};

Simple linked list with full set Rule of Five

I'm trying to correctly implement a simple linked list respecting the rule of 5. I get to about 3, although I already have my doubts here, but from there on, I'm on thin ice. As it seems like a fairly common topic, I was surprised I couldn't find a complete example. I've found bits and pieces, but no complete set. So if I get this sorted, it could serve as a future reference as well.
I've added an example class Data for some real life "complexity", because most examples just have a node with a single int and a pointer to the next item.
EDIT: I've completed the class with the code as shown below by PaulMcKenzie and it compiles ok in VS2019, but gives warning on the move constructor and assignment operator: C26439: This kind of function may not throw. Declare it 'noexcept' (f.6).
class Data
{
public:
int id;
string name;
float[5] datapoints;
};
class Node
{
public:
Node(Data d = { 0 }, Node* n = nullptr) : data(d), next(n) {};
Data& GetData() { return data; }
Node*& GetNext() { return next; }
private:
Data data;
Node* next;
};
class NodeList
{
public:
NodeList() :head(nullptr) {} // constructor
~NodeList(); // 1. destructor
NodeList(const NodeList& src); // 2. copy constructor
NodeList& operator=(const NodeList& src); // 3. copy assignment operator
NodeList(NodeList&& src); // 4. move constructor
NodeList& operator=(NodeList&& src); // 5. move assignment operator
void AddToNodeList(Data data); // add node
private:
Node* head;
};
void NodeList::AddToNodeList(Data data)
{
head = new Node(data, head);
}
NodeList::~NodeList()
{
Node* n = head, * np;
while (n != nullptr)
{
np = n->GetNext();
delete n;
n = np;
}
}
NodeList::NodeList(const NodeList & src) : head(nullptr)
{
Node* n = src.head;
while (n != nullptr)
{
AddToNodeList(n->GetData());
n = n->GetNext();
}
}
NodeList& NodeList::operator= (const NodeList& src)
{
if (&src != this)
{
NodeList temp(src);
std::swap(head, temp.head);
}
return *this;
}
NodeList::NodeList(NodeList&& src) : head{src.head}
{
src.head = nullptr;
}
NodeList& NodeList::operator=(NodeList&& src)
{
if (this != &src)
std::swap(src.head, head);
return *this;
}
The first thing to address is that your assignment operator is not correct. You're using the copy / swap idiom, but you forgot to do the copy.
NodeList& NodeList::operator=(NodeList src)
{
std::swap(head, src.head);
return *this;
}
Note the change from a const NodeList& to NodeList src as the argument. This will make the compiler automatically do the copy for us, since the parameter is passed by value.
If you still want to pass by const reference, the following change would need to be made:
NodeList& NodeList::operator=(const NodeList& src)
{
if ( &src != this )
{
NodeList temp(src); // copy
std::swap(head, temp.head);
}
return *this;
}
Note the additional test for self-assignment. It really isn't necessary, but may speed up the code (but again, no guarantee).
As to whether this is the most efficient way to do this, that is up for debate -- it all depends on the object. But one thing is for sure -- there will be no bugs, dangling pointers, or memory leaks if you use the copy/swap idiom (correctly).
Now onto the move functions:
To implement the missing functions, you should basically remove the contents from the existing object, and steal the contents from the passed-in object:
First, the move contructor:
NodeList::NodeList(Nodelist&& src) : head{src.head}
{
src.head = nullptr;
}
All we really want to do is steal the pointer from src, and then set the src.head to nullptr. Note that this will make src destructible, since src.head will be nullptr (and your destructor for NodeList handles the nullptr correctly).
Now for the move-assignment:
Nodelist& operator=(NodeList&& src)
{
if ( this != &src )
std::swap(src.head, head);
return *this;
}
We check for self assignment, since we don't want to steal from ourselves. In reality, we really didn't steal anything, only swapped things out. However unlike the assignment operator, no copy is done -- just a swap of the internals (this is basically what your incorrect assignment operator that was fixed earlier was doing). This allows the src to destroy the old contents when it's time for the src destructor to be invoked.
Note that after the move (either construction or assignment), the passed-in object is basically in a state that may or may not make the object usable or if not usable, stable (because potentially, the internals of the passed-in object have been changed).
The caller can still use such an object, but with all the risks of using an object that may or may not be in a stable state. Thus the safest thing for the caller is to let the object die off (which is why in the move constructor, we set the pointer to nullptr).

write access violation this->head was 0xDDDDDDDD in pop_front

I have some linked list code and everything is working until I create my pop_front function in my destructor. The code works everwhere else and I've tried printing the linked list to see if it was created properly and it was. This is the only part of my code where pop_front as been called and the only part where I am deallocating anything
template <typename T>
class DList {
Node<T>* head;
Node<T>* tail;
public:
DList() {
head = nullptr;
tail = nullptr;
}
void push_front(T newData) {
Node<T>* newNode = new Node<T>(newData, head, nullptr);
if (head) {
head->prev = newNode;
}
else {
tail = newNode;
};
head = newNode;
}
void push_front(DList<T> newList) {
newList.tail->next = head;
head->prev = newList.tail;
head = newList.head;
}
void pop_front() {
if (head) {
Node<T>* pop = head;
head = pop->next;
if (head) {
head->prev = nullptr;
}
else {
tail = nullptr;
}
delete pop;
}
}
~DList() {
while (head) {
pop_front();
}
}
};
Your DList class (and possibly also the template class Node as well, we can't see its definition) does not follow the rule of three/five. You define a destructor but you don't define a copy constructor or copy assignment operator.
This means that making a copy of a DList at all (which calling DList::push_front(DList<T>) almost certainly will) will cause a use-after-free in the destructor.
Remember that compiler-generated copy constructors and copy assignment operators simply perform a memberwise copy of values. In this case, your values are pointers. Therefore, a copy of a DList object will simply copy the original pointers to the new object.
When one of those objects is destructed, the list will be torn down, however the other object will still be holding pointers to the memory allocations that were freed.
You must implement the copy constructor and copy assignment operator if you wish your type to be copyable, because the compiler-generated versions will do the wrong thing. If you are using C++11, you should also define the move constructor and move assignment operator.
In C++11, you could also delete these constructors and operators and then you'll get a compile-time error everywhere that you attempt to make a copy:
DList(DList const &) = delete;
DList(DList &&) = delete;
DList & operator=(DList const &) = delete;
DList & operator=(DList &&) = delete;
Without the definition of the Node template class I cannot suggest an implementation of the copy constructor or copy assignment operator. However, I can suggest an implementation for the move variants:
void swap(DList & other) {
std::swap(head, other.head);
std::swap(tail, other.tail);
}
DList(DList && other) : head(nullptr), tail(nullptr) {
swap(other);
}
DList & operator=(DList && other) {
swap(other);
return *this;
}
Another error:
void push_front(DList<T> newList)
Should be:
void push_front(DList<T> & newList)
Because you modify the "new list" -- it doesn't make much sense to modify a copy. However, the semantic meaning of this doesn't match push_front(T)!
The single-value overload takes a value and pushes it onto this list.
The list overload takes another list and pushes this list onto the other list. It would make much more sense for the list overload to push the other list onto this list as it would mirror the behavior of the single-value overload ("add this thing to this list, whatever that means").
The implementation of this method is also flawed, as it doesn't set either list's pointers to null, which means that you are setting up another use-after-free.

How to return a List in C++

Let's say I have a List class:
class List
{
private:
class Node{
public:
int data;
Node* next;
public:
virtual ~Node()
{
if (next != NULL)
delete next;
}
};
Node* head;
public:
virtual ~List()
{
if (head != NULL)
{
delete head;
}
}
public:
void AddNode(int data);
void DeleteNode(int data);
//....
};
Now I want to implement a function, which takes two List reference as arguments and return a new created List:
List SumTwoList(List& list_1, List& list_2)
{
//here I create a new List list_3 and add some elements to it based on list_1 and list_2(add operation use dynamic allocation).
//finally I want to return this list_3.
return list_3;
}
I am confusing here. How should I create this list_3 inside SumTwoList(). If I just make it a local variable, when function returns, the destructor of list_3 will free all the nodes added to the list and destroy the whole list. However, if I do dynamic allocation to create this list_3 and return a pointer to it, it is the user's responsibility to delete this list after using.
I don't know how to choose from these two ideas and I'm pretty sure there are some much better methods to solve this problem. Thanks ahead for your advice. :-)
Add a copy constructor and assignment operator (and fix your destructor):
class List
{
private:
typedef struct node{
int data;
struct node* next;
}NODE;
NODE* head;
public:
virtual ~List() // Please fix this, as others have mentioned!
{ }
List(const List& rhs)
{
// this needs to be implemented
}
List& operator = (const List& rhs)
{
// this needs to be implemented
}
//...
};
The "this needs to be implemented" is what you need to fill in to get the copies to work.
So what does this buy you? First, you can now write functions in the form that you were attempting to do in your question. That is, you can return a List by value safely and without doing any further coding to dynamically allocate or delete a list.
List SumTwoList(List& list_1, List& list_2)
{
List list_3;
// do stuff to add data to list_3...
//...
//finally I want to return this list_3.
return list_3;
}
Second, say we want to copy a list to another list. That can be done easily using the "=" or by simple copy construction
If you don't want to copy, then turn copying off by making copy-ctor and assignment op private and unimplemented*, otherwise copying will be an option to the programmer using your class and the compiler will also make copies when it needs to.
In other words, if copying is implemented correctly, then a simple program like this should work:
int main()
{
List lis1;
// Add some nodes to lis1...
//...
// Assume that lis1 now has nodes...complete the copying test
List lis2(lis1);
List lis3;
lis3 = lis1;
}
The program above should have no memory leaks, and no crashes, even when main() returns. If not, then copying and/or destruction is broken.
*C++11 allows you to make the copy constructor and assignment operator disabled in an easier way than with pre C++11 code by using the delete keyword when defining the functions.
One option is to return a pointer to a new List
List * SumTwoList(List& list_1, List& list_2)
{
List * pResultList = new List;
// Iterate over all elements of list_1 and add (append) them to pResultList
// Iterate over all elements of list_2 and add (append) them to pResultList
return pResultList;
}
However, it has the drawback that the caller have to remember to delete the returned object.
Rather, it might be better to design the API slightly differently where elements of list_2 are append to the existing list, for example
// Append contents of rhs to this. rhs stays as is
List::append( List const & rhs );
Callers may call it as
List list_1;
// work on list_1, such as add elements
List list_2;
// work on list_2, such as add elements
list_1.append( list_2 );
This is close to std::list::splice(), but not entirely. splice() moves the elements from rhs to this, but append() just copies the elements.
Note, your destructor is buggy, it is delete'ing only thehead, the later elements are leaked.
A sketch for destructor implementation
List::~List() {
while( head ) {
struct node * oldHead = head;
head = head->next;
delete oldHead;
}
head = NULL;
}
You will return a copy of a list. This will do what you expect if copy constructor and assignment operator of class List will be implemented.
class List
{
private:
//...
public:
List();
List( const List& other);
List& operator= ( const List& other);
//...
};
Maybe better option would be however to create a constructor which takes two Lists and constructs a new one by merging them ( doing all this what SumTwoList would do):
class List
{
private:
//...
public:
List( const List& first, const List& second) { // similar to SumTwoList
}
//...
};

Looking for help when creating a copy constructor for a LinkedList class in C++

First, I realize that there are multiple topics about this on StackOverflow. I'm having a slight bit of trouble understanding the responses in some of them. I'm creating this in hopes that someone can help me understand the process.
I apologize if this question seems relatively novice, but I am doing my best to understand it.
I'm learning about data structures and have been asked to create a LinkedList implementation file based on a header that was provided.
This is homework, so please no 'here is the exact code' type answers. Pseudocode, steps, and hints are welcome.
Here is the part of the header that I'm working on at the moment:
typedef std::string ElementType;
class LinkedList
{
private:
class Node
{
public:
ElementType data;
Node * next;
Node( )
: data( ElementType( ) ), next( NULL )
{ }
Node( ElementType initData )
: data( initData ), next( NULL )
{ }
}; // end of Node class
typedef Node * NodePointer;
public:
LinkedList( );
/* Construct a List object
Precondition: none.
Postcondition: An empty List object has been constructed.
*/
LinkedList( const LinkedList &source );
/* Construct a copy of a List object.
Precondition: None.
Postcondition: A copy of source has been constructed.
*/
~LinkedList( );
/* Destroys a List object.
Precondition: None.
Postcondition: Any memory allocated to the List object has been freed.
*/
const LinkedList & operator=( const LinkedList &rightSide );
/* Assign a copy of a List object to the current object.
private:
NodePointer first;
int mySize;
};
So far, I've created the destructor, can you check and make sure it is correct?
//Destructor
LinkedList::~LinkedList()
{
NodePointer ptr = first;
while(ptr != 0 ) {
NodePointer next = ptr->next;
delete ptr;
ptr = next;
}
first = 0;
}
Now here is the part where I'm lost...What are the basic steps of creating the copy constructor? I've finished the default constructor which was simple, but I'm a bit confused with what I should be doing on the copy constructor.
I'm also slightly confused about overloading the = operator, I assume it will be very similar to the copy constructor.
Edit
My first attempt at the copy constructor:
LinkedList::LinkedList(const LinkedList & source)
{
//create a ptr to our copy
Node * copy_node = source.first;
//where we will be inserting our copy
Node * insert_node = first;
while(copy_node != nullptr)
{
//insert our new copy node
insert_node = new Node(copy_node->data);
//move to our next node
copy_node = copy_node->next;
if(copy_node != nullptr) {
insert_node = insert_node->next;
} else {
insert_node = first;
}
//update size
mySize++;
}
}
I feel like something is missing there.
What are the basic steps of creating the copy constructor? I've finished the default constructor which was simple, but I'm a bit confused with what I should be doing on the copy constructor.
Well, you need to copy the source, obviously. If the source is a list of N nodes then you need to construct another list of N nodes, with each node being a copy of the corresponding one in the source.
So loop over the source nodes and create copies of them.
I'm also slightly confused about overloading the = operator, I assume it will be very similar to the copy constructor.
Yes, except you need to dispose of the current nodes first. However, a simple and safe way to implement assignment is copy-and-swap, so define a correct swap member:
void swap(LinkedList& other)
{
std::swap(first, other.first);
std::swap(size, other.size);
}
Then use it to implement assignment:
LinkedList& operator=(const LinkedList& source)
{
LinkedList(source).swap(*this);
return *this;
}
This creates a temporary that is a copy of source, then swaps it with *this, so the old contents of *this get destroyed by the temporary, and *this ends up with the copied data.
N.B. the return type should be non-const, it is not idiomatic to return a const-reference from an assignment operator.
The most easiest way is to implement a function which adds new nodes into the list and call it in the loop inside the constructor:
LinkedList(const LinkedList& rhs)
{
Node* node = rhs.first;
while (node) {
add(node.data);
node = node->next;
}
}
void add(ElementType data)
{
Node* node = new Node(data);
// add node somehow
// I'd recommend to keep pointer to the tail
}
Please note this implementation is not exception safe!
EDIT: added copy function for the copy constructor, the first step to the exception safety:
LinkedList(const LinkedList& rhs)
{
copy(rhs.first, first);
}
void copy(Node* src, Node*& dest)
{
// handle the head element separately
if (!src) {
return; // empty list
} else {
dest = new Node(src->data); // equivalent to first = new Node...
src = src->next;
}
// copy the rest of the list
Node* p = dest;
while (src) {
p->next = new Node(src->data);
src = src->next;
p = p->next;
}
}