[fix/docs]: remove memory leak in queue (#2417)

* fix: remove memory leak in queue

* updating DIRECTORY.md

* clang-format and clang-tidy fixes for effd74c9

* style: simplify logic while using reserve

* style: use value_type as return type in front

* style: use proper error message

* style: use pre-increment and pre-decrement

* docs: use doxygen syntax

* docs: improve wording

Co-authored-by: github-actions[bot] <github-actions@users.noreply.github.com>
Co-authored-by: David Leal <halfpacho@gmail.com>
This commit is contained in:
Piotr Idzik 2023-01-26 07:29:37 +01:00 committed by GitHub
parent 5b238724b8
commit 7c090481da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 250 additions and 161 deletions

View File

@ -57,7 +57,8 @@
* [Linkedlist Implentation Usingarray](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/linkedlist_implentation_usingarray.cpp)
* [List Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/list_array.cpp)
* [Morrisinorder](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/morrisinorder.cpp)
* [Queue](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/queue.h)
* [Node](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/node.hpp)
* [Queue](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/queue.hpp)
* [Queue Using Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/queue_using_array.cpp)
* [Queue Using Array2](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/queue_using_array2.cpp)
* [Queue Using Linked List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/queue_using_linked_list.cpp)

46
data_structures/node.hpp Normal file
View File

@ -0,0 +1,46 @@
/**
* @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;
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_

104
data_structures/queue.hpp Normal file
View File

@ -0,0 +1,104 @@
/* 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;
/**
* @brief prints the queue into the std::cout
*/
void display() const {
std::cout << "Front --> ";
display_all(this->queueFront.get());
std::cout << '\n';
std::cout << "Size of queue: " << size << '\n';
}
/**
* @brief converts the queue into the std::vector
* @return std::vector containning all of the elements of the queue in the
* same order
*/
std::vector<value_type> toVector() const {
return push_all_to_vector(this->queueFront.get(), this->size);
}
private:
/**
* @brief throws an exception if queue is empty
* @exception std::invalid_argument if queue is empty
*/
void ensureNotEmpty() const {
if (isEmptyQueue()) {
throw std::invalid_argument("Queue is empty.");
}
}
public:
/**
* @brief checks if the queue has no elements
* @return true if the queue is empty, false otherwise
*/
bool isEmptyQueue() const { return (queueFront == nullptr); }
/**
* @brief inserts a new item into 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
* @exception std::invalid_argument if queue is empty
*/
value_type front() const {
ensureNotEmpty();
return queueFront->data;
}
/**
* @brief removes the first element from the queue
* @exception std::invalid_argument if queue is empty
*/
void deQueue() {
ensureNotEmpty();
queueFront = queueFront->next;
--size;
}
/**
* @brief removes all elements from the 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_
#define DATA_STRUCTURES_STACK_HPP_
#include <iostream> /// for IO operations
#include <memory> /// for std::shared_ptr
#include <stdexcept> /// for std::invalid_argument
#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 {
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);
}
}
#include "node.hpp" /// for Node
/** Definition of the stack class
* \tparam value_type type of data nodes of the linked list in the stack should
@ -42,20 +23,13 @@ class stack {
/** Show stack */
void display() const {
std::cout << "Top --> ";
traverse(stackTop.get(), [](const node<value_type>& inNode) {
std::cout << inNode.data << " ";
});
std::cout << std::endl;
display_all(this->stackTop.get());
std::cout << '\n';
std::cout << "Size of stack: " << size << std::endl;
}
std::vector<value_type> toVector() const {
std::vector<value_type> res;
res.reserve(this->size);
traverse(stackTop.get(), [&res](const node<value_type>& inNode) {
res.push_back(inNode.data);
});
return res;
return push_all_to_vector(this->stackTop.get(), this->size);
}
private:
@ -71,7 +45,7 @@ class stack {
/** Add new item to the stack */
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->next = stackTop;
stackTop = newNode;
@ -98,7 +72,7 @@ class stack {
}
private:
std::shared_ptr<node<value_type>> stackTop =
std::shared_ptr<Node<value_type>> stackTop =
{}; /**< Pointer to the stack */
std::size_t size = 0; ///< size of stack
};

View File

@ -1,41 +1,93 @@
#include <iostream>
#include <string>
#include <cassert> /// for assert
#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() {
queue<std::string> q;
std::cout << "---------------------- Test construct ----------------------"
<< std::endl;
q.display();
std::cout
<< "---------------------- Test isEmptyQueue ----------------------"
<< std::endl;
if (q.isEmptyQueue())
std::cout << "PASS" << std::endl;
else
std::cout << "FAIL" << std::endl;
std::cout << "---------------------- Test enQueue ----------------------"
<< std::endl;
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();
testConstructedQueueIsEmpty<int>();
testConstructedQueueIsEmpty<double>();
testConstructedQueueIsEmpty<std::vector<long double>>();
testEnQueue();
testDeQueue();
testQueueAfterClearIsEmpty();
testFrontThrowsAnInvalidArgumentWhenQueueEmpty();
testDeQueueThrowsAnInvalidArgumentWhenQueueEmpty();
std::cout << "All tests pass!\n";
return 0;
}

View File

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