mirror of
https://hub.njuu.cf/TheAlgorithms/C-Plus-Plus.git
synced 2023-10-11 13:05:55 +08:00
fix: remove memory leak in queue
This commit is contained in:
parent
e2bf654e82
commit
effd74c9ae
48
data_structures/node.hpp
Normal file
48
data_structures/node.hpp
Normal 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_
|
@ -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
80
data_structures/queue.hpp
Normal 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_
|
@ -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
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user