fix: remove memory leak in queue

This commit is contained in:
piotr.idzik 2023-01-11 17:03:58 +01:00
parent e2bf654e82
commit effd74c9ae
6 changed files with 226 additions and 160 deletions

48
data_structures/node.hpp Normal file
View File

@ -0,0 +1,48 @@
/**
* @file
* @brief Provides Node class and related utilities
**/
#ifndef DATA_STRUCTURES_NODE_HPP_
#define DATA_STRUCTURES_NODE_HPP_
#include <iostream> /// for std::cout
#include <memory> /// for std::shared_ptr
#include <vector> /// for std::vector
/** Definition of the node as a linked-list
* \tparam ValueType type of data nodes of the linked list should contain
*/
template <class ValueType>
struct Node {
using value_type = ValueType;
ValueType data = {};
std::shared_ptr<Node<ValueType>> next = {};
};
template <typename Node, typename Action>
void traverse(const Node* const inNode, const Action& action) {
if (inNode) {
action(*inNode);
traverse(inNode->next.get(), action);
}
}
template <typename Node>
void display_all(const Node* const inNode) {
traverse(inNode,
[](const Node& curNode) { std::cout << curNode.data << " "; });
}
template <typename Node>
std::vector<typename Node::value_type> push_all_to_vector(
const Node* const inNode, const std::size_t expected_size=0) {
std::vector<typename Node::value_type> res;
if (expected_size != 0) {
res.reserve(expected_size);
}
traverse(inNode,
[&res](const Node& curNode) { res.push_back(curNode.data); });
return res;
}
#endif // DATA_STRUCTURES_NODE_HPP_

View File

@ -1,88 +0,0 @@
/* This class specifies the basic operation on a queue as a linked list */
#ifndef DATA_STRUCTURES_QUEUE_H_
#define DATA_STRUCTURES_QUEUE_H_
#include <cassert>
#include <iostream>
/** Definition of the node */
template <class Kind>
struct node {
Kind data;
node<Kind> *next;
};
/** Definition of the queue class */
template <class Kind>
class queue {
public:
/** Show queue */
void display() {
node<Kind> *current = queueFront;
std::cout << "Front --> ";
while (current != NULL) {
std::cout << current->data << " ";
current = current->next;
}
std::cout << std::endl;
std::cout << "Size of queue: " << size << std::endl;
}
/** Default constructor*/
queue() {
queueFront = NULL;
queueRear = NULL;
size = 0;
}
/** Destructor */
~queue() {}
/** Determine whether the queue is empty */
bool isEmptyQueue() { return (queueFront == NULL); }
/** Add new item to the queue */
void enQueue(Kind item) {
node<Kind> *newNode;
newNode = new node<Kind>;
newNode->data = item;
newNode->next = NULL;
if (queueFront == NULL) {
queueFront = newNode;
queueRear = newNode;
} else {
queueRear->next = newNode;
queueRear = queueRear->next;
}
size++;
}
/** Return the first element of the queue */
Kind front() {
assert(queueFront != NULL);
return queueFront->data;
}
/** Remove the top element of the queue */
void deQueue() {
node<Kind> *temp;
if (!isEmptyQueue()) {
temp = queueFront;
queueFront = queueFront->next;
delete temp;
size--;
} else {
std::cout << "Queue is empty !" << std::endl;
}
}
/** Clear queue */
void clear() { queueFront = NULL; }
private:
node<Kind> *queueFront; /**< Pointer to the front of the queue */
node<Kind> *queueRear; /**< Pointer to the rear of the queue */
int size;
};
#endif // DATA_STRUCTURES_QUEUE_H_

80
data_structures/queue.hpp Normal file
View File

@ -0,0 +1,80 @@
/* This class specifies the basic operation on a queue as a linked list */
#ifndef DATA_STRUCTURES_QUEUE_HPP_
#define DATA_STRUCTURES_QUEUE_HPP_
#include "node.hpp"
/** Definition of the queue class */
template <class ValueType>
class queue {
using node_type = Node<ValueType>;
public:
using value_type = ValueType;
/** Show queue */
void display() const {
std::cout << "Front --> ";
display_all(this->queueFront.get());
std::cout << '\n';
std::cout << "Size of queue: " << size << '\n';
}
std::vector<value_type> toVector() const {
return push_all_to_vector(this->queueFront.get(), this->size);
}
private:
void ensureNotEmpty() const {
if (isEmptyQueue()) {
throw std::invalid_argument("Stack is empty.");
}
}
public:
/** Determine whether the queue is empty */
bool isEmptyQueue() const { return (queueFront == nullptr); }
/** Add new item to the queue */
void enQueue(const value_type& item) {
auto newNode = std::make_shared<node_type>();
newNode->data = item;
newNode->next = nullptr;
if (isEmptyQueue()) {
queueFront = newNode;
queueRear = newNode;
} else {
queueRear->next = newNode;
queueRear = queueRear->next;
}
size++;
}
/** Return the first element of the queue */
ValueType front() const {
ensureNotEmpty();
return queueFront->data;
}
/** Remove the top element of the queue */
void deQueue() {
ensureNotEmpty();
queueFront = queueFront->next;
size--;
}
/** Clear queue */
void clear() {
queueFront = nullptr;
queueRear = nullptr;
size = 0;
}
private:
std::shared_ptr<node_type> queueFront =
{}; /**< Pointer to the front of the queue */
std::shared_ptr<node_type> queueRear =
{}; /**< Pointer to the rear of the queue */
std::size_t size = 0;
};
#endif // DATA_STRUCTURES_QUEUE_HPP_

View File

@ -7,28 +7,9 @@
#ifndef DATA_STRUCTURES_STACK_HPP_ #ifndef DATA_STRUCTURES_STACK_HPP_
#define DATA_STRUCTURES_STACK_HPP_ #define DATA_STRUCTURES_STACK_HPP_
#include <iostream> /// for IO operations
#include <memory> /// for std::shared_ptr
#include <stdexcept> /// for std::invalid_argument #include <stdexcept> /// for std::invalid_argument
#include <vector> /// for std::vector
/** Definition of the node as a linked-list #include "node.hpp" /// for Node
* \tparam ValueType type of data nodes of the linked list should contain
*/
template <class ValueType>
struct node {
ValueType data = {}; ///< data at current node
std::shared_ptr<node<ValueType>> next =
{}; ///< pointer to the next ::node instance
};
template <typename Node, typename Action>
void traverse(const Node* const inNode, const Action& action) {
if (inNode) {
action(*inNode);
traverse(inNode->next.get(), action);
}
}
/** Definition of the stack class /** Definition of the stack class
* \tparam value_type type of data nodes of the linked list in the stack should * \tparam value_type type of data nodes of the linked list in the stack should
@ -42,20 +23,13 @@ class stack {
/** Show stack */ /** Show stack */
void display() const { void display() const {
std::cout << "Top --> "; std::cout << "Top --> ";
traverse(stackTop.get(), [](const node<value_type>& inNode) { display_all(this->stackTop.get());
std::cout << inNode.data << " "; std::cout << '\n';
});
std::cout << std::endl;
std::cout << "Size of stack: " << size << std::endl; std::cout << "Size of stack: " << size << std::endl;
} }
std::vector<value_type> toVector() const { std::vector<value_type> toVector() const {
std::vector<value_type> res; return push_all_to_vector(this->stackTop.get(), this->size);
res.reserve(this->size);
traverse(stackTop.get(), [&res](const node<value_type>& inNode) {
res.push_back(inNode.data);
});
return res;
} }
private: private:
@ -71,7 +45,7 @@ class stack {
/** Add new item to the stack */ /** Add new item to the stack */
void push(const value_type& item) { void push(const value_type& item) {
auto newNode = std::make_shared<node<value_type>>(); auto newNode = std::make_shared<Node<value_type>>();
newNode->data = item; newNode->data = item;
newNode->next = stackTop; newNode->next = stackTop;
stackTop = newNode; stackTop = newNode;
@ -98,7 +72,7 @@ class stack {
} }
private: private:
std::shared_ptr<node<value_type>> stackTop = std::shared_ptr<Node<value_type>> stackTop =
{}; /**< Pointer to the stack */ {}; /**< Pointer to the stack */
std::size_t size = 0; ///< size of stack std::size_t size = 0; ///< size of stack
}; };

View File

@ -1,41 +1,93 @@
#include <iostream> #include <cassert> /// for assert
#include <string> #include <iostream> /// for std::cout
#include "./queue.h" #include "./queue.hpp"
template <typename T>
void testConstructedQueueIsEmpty() {
const queue<T> curQueue;
assert(curQueue.isEmptyQueue());
}
void testEnQueue() {
queue<int> curQueue;
curQueue.enQueue(10);
assert(curQueue.toVector() == std::vector<int>({10}));
curQueue.enQueue(20);
assert(curQueue.toVector() == std::vector<int>({10, 20}));
curQueue.enQueue(30);
curQueue.enQueue(40);
assert(curQueue.toVector() == std::vector<int>({10, 20, 30, 40}));
}
void testDeQueue() {
queue<int> curQueue;
curQueue.enQueue(10);
curQueue.enQueue(20);
curQueue.enQueue(30);
curQueue.deQueue();
assert(curQueue.toVector() == std::vector<int>({20, 30}));
curQueue.deQueue();
assert(curQueue.toVector() == std::vector<int>({30}));
curQueue.deQueue();
assert(curQueue.isEmptyQueue());
}
void testFront() {
queue<int> curQueue;
curQueue.enQueue(10);
assert(curQueue.front() == 10);
curQueue.enQueue(20);
assert(curQueue.front() == 10);
}
void testQueueAfterClearIsEmpty() {
queue<int> curQueue;
curQueue.enQueue(10);
curQueue.enQueue(20);
curQueue.enQueue(30);
curQueue.clear();
assert(curQueue.isEmptyQueue());
}
void testFrontThrowsAnInvalidArgumentWhenQueueEmpty() {
const queue<int> curQueue;
bool wasException = false;
try {
curQueue.front();
} catch (const std::invalid_argument&) {
wasException = true;
}
assert(wasException);
}
void testDeQueueThrowsAnInvalidArgumentWhenQueueEmpty() {
queue<int> curQueue;
bool wasException = false;
try {
curQueue.deQueue();
} catch (const std::invalid_argument&) {
wasException = true;
}
assert(wasException);
}
int main() { int main() {
queue<std::string> q; testConstructedQueueIsEmpty<int>();
std::cout << "---------------------- Test construct ----------------------" testConstructedQueueIsEmpty<double>();
<< std::endl; testConstructedQueueIsEmpty<std::vector<long double>>();
q.display();
std::cout testEnQueue();
<< "---------------------- Test isEmptyQueue ----------------------" testDeQueue();
<< std::endl;
if (q.isEmptyQueue()) testQueueAfterClearIsEmpty();
std::cout << "PASS" << std::endl;
else testFrontThrowsAnInvalidArgumentWhenQueueEmpty();
std::cout << "FAIL" << std::endl; testDeQueueThrowsAnInvalidArgumentWhenQueueEmpty();
std::cout << "---------------------- Test enQueue ----------------------"
<< std::endl; std::cout << "All tests pass!\n";
std::cout << "After Hai, Jeff, Tom, Jkingston go into queue: " << std::endl;
q.enQueue("Hai");
q.enQueue("Jeff");
q.enQueue("Tom");
q.enQueue("Jkingston");
q.display();
std::cout << "---------------------- Test front ----------------------"
<< std::endl;
std::string value = q.front();
if (value == "Hai")
std::cout << "PASS" << std::endl;
else
std::cout << "FAIL" << std::endl;
std::cout << "---------------------- Test deQueue ----------------------"
<< std::endl;
q.display();
q.deQueue();
q.deQueue();
std::cout << "After Hai, Jeff left the queue: " << std::endl;
q.display();
return 0; return 0;
} }

View File

@ -157,7 +157,7 @@ void testAssign() {
assert(stackB.toVector() == otherExpectedDataB); assert(stackB.toVector() == otherExpectedDataB);
} }
void testTopThrowsAnvalidArgumentWhenStackEmpty() { void testTopThrowsAnInvalidArgumentWhenStackEmpty() {
const stack<long double> curStack; const stack<long double> curStack;
bool wasException = false; bool wasException = false;
try { try {
@ -168,7 +168,7 @@ void testTopThrowsAnvalidArgumentWhenStackEmpty() {
assert(wasException); assert(wasException);
} }
void testPopThrowsAnvalidArgumentWhenStackEmpty() { void testPopThrowsAnInvalidArgumentWhenStackEmpty() {
stack<bool> curStack; stack<bool> curStack;
bool wasException = false; bool wasException = false;
try { try {
@ -195,8 +195,8 @@ int main() {
testAssign(); testAssign();
testTopThrowsAnvalidArgumentWhenStackEmpty(); testTopThrowsAnInvalidArgumentWhenStackEmpty();
testPopThrowsAnvalidArgumentWhenStackEmpty(); testPopThrowsAnInvalidArgumentWhenStackEmpty();
std::cout << "All tests pass!\n"; std::cout << "All tests pass!\n";
return 0; return 0;