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.)
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 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;
}
// ...
};
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.
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.
I wrote this implementation of Linked list:
template<typename T> // implementation: Linked_list
class Linked_list {
private:
Node<T>* head;
Node<T>* tail;
Node<T>* current;
int size;
void init()
{
head = tail = current = new Node<T>();
size = 0;
}
Node<T>* search_previous()
{
if (current == head) {
return nullptr;
}
Node<T>* previous_node = head;
while (previous_node->next != current) {
previous_node = previous_node->next;
}
return previous_node;
}
public:
Linked_list()
{
init();
}
void clear()
{
while (head != nullptr) {
current = head;
head = head->next;
delete current;
}
init();
}
~Linked_list()
{
clear();
delete head;
}
void append(T p_element)
{
tail->next = new Node<T>(p_element);
tail = tail->next;
++size;
}
void insert(T p_element)
{
current->next = new Node<T>(p_element, current->next);
if (current == tail) {
tail = tail->next;
}
++size;
}
T remove()
{
if (current->next == nullptr) {
throw std::runtime_error("No element to remove");
}
T removed_element = current->next->element;
Node<T>* temporary_pointer = current->next;
current->next = current->next->next;
if (temporary_pointer == tail) {
tail = current;
}
delete temporary_pointer;
--size;
return removed_element;
}
T get_element()
{
if (current->next == nullptr) {
throw std::runtime_error("No element to get");
}
return current->next->element;
}
void go_to_start()
{
current = head;
}
void go_to_end()
{
current = tail;
}
void go_to_pos(int p_pos)
{
if ((p_pos < 0) || (p_pos >= size)) {
throw std::runtime_error("Index out of bounds");
}
current = head;
for (int index = 0; index < p_pos; ++index) {
current = current->next;
}
}
void next()
{
if (current != tail) {
current = current->next;
}
else {
throw std::runtime_error("There's no next positition");
}
}
void previous()
{
if (current != head) {
current = search_previous();
}
else {
throw std::runtime_error("There's no previous positition");
}
}
int get_pos()
{
int pos = 0;
Node<T>* temporary_pointer = head;
while (temporary_pointer != current) {
temporary_pointer = temporary_pointer->next;
++pos;
}
return pos;
}
int get_size()
{
return size;
}
void concat(Linked_list<T> p_list)
{
for (p_list.go_to_start(); p_list.get_pos() < p_list.get_size(); p_list.next()) {
append(p_list.get_element());
}
}
};
And here's the node:
template<typename T>
class Node {
public:
T element;
Node<T>* next;
Node(T p_element, Node<T>* p_next = nullptr)
{
element = p_element;
next = p_next;
}
Node(Node<T>* p_next = nullptr)
{
next = p_next;
}
};
The problem that I have is that when I try to use the method concat I get this message from Clang:
proofs(13417,0x7fff7bb9f000) malloc: * error for object 0x7fe10b603170: pointer being freed was not allocated
* set a breakpoint in malloc_error_break to debug
Abort trap: 6
What can I do for fix it?
The obvious error is this:
void concat(Linked_list<T> p_list)
You are passing a Linked_list by value. That means that a temporary copy of the linked list is created and destroyed. Since the destructor deletes the memory, it is also deleting the memory of the linked list that you are making the copy of.
Since your Linked_list class does not have a user-defined assignment operator or copy constructor to handle the members that point to dynamically allocated memory, the class cannot be safely copied (if you debugged, you should have seen that a destructor was called that you didn't expect, and that is the temporary being destroyed, thus corrupting the original object).
To prevent this, either pass by reference (not value),
void concat(Linked_list<T>& p_list)
or provide appropriate copy constructor and assignment operator.
See What is the rule of Three