This code is copied from the c++ primer plus. I think some
steps in the dequeue function is unnecessary. But the book
say it is important.I don't understand. I hope some one can show me more detail explanation.Here is the definition of the queue.
typedef unsigned long Item;
class Queue
{
private:
struct Node{ Item item; struct Node * next; };
enum{ Q_SIZE = 10 };
Node * front;
Node * rear;
int items; // the number of item in the queue
const int qsize;
Queue(const Queue & q) :qsize(0){};
Queue & operator=(const Queue & q){ return *this; }
Queue & operator=(const Queue & q){ return *this; }
public:
Queue(int qs = Q_SIZE);
~Queue();
bool isempty()const;
bool isfull()const;
int queuecount()const;
bool enqueue(const Item & item);
bool dequeue(Item & item);
};
bool Queue::dequeue(Item & item)
{
if (isempty())
return false;
item = front->item;
Node * temp;
temp=front; // is it necessary
front = front->next;
items--;
delete temp;
if (items == 0)
rear = NULL; //why it is not front=rear=Null ;
return true;
}
The nodes in this queue are stored as pointers. To actually create a node some code like Node* tmp = new Node() is probably somewhere in the enqueue-Function.
With front = front->next; the pointer to the first element gets moved to the next element in the queue. But what about the previous front-node? By moving the pointer we "forget" its adress, but we don't delete the object or free the memory. We have to use delete to do so, which is why the adress is temporarily stored to call the delete. Not deleting it would cause a memory leak here.
About your second question: The frontpointer has already been moved to front->next. What could that be if there was only one element inside the queue? Probably NULL, which should be ensured by the enqueue-function. ("Note: If you are managing this code, it is a good idea to replace NULL with nullptr).
The only variable that didn't get updated yet is rear.
temp = front;
saves a pointer to the front element so it can be deleted after front has been modified.
If the queue is empty, front = front->next; has already set front to null, so there's no need to do it again.
Related
hey i am trying to add to my queue but I have a problem, and I need some help
i used a linked list for my queue and the problem is when I add a 3rd item to my list I overwrite the second
this is the code
void addnode(node* data)
{
if (begin == NULL)
{
data->next = begin;
begin = data;
}
else
{
end = data; //this is where the problem when i add a 3rd data i dont save anywhere my end so its gone
begin->next = end;
end->next = NULL;
}
}
in my code i have begin for the start of the queue, and end for the end of it
the linked list i built is with classes in c++,
but whenever i add a 3rd data the second gets overwriten so i always have two..
I need some help with how to fix it, thanks :)
edit this is more of the code: this is my class for the queue
#include"node.h"
class queue
{
public:
queue();
~queue();
void addNode(node*);
private:
node* begin;
node* end;
};
this is the class that i get the data from
using namespace std;
class node
{
friend void printclient(node &);
public:
node();
~node();
void setstr(string);
void setmoney(int);
node* next;
private:
string name;
double money;
int id;
};
The function can look the following way. I suppose that the data member next of the node pointed to by the pointer data is already set to nullptr.
void addnode(node* data)
{
if (begin == nullptr)
{
begin = end = data;
}
else
{
end = end->next = data;
}
}
That is if the queue is empty (the pointers begin and end are equal to nullptr) then begin and end are set to the added pointer.
Otherwise the new node is appended to the end of the queue. In this case the data member next of the node pointed to by the pointer end is set to the new pointer and this pointer becomes the end pointer.
Pay attention to that the user of the queue should know nothing about the class node. The class should be declared as a private or protected member of the class queue. And the method addNode should be substitute for the method push declaration of which should look like
void push( const std::string &name, int id, double money );
I have to implement a Circularly linked queue class as the LinkedQueueType class. For some reason, when I call the enqueue function, it isn't getting added to the queue, even my test prints don't print out. Here is my queue class:
#define LINKED_QUEUE_H
class FullQueue {};
class EmptyQueue{};
typedef int ItemType;
struct NodeType{
ItemType info;
NodeType* next;
};
class LinkedQueueType {
public:
LinkedQueueType ();
// Class constructor.
// Because there is a default constructor, the precondition // that the queue has been initialized is omitted. LinkedQueueType(const LinkedQueueType& qt);
//Copy Constructor
LinkedQueueType operator=(const LinkedQueueType& rhs); //Overloaded assignment operator=
~LinkedQueueType ();
// Class destructor.
void MakeEmpty();
// Function: Initializes the queue to an empty state.
// Post: Queue is empty.
bool IsEmpty() const;
// Function: Determines whether the queue is empty.
// Post: Function value = (queue is empty)
bool IsFull() const;
// Function: Determines whether the queue is full. // Post: Function value = (queue is full)
void Enqueue(ItemType newItem);
// Function: Adds newItem to the rear of the queue. // Post: newItem is at rear of queue.
void Dequeue(ItemType& item);
// Function: Removes front item from the queue and returns it in // item.
// Post: If (queue is empty) EmptyQueue exception is thrown
// and item is undefined
// else front element has been removed from queue and
// item is a copy of removed element.
void Print(); //Print function
private:
NodeType* rear;
int length;
};
#endif
and here is the implementation for the enqueue function:
void LinkedQueueType::Enqueue(ItemType newItem){
NodeType *newNode=nullptr;
NodeType*temp=nullptr;
newNode->info=newItem;
newNode->next=nullptr;
if(rear==nullptr){
rear=newNode;
}else{
temp=rear->next;
rear->next=newNode;
}
rear=newNode;
rear->next=temp;
}
Here is my testing:
cout<<"hi";
LinkedQueueType q;
q.Enqueue(5);
When I run my driver, it doesn't print, could someone show me the way!
This is your code:
NodeType *newNode=nullptr;
NodeType*temp=nullptr;
newNode->info=newItem;
newNode->next=nullptr;
This is what you are saying:
Make a new pointer called newNode of type NodeType, and let it point to nothing.
Do the same for variable 'temp'.
Now we put newItem in nothing -> Because that is what newNode is pointing to.
Your program tries to dereference the variable newNode to access its property info. But that's not possible.
This is what your code should be:
NodeType *newNode = new NodeType();
NodeType*temp=nullptr;
newNode->info=newItem;
newNode->next=nullptr;
This way your program has an object to derefer and get a property from.
The code you posted definitely crashes (segmentation fault on Linux or access violation on Windows™) dereferencing the null pointer. You need to create the node before accessing it, like:
NodeType *newNode = new NodeType();
I am trying to build my own version of one of the std containers using the linked list method. Every time I get new data I create a new Node and place it in the container. The D'tor will destroy all Nodes when the container it destroyed.
the weird thing is that after a leak check (using valgrind) it says I have a leak every-time I Insert the first data. this is the insert code:
template<typename A, typename T>
typename container<A, T>::Iterator Queue<A, T>::insert(
const A& priority, const T& object) {
Iterator head = this->begin();
Iterator tail = this->begin();
this->findElementPlace(priority, head, tail);
Node<A, T> *newNode = new Node<A, T>(priority, object);
head.node->next = newNode;
newNode->next = (tail.node);
++head;
(this->Psize)++;
return head;
}
it keeps referring me to this line:
Node<A, T> *newNode = new Node<A, T>(priority, object);
the Node class is very basic:
template<typename A, typename T>
class Node {
public:
Element<A, T> element;
Node* next;
Node() :
element(), next(NULL) {
}
Node(const A priority, const T data) :
element(priority, data), next(NULL) {
}
~Node() {
}
};
it doesn't matter where the first data is stored, it always says that specific data is not deleted although the D'tor takes care of it. it uses an erase function that erases all elements from the first to the last. this is the main loop:
while ((from < to) && (from < this->end())) {
it.node->next = from.node->next;
Iterator temp = from;
++from;
delete temp.node;
(this->Psize)--;
}
it deletes all Nodes between the Iterator "from" to Iterator "to" including "from", not including "to"
does anyone know how to fix this?
I found the problem.
The iterator contains an index parameter and the operator< compares the indexes.
the problem was that each loop "this->end()" was recomputed but the iterators "from" and "to" where not relevant anymore because their indexes weren't relevant anymore.
I added an updating to their indexes so that this time all the elements in the container will be released.
the new erase loop is:
while ((from < to) && (from < this->end())) {
it.node->next = from.node->next;
Iterator temp = from;
from.index--;
++from;
delete temp.node;
(this->Psize)--;
to.index--;
}
I'm writing abstract data type of priority queue as a task for an university, which others are going to use. I have a function in my class dequeue, which deletes the first element in the queue and returns the data of this element. However when I try to delete an element from an empty queue, the program crashes. What should I do here ?
Here's the code if it helps:
#ifndef PRIORITYQUEUE_H
#define PRIORITYQUEUE_H
#include <iostream>
using namespace std;
const int max_queue_items = 1000;
template<class T>
struct node{
T data;
int priority;
node *next;
};
template<class T>
class PriorityQueue
{
public:
/*
Constructor that creates an empty queue.
*/
PriorityQueue(){
head = NULL;
size = 0;
}
/*
Adds an element to the queue.
Params:
data - data of the element
priority - priority of the element
*/
bool is_empty(){
if (size == 0){
return true;
}
return false;
}
bool is_full(){
if (size == max_queue_items){
return true;
}
return false;
}
/*
Adds an element to thq queue.
It gets inserted before the first element with
lower priority.
*/
void enqueue(T data, int priority){
node<T> * previous = NULL;
node<T> * now = head;
while (now != NULL && now->priority >= priority){
previous = now;
now = now->next;
}
node<T> * new_element = new node<T>;
new_element->data = data;
new_element->priority = priority;
new_element->next = now;
if (previous == NULL){
head = new_element;
} else {
previous->next = new_element;
}
size++;
}
/*
Removes the first element in the queue
*/
T dequeue(){
T data;
if (!is_empty()){
node<T> * now = head;
data = now->data;
head = head->next;
delete now;
size--;
}
return data;
}
/*
Returns the priority of the first element.
It's always the highest priority in the queue.
*/
int get_first_priority(){
return head->priority;
}
/*
Returns the data of the first element in the queue.
*/
T get_first_value(){
if (is_empty())
throw 0;
return head->data;
}
/*
Returns the number of elements in the queue.
*/
int get_size(){
return size;
}
/*
Deletes the whole queue from the memory.
*/
void flush(){
node<T> * now;
while (head != NULL){
now = head;
head = head->next;
delete now;
size--;
}
}
/*
Prints the whole queue following this format:
data(priority)
*/
void print(){
node<T> * now = head;
while (now != NULL){
cout << now->data << "(" << now->priority << ")" << endl;
now = now->next;
}
}
private:
node<T> * head; // Pointer to the head of the queue
int size; // Number of elements in the queue
};
#endif // PRIORITYQUEUE_H
This may or may not be the source of your problem, but I would definitely consider it an issue. In function dequeue() you are potentially returning an uninitialized variable (if T is not a class type) when is_empty() returns true:
T dequeue()
{
T data; // Uninitialized if T is not a class type
if (!is_empty())
{
node<T> * now = head;
//--------------------------------------------------------------
// This initialization is skipped when `is_empty()` returns true
data = now->data;
//--------------------------------------------------------------
head = head->next;
delete now;
size--;
}
return data;
}
Depending on what you do with the value returned by this function and on the type of T, your program might have Undefined Behavior (I can imagine T being a pointer type that you later dereference).
You may want to change the first line of the function into:
T data = T();
Which enforces value-initialization of your data object. If T is a class type, the default constructor will be invoked. Otherwise, data will be zero-initialized.
The function which calls dequeue() should then check the returned value before using it (or better, call is_empty() on the queue to check it is not empty before trying to pop a value from it).
You may even consider throwing an exception when dequeue() is invoked and the queue is empty:
T dequeue()
{
if (is_empty())
{
// Requires including the <stdexcept> standard header
throw std::logic_error("Queue is empty");
}
node<T> * now = head;
T data = now->data;
head = head->next;
delete now;
size--;
return data;
}
Clients are now responsible for making sure that dequeue() is never called on an empty queue (or they shall wrap calls to dequeue() into a try/catch block to handle the possibly thrown exception.
Another possibility is returning a bool to your client indicating whether the value was successfully popped, possibly assigning the popped element to an argument passed by reference:
bool dequeue(T& data)
{
if (is_empty())
{
return false;
}
node<T> * now = head;
data = now->data;
head = head->next;
delete now;
size--;
return true;
}
This way, the client is responsible for checking the result of the function. If the function returns false, the data variable will be initialized to whatever the client initialized it to. Responsibilities for handling error situations is again transferred to the client.
I think there are some issues.
First and most important, there is no destructor for the class. And if not all elements are dequeued in your program there will be a memory leak. Write the destructor or use smart pointer instead of the raw one.
Second, as #Andy Prowl(btw who knows how to # people in post like twitter?) said, uninitialized local variable should be considered. And T data = T() works well both for built-in and custom types.
Third, I think there is a capacity restriction max_queue_items for the queue but there is no corresponding code for the enqueue part.
Even though, I don't think all these flaws could cause a serious crash in normal case. Maybe the problem occurs in your code invokes the class and the incorrect processing for uninitalized return value leads to a crash.
The only potential problem I see in you dequeue is that you are creating a temporary variable of the unknown type T. If you are storing data of a type with no default constructor in your priority queue, you are going to have a problem when your dequeue calls and tries to default construct that variable.
If this is the case, I would suggest that you re-work your priority queue to hold pointers to the template type rather than the data itself.
I would like to ask you how to write a copy constructor (and operator = ) for the following classes.
Class Node stores coordinates x,y of each node and pointer to another node.
class Node
{
private:
double x, y;
Node *n;
public:
Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {}
void setNode (Node *nn) : n(nn) {}
...
};
Class NodesList (inherited from std:: vector) stores all dynamically allocated Nodes
class NodesList : public std::vector<Node *>
{}
The main program:
int main()
{
Node *n1 = new Node(5,10,NULL);
Node *n2 = new Node(10,10,NULL);
Node *n3 = new Node(20,10,NULL);
n1->setNode(n2);
n2->setNode(n3);
n3->setNode(n2);
NodesList nl1;
nl1.push_back(n1);
nl1.push_back(n2);
nl1.push_back(n3);
//Copy contructor is used, how to write
NodesList nl2(nl1);
//OPerator = is used, how to write?
NodesList nl3 = nl1;
}
I do not want to create a shallow copy of each node but a deep copy of each node. Could I ask you for a sample code with copy constructor?
Each node can be pointed more than once. Let us have such situation, when 3 nodes n[1], n[2], n[3] are stored in the NodesList nl1:
n[1] points to n[2]
n[2] points to n[3]
n[3] points to n[2]
A] Our copy constructor process the node n[1]. It creates a new object n[1]_new represented by the copy of the old object n[1]_old. The node n[2] pointed from n[1]_old still does not exist, so n[2]_new must be also created... The pointer from n1_new to n2_new is set.
B] Then second point n[2] is processed. It can not be created twice, n[2]_new was created in A]. But pointed node n[3] does not exist, so the new object n[3]_new as a copy of an old object n[3]_old is created. The pointer from n2_new to n3_new is set.
C] Node n[3]_new has already been created and n[2]_new. The pointer from n3_new to n2_new is set and no other object will be created...
So the copy constructor should check whether the object has been created in the past or has not...
Some reference counting could be helpful...
There is my solution of the problem. A new data member n_ref storing a new verion of the node n was added:
class Node
{
private:
double x, y;
Node *n, *n_ref;
public:
Node (double xx, double yy, Node *nn) : x(xx), y(yy), n(nn) {n_ref = NULL;}
Node * getNode() {return n;}
Node * getRefNode () {return n_ref;}
void setNode (Node *nn) {this->n = nn;}
void setRefNode (Node *nn) {this->n_ref = nn;}
The copy constructor creates a shallow copy of the node:
Node (const Node *node)
{
x = node->x;
y = node->y;
n = node->n;
n_ref = node->n_ref;
}
The copy constructor for NodesList
NodesList::NodesList(const NodesList& source)
{
const_iterator e = source.end();
for (const_iterator i = source.begin(); i != e; ++i) {
//Node* n = new Node(**i);
//Node n still has not been added to the list
if ((*i)->getRefNode() == NULL)
{
//Create node
Node *node = new Node(*i);
//Add node to the list
push_back(node);
//Set this note as processed
(*i)->setRefNode(node);
//Pointed node still has not been added to the list
if ((*i)->getNode()->getRefNode() == NULL)
{
//Create new pointed node
Node *node_pointed = new Node ((*i)->getNode());
//Add node to the list
push_back(node_pointed);
//Set pointer to n
node->setNode(node_pointed);
//Set node as processed
((*i)->getNode())->setRefNode(node_pointed);
}
//Pointed node has already been added to the list
else
{
//Set pointer to node n
node->setNode((*i)->getRefNode());
}
}
//Node n has already been added to the list
else
{
//Get node
Node * node = (*i)->getRefNode();
//Pointed node still has not been added
if ((*i)->getNode()->getRefNode() == NULL)
{
//Create new node
Node *node_pointed = new Node ((*i)->getNode());
//Add node to the list
push_back(node_pointed);
//Set pointer to n
node->setNode(node_pointed);
//Set node as processed
((*i)->getNode())->setRefNode(node_pointed);
}
//Pointed node has already been added to the list
else
{
//Set pointer to n
node->setNode((*i)->getNode()->getRefNode());
}
}
}
}
Perform a shallow copy in NodeList::NodeList(const NodeList&) and you don't have to worry about cycles breaking the copy operation. Disclaimer: the following is untested, incomplete and may have bugs.
class NodeList {
private:
typedef std::vector<Node*> Delegate;
Delegate nodes;
public:
NodeList(int capacity=16) : nodes() { nodes.reserve(capacity); }
NodeList(const NodeList& from);
virtual ~NodeList();
NodeList& operator=(const NodeList& from);
/* delegated stuff */
typedef Delegate::size_type size_type;
typedef Delegate::reference reference;
typedef Delegate::const_reference const_reference;
typedef Delegate::iterator iterator;
typedef Delegate::const_iterator const_iterator;
size_type size() const { return nodes.size(); }
iterator begin() { return nodes.begin(); }
const_iterator begin() const { return nodes.begin(); }
iterator end() { return nodes.end(); }
const_iterator end() const { return nodes.end(); }
// ...
};
NodeList::NodeList(const NodeList& from)
: nodes(from.size()), flags(NodeList::owner)
{
std::map<Node*, Node*> replacement;
Delegate::const_iterator pfrom;
Delegate::iterator pto;
// shallow copy nodes
for (pfrom=from.begin(), pto=nodes.begin();
pfrom != from.end();
++pfrom, ++pto)
{
replacement[*pfrom] = *pto = new Node(**pfrom);
}
// then fix nodes' nodes
for (pto = nodes.begin(); pto != nodes.end(); ++pto) {
(*pto)->setNode(replacement[(*pto)->getNode()]);
}
}
NodeList::operator=(const NodeList&) can use the copy-swap idiom, the same as Tronic's Node::operator=(const Node&).
This design has a potential memory leak in that a copied NodeList is (initally) the only place that references its nodes. If a temporary NodeList goes out of scope, a poor implementation will leak the Nodes the list contained.
One solution is to proclaim that NodeLists own Nodes. As long as you don't add a Node to more than one NodeList (via NodeList::push_back, NodeList::operator[] &c), NodeList's methods can delete nodes when necessary (e.g. in NodeList::~NodeList, NodeList::pop_back).
NodeList::~NodeList() {
Delegate::iterator pnode;
for (pnode = nodes.begin(); pnode != nodes.end(); ++pnode) {
delete *pnode;
}
}
void NodeList::pop_back() {
delete nodes.back();
nodes.pop_back();
}
Another solution is to use smart pointers, rather than Node*. NodeList should store shared pointers. Node::n should be a weak pointer to prevent ownership cycles.
I would just use std::list<Node> instead of NodesList. Well, let's code...
NodesList::NodesList(const NodesList& source)
{
const_iterator e = source.end();
for (const_iterator i = source.begin(); i != e; ++i) {
Node* n = new Node(**i);
push_back(n);
}
}
Apparently each Node is only allowed to point to another Node in the same list? Otherwise the "deep copy" of a list needs more definition. Should it not be connected to the original NodeList? Should it not be connected to any original Node? Are copies of Nodes not in the list being copied added to some other list or free-floating?
If all the Node-to-Node pointers are constrained within the NodeList, then perhaps you should store indexes instead of pointers, then no special handling is required.
You should not inherit from standard library containers (because they lack virtual destructors). Instead, include them as member variables in your classes.
Since you want a deep copy, you need these: (rule of three)
Node(Node const& orig): x(orig.x), y(orig.y), n() {
if (orig.n) n = new Node(*orig.n);
}
Node& operator=(Node const& orig) {
// The copy-swap idiom
Node tmp = orig;
swap(tmp); // Implementing this member function left as an exercise
return *this;
}
~Node() { delete n; }
A better idea might be to avoid using pointers entirely and just put your nodes in a suitable container.