I have this code, and I have to create a copy constructor for it to create a deep copy of the passed object. How can I create that?
template<typename T>
class SSL {
struct Node {
T data;
Node* next;
};
Node* head = nullptr;
public:
// ...
};
You simply need to iterate the source object's list, making new nodes that have copies of its data, eg:
template <typename T>
class SSL {
struct Node {
T data;
Node* next = nullptr;
Node(const T &value) : data(value) {}
};
Node* head = nullptr;
public:
// ...
SSL() = default;
SSL(const SSL &src) {
Node **n = &head;
for (Node *cur = src.head; cur; cur = cur->next) {
*n = new Node{cur->data};
n = &(n->next);
}
}
SSL(SSL &&src) : head(src.head) {
src.head = nullptr;
};
~SSL() {
Node *cur = head;
while (cur) {
Node *n = cur;
cur = cur->next;
delete n;
}
}
SSL& operator=(SSL rhs) {
std::swap(head, rhs.head);
return *this;
}
// ...
};
Related
I am currently learning Linked Lists and have implemented a singly linked list with Append and Prepend methods where I have allocated objects of type Node on heap using the 'new' operator. Do I need to deallocate the object on heap using 'delete', and if so then how do I do it ?
Here is my code:-
class List
{
private:
class Node
{
public:
int data;
Node* next;
Node()
{
data = 0;
next = NULL;
}
Node(const int& data)
{
this->data = data;
}
};
Node* head;
public:
List()
{
head = NULL;
}
void Append(const int&val);
void Prepend(const int&val);
void DisplayAll();
};
void List::Append(const int&val)
{
Node* n = new Node(val); //dynamically allocated
if (head == NULL)
{
head = n;
return;
}
Node* temp = NULL;
temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = n;
}
void List::Prepend(const int&val)
{
Node* node = new Node(val);//dynamically allocated
if (head == NULL)
{
head = node;
return;
}
node->next = head;
head = node;
}
void List::DisplayAll()
{
Node* temp = head;
while (temp != NULL)
{
std::cout << temp->data << ' ';
temp = temp->next;
}
}
For starters this constructor
Node(const int& data)
{
this->data = data;
}
does not initialize the data member next. As a result member functions Append and Prepend have a bug
void List::Append(const int&val)
{
Node* n = new Node(val); //dynamically allocated
if (head == NULL)
{
head = n;
return;
}
//...
and
void List::Prepend(const int&val)
{
Node* node = new Node(val);//dynamically allocated
if (head == NULL)
{
head = node;
return;
}
//...
The data member next of the head node has an indeterminate value.
You could declare the class Node simpler like
struct Node
{
int data;
Node* next;
};
Node* head = nullptr;
In this case for example the function Prepend will look like
void List::Prepend( const int &val )
{
head = new Node { val, head };
}
And the constructor will look like
List() = default;
To free all allocated nodes in the list you could write two more member functions clear and the destructor that calls the function clear.
For example
#include <functional>
//...
class List
{
//...
public:
void clear()
{
while ( head ) delete std::exchange( head, head->next );
}
~List() { clear(); }
//...
Also you should at least either write a copy constructor and the copy assignment operator or define them as deleted.
I have written the following link list append, prepend and print methods for LinkedList:
class Node {
public:
int data;
Node *next;
Node(int data) {
this->data = data;
}
};
class LinkedList {
private:
Node *header;
Node *tail;
int size;
public:
LinkedList() {
header = NULL;
tail = NULL;
size = 0;
}
int getSize() {
return size;
}
void append(int data) {
Node *n = new Node(data);
if (header == NULL) {
header = n;
tail = n;
}
else {
tail->next = n;
tail = n;
}
size++;
}
void prepend(int data) {
Node *n = new Node(data);
if (header == NULL) {
header = n;
tail = n;
}
else {
Node *temp = header;
header = n;
n->next = temp;
}
size++;
}
void toString() {
Node *temp = header;
while (temp != NULL) {
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
};
int main()
{
LinkedList list;
list.append(1);
list.append(2);
list.append(3);
list.toString();
return 0;
}
But the program is giving bad access after reaching the last node while printing the list. What is the problem with this code? Can anyone please explain on this?
append() is not initializing the n->next member to NULL. You should do that initialization in Node's constructor, eg:
class Node {
public:
int data;
Node *next;
Node(int data) {
this->data = data;
this->next = NULL; // <-- add this!
}
};
Alternatively:
class Node {
public:
int data;
Node *next = nullptr;
Node(int data) : data(data) {}
};
Or:
class Node {
public:
int data;
Node *next;
Node(int data, Node *next = nullptr) : data(data), next(next) {}
};
That being said, your LinkedList is leaking nodes. You need to add a destructor to free the nodes. You should also add a copy constructor and a copy assignment operator, per the Rule of 3. And in C++11 and later, add a move constructor and a move assignment operator, per the Rule of 5.
Try this:
class Node {
public:
int data;
Node *next;
Node(int data, Node *next = nullptr) : data(data), next(next) {}
};
class LinkedList {
private:
Node *header = nullptr;
Node *tail = nullptr;
int size = 0;
public:
LinkedList() = default;
LinkedList(const LinkedList &src) : LinkedList() {
Node *temp = src.header;
while (temp) {
append(temp->data);
temp = temp->next;
}
}
LinkedList(LinkedList &&src) : LinkedList() {
std::swap(header, src.header);
std::swap(tail, src.tail);
std::swap(size, src.size);
}
~LinkedList() {
Node *temp = header;
while (temp) {
Node *n = temp->next;
delete temp;
temp = n;
}
}
LinkedList& operator=(LinkedList src) {
std::swap(header, src.header);
std::swap(tail, src.tail);
std::swap(size, src.size);
return *this;
}
int getSize() const {
return size;
}
void append(int data) {
Node **temp = (tail) ? &(tail->next) : &header;
*temp = new Node(data);
tail = *temp;
++size;
}
void prepend(int data) {
header = new Node(data, header);
if (!tail) tail = header;
++size;
}
void toString() const {
Node *temp = header;
while (temp) {
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
};
int main()
{
LinkedList list;
list.append(1);
list.append(2);
list.append(3);
list.toString();
return 0;
}
You should always initialize the members of your class.
Node(int data)
: data(data)
, next(nullptr)
{
}
You've forgotten to assign n->next in append(). I suggest that you add an argument to your Node constructor to not forget that:
class Node {
public:
int data;
Node *next;
// make the constructor initialize both "data" and "next"
Node(int data, Node* next) :
data(data),
next(next)
{}
};
With that, your append() and prepend() functions would have to supply the next node upon construction, making mistakes like this a little harder:
void append(int data) {
Node *n = new Node(data, nullptr);
if (header == nullptr) header = n;
else tail->next = n;
tail = n;
++size;
}
void prepend(int data) {
Node *n = new Node(data, header);
if (header == nullptr) tail = n;
header = n;
++size;
}
You also need to add a destructor to delete all the nodes to not leak memory.
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.)
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.
template<typename T>
LinkedStack<T>::LinkedStack(const LinkedStack<T> &other) {
Node<T> *temp = other.stktop;
if (other.stktop != nullptr) {
Node<T> *newnode = new Node<T>;
newnode = temp;
while (temp->next != nullptr) {
temp = temp->next;
newnode->next = temp;
}
}
}
This is my copy constructor.
template <typename U>
struct Node {
U data;
Node<U> *next;
};
Node<T> *stktop;
and this is my struct node located in private section of my LinkedStack Class.
I tried following the logic for writing the copy constructors and I came up with this code.However it doesn't work.Any idea what is wrong?
Something like that:
template<typename T>
LinkedStack<T>::LinkedStack(const LinkedStack<T> &other)
{
// Create first node
if (!other.stktop) // Use pointer to bool cast
{
stktop = new Node<T>;
// Copy data. This is the simplest copy if U support it.
stktop->data = other.stktop->data;
}
else
{
stktop = nullptr; // Very important otherwise its value is undefined
return;
}
auto pothernode = other.stktop;
auto pthisnode = stktop;
// Iterate overall the elements
while (pothernode->next)
{
pthisnode->next = new Node<T>;
pthisnode->next->data = pothernode->next->data;
pothernode = pothernode->next;
pthisnode = pthisnode->next;
}
}
Note that all the nodes are allocated on the heap so do not forget to delete them. I suggest to use std::unique_ptr or std::shared_ptr instead of pure pointer.
Some possible optimization:
template<typename T>
LinkedStack<T>::LinkedStack(const LinkedStack<T> &other)
{
const auto& pothernode = other.stktop;
auto& pthisnode = stktop;
pthisnode = nullptr;
// Iterate overall the elements
while (pothernode)
{
pthisnode = new Node<T>;
pthisnode->data = pothernode->data;
pothernode = pothernode->next;
pthisnode = pthisnode->next;
}
}