Assignment operator in linked list C++ - c++

I'm trying to implement linked list in c++.
I implement my assignment operator like this:
// assignment operator
template<class T>
LinkedList<T>& LinkedList<T>::operator = (const LinkedList& rhs) {
if (&rhs != this) {
Node *tmp = head;
while (tmp -> next) {
head = head -> next;
delete tmp;
tmp = head;
}
tmp = rhs -> head;
while (tmp) {
append(tmp);
tmp = tmp -> next;
}
}
return *this;
}
In my main function, i use the following code to test:
LinkedList<int> *lst1 = new LinkedList<int>(7);
LinkedList<int> *lst2 = new LinkedList<int>(101);
std::cout << lst1 -> head -> data << std::endl;
std::cout << lst2 -> head -> data << std::endl;
lst1 = lst2;
std::cout << lst1 -> head -> data << std::endl;
delete lst1;
delete lst2; <--------------- error here
As I expect, the console outputs:
7 101 101
But when the program try to delete lst2, i get an error saying:
pointer being freed was not allocated
I use debugger and find out when the program is doing assignment:
lst1 = lst2;
lst1 is actually referring to the address that points lst2 instead of getting a copy of lst2, so when i delete lst1, lst2 is already gone.
So can anyone please tell me what is wrong with my assignment operator?
I'm sorry if this is a novice question but I've been spending a few hours and could not figure out.
My completed code is shown below:
template<class T>
class LinkedList {
private:
class Node {
public:
T data;
Node *next;
// default constructor
Node() = default;
// constructor with data
Node(const T& data) : data(data), next(NULL) {}
};
public:
Node *head;
LinkedList(const LinkedList& copyLst);
LinkedList& operator=(const LinkedList& byValList);
LinkedList() : head(NULL){}
LinkedList(Node *newNode) : head(newNode) {}
LinkedList(T val) {
head = new Node(val);
}
~LinkedList();
static LinkedList<int> sumLists(const LinkedList<int>& lst1, const LinkedList<int>& lst2) ;
void insertAtFront(T val);
void insertAtEnd(T val);
void printList();
void insert(T val);
void append(const Node&);
};
// copy constructor
template<class T>
LinkedList<T>::LinkedList(const LinkedList<T>& copyLst) {
const Node *cpCurrent = copyLst.head;
Node *lsCurrent = NULL;
if (cpCurrent != NULL) {
head = new Node(cpCurrent -> data);
lsCurrent = head;
cpCurrent = cpCurrent -> next;
}
while (cpCurrent != NULL) {
Node *newNode = new Node(cpCurrent -> data);
lsCurrent -> next = newNode;
lsCurrent = lsCurrent -> next;
cpCurrent = cpCurrent -> next;
}
}
// assignment operator
template<class T>
LinkedList<T>& LinkedList<T>::operator = (const LinkedList& rhs) {
if (&rhs != this) {
Node *tmp = head;
while (tmp -> next) {
head = head -> next;
delete tmp;
tmp = head;
}
tmp = rhs -> head;
while (tmp) {
append(tmp);
tmp = tmp -> next;
}
}
return *this;
}
// destructor
template<class T>
LinkedList<T>::~LinkedList() {
Node *current = head;
while (current != NULL) {
head = head -> next;
delete current;
current = head;
}
}
template<typename T>
void LinkedList<T>::append(const Node& node ){
if (NULL == head) {
Node *newNode = new Node(node -> data);
head = newNode;
} else {
Node *current = head;
while (current -> next) {
current = current -> next;
}
Node *newNode = new Node(node -> data);
current -> next = newNode;
}
}

Your current implementation duplicates code that already exists in the copy constructor, so why not reuse it?
If you have a working copy constructor and destructor, usage of the copy / swap idiom would be the easiest and safest way to implement the assignment operator.
#include <algorithm>
//...
template<class T>
LinkedList<T>& LinkedList<T>::operator = (const LinkedList<T>& rhs)
{
LinkedList<T> temp(rhs);
std::swap(temp.head, head);
return *this;
}
Given that your copy constructor and destructor work correctly, this is guaranteed to work correctly. We create a copy temp of the object rhs and swap out its contents with the contents of *this. When temp gets destroyed at the return, it takes along with it the old data that used to be in *this.
If you have a C++ 11 compiler, you can take advantage of move construction on the passed-in parameter with pass-by-value.
#include <algorithm>
//...
template<class T>
LinkedList<T>& LinkedList<T>::operator = (LinkedList<T> rhs)
{
std::swap(rhs.head, head);
return *this;
}
* It should be noted that you need to swap ALL members of the class when using the copy and swap idiom, otherwise you will likely invalidate the class members' invariants *

Related

C++ Queue Implementation using the Default Move Assignment Operator

My group and I attempted to implement a queue in C++ based on the following example for the code of a stack.
template<typename T>
class Stack {
private:
struct Node {
T data;
Node* next;
Node(T data, Node* next): data(data), next(next) {}
// No destructor here; it is a private inner class and would be slow
// to delete a chain of nodes this way!
};
private:
Node* head;
public:
Stack(): head(nullptr) {}
~Stack() {
while (head != nullptr) {
Node* old_head = head;
head = head->next; // update
delete old_head;
}
}
// Prohibit copy construction and copy assignment
Stack(Stack&) = delete;
Stack& operator=(Stack&) = delete;
// Wait wait I want to be able to move
Stack(Stack&&) = default;
Stack& operator=(Stack&&) = default;
public:
int getSize() {
int count = 0;
for (Node* n = head; n != nullptr; n = n->next) count++;
return count;
}
bool isEmpty() {
return head == nullptr;
}
void push(T x) {
head = new Node(x, head);
}
T pop() {
if (isEmpty()) {
throw underflow_error("Cannot pop from empty stack");
}
Node* nodeToDelete = head;
T poppedValue = head->data;
head = head->next;
delete nodeToDelete;
return poppedValue;
}
T peek() {
if (isEmpty()) {
throw underflow_error("Cannot peek into empty stack");
}
return head->data;
}
};
Just like the stack example, we attempted to implement move for the queue using the same ideas, including the default move assignment operator. Doing this, we came across problems such as invalid operands errors, collapsed stack frames and more when running our code.
template < typename T >
class Queue {
private:
struct Node {
T data;
Node * next;
Node(T data, Node * next): data(data), next(next) {}
};
private: Node * head;
private: Node * tail;
public:
Queue(): head(nullptr), tail(nullptr) {}~Queue() {
while (head != nullptr) {
Node * old = head;
head = head -> next;
delete old;
}
}
Queue(Queue & ) = delete;
Queue & operator = (Queue & ) = delete;
Queue(Queue && ) = default;
Queue & operator = (Queue && ) = default;
public:
int get_size() {
int count = 0;
for (Node * n = head; n != nullptr; n = n -> next)
count++;
return count;
}
bool isEmpty() {
return head == nullptr;
}
void enqueue(T x) {
Node * temp = new Node {x, nullptr};
if (head == nullptr) {
head = temp;
tail = temp;
} else {
tail -> next = temp;
tail = temp;
}
}
T dequeue() {
if (isEmpty()) {
throw underflow_error("Cannot dequeue from empty queue");
}
Node * nodeToDelete = head;
T poppedValue = head -> data;
head = head -> next;
delete nodeToDelete;
return poppedValue;
}
};
We ended up making our own move assignment operator which the queue worked as expected. The following code is what we ended up with.
Template <typename T>
class Queue {
private:
struct Node {
T data;
Node *next;
Node(T data, Node *next) : data(data), next(next) {}
};
private:
Node *head;
private:
Node *tail;
public:
Queue() : head(nullptr), tail(nullptr) {}
~Queue() {
while (head != nullptr) {
Node *old = head;
head = head->next;
delete old;
}
}
Queue(Queue &) = delete;
Queue &operator=(Queue &) = delete;
Queue(Queue &&) = default;
Queue &operator=(Queue &&other) {
if (&other == this) {
return *this;
}
delete head;
head = other.head;
tail = other.tail;
other.head = nullptr;
other.tail = nullptr;
return *this;
}
public:
int get_size() {
int count = 0;
for (Node *n = head; n != nullptr; n = n->next) {
count++;
}
return count;
}
bool is_empty() {
return head == nullptr;
}
void enqueue(T x) {
Node *temp = new Node{x, nullptr};
if (head == nullptr) {
head = temp;
tail = temp;
} else {
tail->next = temp;
tail = temp;
}
}
T dequeue() {
if (is_empty()) {
throw underflow_error("Cannot dequeue from empty queue");
}
Node *nodeToDelete = head;
T poppedValue = head->data;
head = head->next;
delete nodeToDelete;
return poppedValue;
}
Although we ultimately went with the latter implementation, we were really curious as to why the default move assignment operator worked for the stack and not the queue. We were wondering if it was because we created our own destructor for the class and disabled the copy operator, but we are unsure. Perhaps we were going about the implementation wrong? Any help/ feedback would be much appreciated!
Generally, you should always consider implement your own move assignment operator and move constructor when it comes to dynamic memory allocation.
Your stack one just appeared to work, but actually it is not correct.
Try to add these to your stack, and you will see the problem:
Stack <int> func() {
Stack <int> s;
s.push(3);
return s;
}
int main()
{
Stack <int> b;
b = func(); // assignment move
return 0;
}
func() is returning a Stack with non-null head member variable (this is critical)
When leaving func(), local variable s is destructed
When leaving main(), local variable b is destructed (problem here)
That's why this code causes a double-free problem in your Stack.
(BTW, your Queue assignment move operator may cause memory leak, since delete head; may not be what you want to do.)

Move and copy constructors in a linked list

I am still trying to learn more about copy and move constructors. I have a linked list class that I want to deep copy using copy and move constructors but I'm having issues. First, to deep copy List class, do I only copy head_ and tail_ in the constructors. I know the code is horrendous, maybe I shouldn't jump into high-level stuff right away.
Any help is appreciated!
template<typename T>
class List
{
public:
class Node {
public:
Node(T value) : value_(value) {}
T value_;
Node* next_;
Node* prev_;
};
Node* head_;
Node* tail_;
//! Default constructor
List() :tail_(nullptr) {}
//! Copy constructor
List(const List& lst) : head_(nullptr) {
//not sure what goes in here
}
}
//! Move constructor
List(List&& move) {
head_ = move.head_;
move.head_ = nullptr;
tail_ = move.tail_;
move.tail_ = nullptr;
}
//! Copy assignment operator
List& operator= (const List& list) {
tail_ = nullptr;
head_ = tail_;
Node* current = list.head_;
Node* next = list.head_->next_;
Node* replace = head_;
while (next != list.tail_) {
current = current->next_;
next = next->next_;
replace->next_ = tail_;
replace->next_->value_;
replace = replace->next_;
}
return *this;
}
//! Move assignment operator
List& operator= (List&& other) {
tail_ = nullptr;
head_ = tail_;
head_->next_ = other.head_->next_;
Node* current = other.head_;
Node* next = other.head_->next_;
while (next != other.tail_) {
current = current->next_;
next = next->next_;
}
current->next_ = tail_;
other.head_->next_ = other.tail_;
return *this;
}
Here is my five cents.:)
The demonstrative program below shows how the copy constructor, move constructor, copy assignment operator, move assignment operator, and the destructor can be implemented including some other auxiliary functions.
#include <iostream>
#include <utility>
#include <functional>
#include <iterator>
template<typename T>
class List
{
private:
struct Node
{
T value;
Node *prev;
Node *next;
} *head = nullptr, *tail = nullptr;
void copy( const List &list )
{
if ( list.head )
{
head = tail = new Node { list.head->value, nullptr, nullptr };
for ( Node *current = list.head->next; current; current = current->next )
{
tail = tail->next = new Node { current->value, tail, nullptr };
}
}
}
public:
//! Default constructor
List() = default;
//! Copy constructor
List( const List &list )
{
copy( list );
}
// Constructor with iterators
template <typename InputIterator>
List( InputIterator first, InputIterator last )
{
if ( first != last )
{
head = tail = new Node { *first, nullptr, nullptr };
while ( ++first != last )
{
tail = tail->next = new Node { *first, tail, nullptr };
}
}
}
// Destructor
~List()
{
clear();
}
//! Move constructor
List( List &&list )
{
std::swap( head, list.head );
std::swap( tail, list.tail );
}
//! Copy assignment operator
List & operator =( const List &list )
{
clear();
copy( list );
return *this;
}
//! Move assignment operator
List & operator =( List &&list )
{
std::swap( head, list.head );
std::swap( tail, list.tail );
return *this;
}
void clear()
{
while ( head )
{
delete std::exchange( head, head->next );
}
tail = head;
}
void push_front( const T &value )
{
head = new Node{ value, nullptr, head };
if ( !tail )
{
tail = head;
}
else
{
head->next->prev = head;
}
}
void push_back( const T &value )
{
Node *new_node = new Node{ value, tail, nullptr };
if ( tail )
{
tail = tail->next = new_node;
}
else
{
head = tail = new_node;
}
}
friend std::ostream & operator <<( std::ostream &os, const List &list )
{
for ( Node *current = list.head; current; current = current->next )
{
os << current->value << " -> ";
}
return os << "null";
}
};
int main()
{
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
List<int> list1( std::begin( a ), std::end( a ) );
std::cout << list1 << '\n';
list1 = List<int>( std::rbegin( a ), std::rend( a ) );
std::cout << list1 << '\n';
}
The program output is
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
For example in this statement
list1 = List<int>( std::rbegin( a ), std::rend( a ) );
there is used the move assignment operator.
Because List tracks the tail, you can call a function that adds items to the end of the list without the added overhead of iterating to the end of the list.
List(const List& lst) : head_(nullptr), tail_(nullptr)
{
Node * cur = lst.head_; //get first source item.
while (cur) // if there is a source item to copy
{
push_back(cur->value_); // stick the item on the end of this list
cur = cur->next_; // get next source item
}
}
where push_back looks something like
void push_back(T value)
{
Node * newnode = new Node(value, tail_, nullptr); //make and link new tail node
if (tail_)
{
tail_->next_ = newnode; // link in new node
}
else
{
head_ = newnode;
}
tail_ = newnode; // update tail
}
and Node picked up a new constructor to simplify insertion:
Node(T value,
Node * prev,
Node * next) : value_(value), prev_(prev), next_(next)
{
}
Note:
In the assignment operators,
tail_ = nullptr;
head_ = tail_;
cut the pointers to any data that was in the linked list, leaking those Nodes. You need to free these nodes before replacing them. The Copy and Swap Idiom (What is the copy-and-swap idiom?) makes this easy by using the copy construction and destruction of a local variable to automate the process.

How to implement a copy assignment on a doubly linked List?

Im a little confused on how to implement an copy assignment on a doubly linked List. I managed to get the copy constructor working but im sure on the assignment. Im trying to do this without the copy and swap method.
List.H
class List
{
public:
List();
~List();
List(const List& c);
List& operator= (const List& t);
private:
List *Next;
List *Prev;
Node *Head;
List.cpp
List::~List()
{
Node* move = Head;
while (move!=NULL)
{
Node *temp = move->Next;
delete move;
move = temp;
}
}
List::List(const List& c)
{
name = c.name;
Prev = c.Prev;
Next = c.Next;
Node* dummy, * current;
Head= dummy = new Node();
current = c.Head;
while (current)
{
dummy->Next = new Node(*current);
current = current->Next;
dummy = dummy->Next;
}
Node* temp = Head;
Head = Head->Next;
delete temp;
}
List& List::operator=(const List& t)
{
Next = t.Next;
return *this;
}
Would I also have to traverse each node in the assignment operator as well?
Edit
So this is what I have now. The problem is when im getting the data from the list it is null.
List& List::operator=(const List& that)
{
if (this != &that)
{
while (Head)
{
Node* temp = Head;
Head = Head->Next;
delete temp;
}
Node* dummy, * current;
Head = dummy = new Node();
current = that.Head;
while (current)
{
dummy->Next = new Node(*current);
current = current->Next;
dummy = dummy->Next;
}
dummy->Next = nullptr;
}
return *this;
}
Short answer is YES.
copy-constructor
List L1;
List L2(L1);
operator=
List L1;
List L2;
L2 = L1;
In both the cases, L1 has to be copied to L2 and L1 should be unchanged after copy or assignment. Hence content of every node has to be replicated to newly created node.
Copy Constructor looks something like this:
List::List(const List& c)
{
Node start;
Node* dummy = &start;
Node* CurrentNode = c.Head;
while (CurrentNode)
{
dummy->next = new Node(*CurrentNode);//New node created with content of *CurrentNode
dummy = dummy->Next;
CurrentNode = CurrentNode->Next;
}
dummy->next = nullptr;
Head = start.next;
}
And assignment operator like this:
List& List::operator=(const List& that)
{
if (this != &that) //avoid self assignment like List L1;L1=L1;
{
while (Head)//Delete exist nodes
{
Node* temp = Head;
Head = Head->Next
delete temp;
}
Node start;
Node* dummy = &start;
Node* thatHead = that.Head;
while (thatHead)
{
dummy->next = new Node(*thatHead);//New node created with content of *thatHead
dummy = dummy->Next;
thatHead = thatHead->Next;
}
dummy->next = nullptr;
}
return *this;
}

C++ memory leak in linked-list push_front and push_back [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'm getting this memory leak error by valgrind:
24 bytes in 1 blocks are definitely lost in loss record 1 of 11
at 0x4C2C21F: operator new(unsigned long) (vg_replace_malloc.c:334)
by 0x413E47: pjc::list::push_back(double) (list.cpp:33)
by 0x416371: ____C_A_T_C_H____T_E_S_T____4() (tests-list-01.cpp:86)
24 bytes in 1 blocks are definitely lost in loss record 2 of 11
at 0x4C2C21F: operator new(unsigned long) (vg_replace_malloc.c:334)
by 0x414047: pjc::list::push_front(double) (list.cpp:66)
by 0x4192C1: ____C_A_T_C_H____T_E_S_T____10() (tests-list-01.cpp:146)
My .hpp file for linked list looks like this:
using std::size_t;
namespace pjc {
class list {
private:
struct node {
double val = 0;
node* prev = nullptr;
node* next = nullptr;
};
node* head = nullptr;
node* tail = nullptr;
size_t num_elements = 0;
public:
list() = default;
list(const list& rhs);
list& operator=(const list& rhs);
list(list&& rhs);
list& operator=(list&& rhs);
~list();
void push_back(double elem);
void push_front(double elem);
};
And definitions of push_back(), push_front() and the destructor of the linked-list look like this:
list::list(const list &rhs) {
head = tail = nullptr;
for(node* tmp = rhs.head; tmp!=NULL; tmp=tmp->next) {
push_back(tmp->val);
}
num_elements = rhs.num_elements;
}
list::~list() {
node *T = head;
while(T != nullptr)
{
node *T2 = T;
T = T->next;
delete T2;
}
head = nullptr;
tail = nullptr;
num_elements = 0;
}
void list::push_back(double elem) {
node *n = new node;
n->val = elem;
if(tail == nullptr)
{
head = n;
tail = head;
}
else
{
tail->next = n;
n->prev = tail;
tail = n;
}
num_elements++;
}
void list::push_front(double elem) {
node *n = new node;
n->val = elem;
if(head == nullptr)
{
head = n;
tail = head;
}
else
{
head->prev = n;
n->next = head;
head = n;
}
num_elements++;
}
list &list::operator=(const list &rhs) {
list temp(rhs);
std::swap(head, temp.head);
std::swap(tail, temp.tail);
std::swap(num_elements, temp.num_elements);
return *this;
}
list::list(list &&rhs) {
head = rhs.head;
tail = rhs.tail;
num_elements = rhs.num_elements;
rhs.head = nullptr;
rhs.tail = nullptr;
rhs.num_elements = 0;
}
list &list::operator=(list &&rhs) {
this->~list(); // Destroy our current contents
std::swap(head, rhs.head);
std::swap(tail, rhs.tail);
std::swap(num_elements, rhs.num_elements);
return *this;
}
I tried to change the destructor, but it seem to be OK. I really don't have and ideas whatsoever where the leak happens.
EDIT: Sorry, i left out some important parts of the code for the first time. Now it should follow the rule of 5.
I see nothing in the code you have shown that leaks, but you have not shown all of your relevant code.
For instance, HOW you use list objects may be contributing to the cause of the leak. For instance, if you are not following the Rule of 3/5/0 by implementing proper copy and move constructors, and copy and move assignment operators, then you can leak memory when copying/moving list objects. But you did not show that code, so we can't tell if you are doing things correctly or not.
That being said, your destructor does have an extra delete that does not belong, and your push_back() and push_front() methods can be simplified.
The safest option is to simply use std::list and let it manage memory for you. But, if you want to do it manually, then try this:
class list
{
private:
struct node
{
double val;
node* prev = nullptr;
node* next = nullptr;
node(double value = 0) : val(value) {}
};
node* head = nullptr;
node* tail = nullptr;
size_t num_elements = 0;
public:
list() = default;
list(const list &src);
list(list &&src);
~list();
list& operator=(const list &rhs);
list& operator=(list &&rhs);
void push_back(double elem);
void push_front(double elem);
void swap(list &other)
};
list::list(const list &src)
: list()
{
for(node *n = src.head; n != nullptr; n = n->next)
push_back(n->val);
}
list::list(list &&src)
: list()
{
src.swap(*this);
}
list::~list()
{
node *n = head;
while (n)
{
node *next = n->next;
delete n;
n = next;
}
}
list& list::operator=(const list &rhs)
{
if (this != &rhs)
list(rhs).swap(*this);
return *this;
}
list& operator=(list &&rhs)
{
list(std::move(rhs)).swap(*this);
return *this;
}
void list::push_back(double elem)
{
node *n = new node(elem);
if (tail)
{
tail->next = n;
n->prev = tail;
}
else
head = n;
tail = n;
++num_elements;
}
void list::push_front(double elem)
{
node *n = new node(elem);
if (head)
{
head->prev = n;
n->next = head;
}
else
tail = n;
head = n;
++num_elements;
}
void list::swap(list &other)
{
std::swap(head, other.head);
std::swap(tail, other.tail);
std::swap(num_elements, other.num_elements);
}
Maybe this will help you:
template<typename T>
class List {
private:
Node<T>* head = nullptr;
Node<T>* tail = nullptr;
std::size_t _size = 0;
public:
List() = default;
// copy constructor
List(const List<T>& l) {
_size = l._size;
Node<T>* current = nullptr;
Node<T>* previous = nullptr;
for (std::size_t i = 0; i < l._size; ++i) {
current = new Node<T>(l[i].data);
current->prev = previous;
if (previous) {
previous->next = current;
} else {
head = current;
}
previous = current;
}
tail = current;
}
// assignment operator
List<T>& operator=(const List<T>& l) {
if (l.isEmpty()) {
this->clear();
return *this;
}
// keeps existing nodes intact, and only changes their value
while (_size > l.size()) {
Node<T>* prev = tail->prev;
delete tail;
prev->next = nullptr;
tail = prev;
--_size;
}
Node<T>* temp = head;
Node<T>* tempL = l.head;
for (std::size_t i = 0; i < _size; ++i) {
temp->data = tempL->data;
temp = temp->next;
tempL = tempL->next;
}
while (_size < l._size) {
this->append(tempL->data);
tempL = tempL->next;
++_size;
}
return *this;
}
~List() {
Node<T>* temp = head;
while (temp) {
Node<T>* next = temp->next;
delete temp;
temp = next;
}
}
void append(const T& value) {
auto* temp = new Node<T>(value);
if (!head) {
// no head also means no tail
head = temp;
tail = temp;
} else {
tail->next = temp;
temp->prev = tail;
tail = temp;
}
++_size;
}
void prepend(const T& value) {
auto* temp = new Node<T>(value);
temp->next = head;
if (head) {
head->prev = temp;
}
head = temp;
++_size;
}
};
That said, you should probably follow the Rule of Three and implement a copy constructor and assignment operator.

Linked list operator= When Move Assigning Then All Elements Are Moved

I'm trying to write Lined list in C++, but some tests fails.
One of those says:
GivenNonEmptyCollection_WhenMoveAssigning_ThenAllElementsAreMoved
And second:
GivenNonEmptyCollection_WhenMovingToOther_ThenAllItemsAreMoved
Here's how I implement operator=
LinkedList& operator=(const LinkedList& other)
{
if(this!=&other)
{
while (!isEmpty())
erase(begin());
for (auto it = other.begin(); it != other.end(); it++)
append(*it);
}
return *this;}
And second one:
LinkedList& operator=(LinkedList&& other)
{
/* SELF ASSIGNMENT CHECK */
if(this!=&other)
{
while (!isEmpty())
erase(begin());
while (!other.isEmpty())
{
append(*(other.begin()));
other.erase(other.begin());
}
}
return *this;
}
Here's something about class Linked list and struct Node:
template <typename Type>
class LinkedList
{
struct Node
{
Node* prev;
Node* next;
Type* data;
Node()
{
data = nullptr;
prev = nullptr;
next = nullptr;
}
Node(const Type val)
{
data = new Type(val);
prev = nullptr;
next = nullptr;
}
~Node()
{
prev = nullptr;
next = nullptr;
delete data;
}
};
private:
Node *head;
Node *tail;
size_type length;
public:
LinkedList(): head(nullptr), tail(nullptr), length(0)
{
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
(...)
I have no idea what's wrong with that.
You're copying and deleting the original list, but you should move it.
In this case, this means "stealing" the data from the other list.
It should look more like this:
LinkedList& operator=(LinkedList&& other)
{
if(this!=&other)
{
// Assuming the existence of 'LinkedList::clear', which empties the list.
// Replace with the name you chose for that function.
clear();
head = other.head;
other.head = nullptr;
tail = other.tail;
other.tail = nullptr;
length = other.length;
other.length = 0;
}
return *this;
}
and your move constructor should be changed similarly.