0% found this document useful (0 votes)
62 views50 pages

Lecture 3 - Queues)

The document discusses abstract data types (ADTs) and how they are constructed using classes in C++. It explains that classes contain public and private sections, with the public section making up the interface of the ADT and the private section containing implementation details. The document then provides an example of an integer stack ADT implemented using a class with methods like push(), pop(), and top(). It concludes by introducing the concept of templates to generalize ADTs so they can work with different data types rather than being limited to a single type.

Uploaded by

Vicky Butt
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
62 views50 pages

Lecture 3 - Queues)

The document discusses abstract data types (ADTs) and how they are constructed using classes in C++. It explains that classes contain public and private sections, with the public section making up the interface of the ADT and the private section containing implementation details. The document then provides an example of an integer stack ADT implemented using a class with methods like push(), pop(), and top(). It concludes by introducing the concept of templates to generalize ADTs so they can work with different data types rather than being limited to a single type.

Uploaded by

Vicky Butt
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 50

ADT's and C++:

A Review
ADT's, a review
 We've learned that it is often necessary to
develop new data types for our programming,
and we develop ADT's.
 In C++, an ADT is constructed using the
class construct.
 Classes contain public and private
information, and can have sections so labeled.
ADT's, a review
 The public section contains the interface to
the ADT. It consists of a set of functions
called methods.
 Two special types of methods exist and should
always be declared: constructors and
destructors.
 Other methods provide the basic ability to
examine and manipulate objects of the new
Abstract Data Type.
ADT's, a review
 The private section contains the basis for the
implementation the ADT. It usually contains
variables, constants and structures necessary for
the implementation.
 This private information is only visible to and
accessible by the ADT itself; applications using
the ADT can't see this information or refer to it
in any way.
ADT's, a review
 The class definition is placed in a header file
which we call the ADT definition header.
 The ADT definition header should be included
by any source or header file that needs to use
the ADT in question.
 The ADT definition header should also be
included by the ADT definition source file,
which implements all of the methods of the
ADT.
ADT's, a review
 When creating a project that uses the ADT, the
ADT definition header should be placed in the
list of project header files. The ADT definition
source file should be placed in the list of project
source files.
ADT's, a review
 Putting all of this together,
we arrive at the
following two files: IntStack.h and IntStack.cpp.
ADT's, a review
// FILE: INTSTACK.H – Stack of ints ADT definition
#ifndef _INTSTACK
#define _INTSTACK
class IntStack
{
public:
IntStack();
~IntStack();
bool is_empty() const;
bool is_full() const;
bool push (const int &value);
bool pop (int &value);
bool pop ();
int top() const;
private:
static const int MAX_SIZE = 10000;
int top_index;
int stack[MAX_SIZE];
};
#endif
ADT's, a review
// FILE: INTSTACK.CPP – Stack of ints ADT
implementation
#include "IntStack.h"
IntStack::IntStack()
{
top_index = -1;
}
IntStack::~IntStack()
{
}
bool IntStack::is_empty() const
{
return (top_index == -1);
}
bool IntStack::is_full() const
{
return (top_index == MAX_SIZE-1);
ADT's, a review
bool IntStack::push (const int &value)
{
bool result = false;
if (top_index < MAX_SIZE - 1)
{
top_index++;
stack[top_index]=value;
result = true;
}
return result;
}
bool IntStack::pop ()
{
bool result = false;
if (top_index != -1)
{
top_index--;
result = true;
}
return result;
}
ADT's, a review
bool IntStack::pop (int &value)
{
bool result = false;
if (top_index != -1)
{
value = stack[top_index];
top_index--;
result = true;
}
return result;
}

int IntStack::top() const


{
return stack[top_index];
}
Introducing Templates
Last week we created the IntStack
ADT.
Now, let's generalize it.
ADT's, a review
 When using an ADT, the application refers only
to the declaration of ADT objects and the use of
the public methods on those objects. The
private information can not be accessed.
 You already know how this is done, since you
did your homework exercise for the week.
Templates
 The problem with the ADT just developed is that it is
only good for stacks of integers. If we wanted a stack
of student records, or a stack of floats, we're just out of
luck.

 We would have to develop a separate ADT for each


new base type. What a nuisance.

 Note that each new stack ADT would be basically


identical to the one we just saw, because a stack is a
stack!!!!
Templates
 To get around this, we will redefine the stack as
a template ADT.
 A template ADT is like a regular ADT, except
that the base type (or other similar information)
becomes a parameter of the template.
 But because of limitations of the C++ language,
we have to make some adjustments to how
things are put together.
Templates
 We will now combine the contents of the ADT
definition header and the ADT definition source
into a single file, the ADT template definition
header.

 There will no longer be a source file for the


ADT definition.
Templates
 Each item that appears in the original .h
and .cpp file is given a template header.
 The class definition is given a template header.
 Each method implementation is given a
template header.
 Template header:
template <class BaseType>
Templates
template <class BaseType>
class Stack Template header: BaseType is
{ a parameter of the template
public: header,
Stack();
~Stack();
bool is_empty() const;
notebool is_full() const;
indent
bool push (const BaseType &value);
ation
bool pop (BaseType &value);
bool pop ();
BaseType top() const;
private:
static const int MAX_SIZE = 10000;
int top_index;
BaseType stack[MAX_SIZE];
};

See how the BaseType is now a


parameter
Templates
 A similar thing is done with each method.
These method template definitions appear in the
header file (unfortunately) because C++ can't do
it any other way.
 For example, here's push:
Templates
template <class BaseType>
bool Stack<BaseType>::push (const BaseType &value)
{
bool result = false;
if (top_index < MAX_SIZE - 1)
{
top_index++;
stack[top_index]=value;
result = true;
}
return result;
}

 We do this for each of the methods and arrive at a file


that looks like the following:
Templates
// FILE: STACK.H – ADT template definition for stacks

#ifndef _STACK_TEMPLATE
#define _STACK_TEMPLATE

template <class BaseType>


class Stack
{
public:
Stack();
~Stack();
bool is_empty() const;
bool is_full() const;
bool push (const BaseType &value);
bool pop (BaseType &value);
bool pop ();
BaseType top() const;
private:
static const int MAX_SIZE = 10000;
int top_index;
BaseType stack[MAX_SIZE];
};
Templates
template <class BaseType>
bool Stack<BaseType>::is_empty() const
{
return (top_index == -1);
}

template <class BaseType>


bool Stack<BaseType>::is_full() const
{
return (top_index == MAX_SIZE-1);
}
Notice how in the member specifier we now show the ADT
name with the parameter BaseType in angle brackets.
Templates
template <class BaseType>
bool Stack<BaseType>::push (const
BaseType
&value)
{
bool result = false;
if (top_index < MAX_SIZE - 1)
{
top_index++;
stack[top_index]=value;
result = true;
}
return result;
}
Templates
template <class BaseType>
bool Stack<BaseType>::pop ()
{
bool result = false;
if (top_index != -1)
{
top_index--;
result = true;
}
return result;
}
Templates
template <class BaseType>
bool Stack<BaseType>::pop (BaseType &value)
{
bool result = false;
if (top_index != -1)
{
value = stack[top_index];
top_index--;
result = true;
}
return result;
}
Templates
template <class BaseType>
BaseType Stack<BaseType>::top() const
{
return stack[top_index];
}

#endif
Using Templates
 Let's look at how we can use a template in an
application.

 The example that follows is for demonstration


of syntax only.

 You would put the source file that follows into


your projects list of source files and stack.h into
your list of header files.
Using Templates
// Sample source file using the Stack template ADT
#include <iostream>
using namespace std;
#include "stack.h"

int main()
{
Stack<int> s;
int value;

while (cin >> value)


{
if (value > 0)
cout << value << endl;
else if (value < 0)
s.push(value)
}
return EXIT_SUCCESS;
}
Homework
 Write a C++ program that reads a series of integers from
cin and determines whether it is a zero-marked
palindrome.
 A zero-marked palindrome consists of a series of integer
values, only one of which has the value zero. If the values
are the same, looking backwards and forwards, with a
zero in the middle, the input is a zero-marked palindrome.
 For example: 5 10 40 20 0 20 40 10 5 is a zero-marked
palindrome.
 Use the template ADT in stack.h to do the checking.
Queues
 A queue is homogeneous, first-in, first-out (FIFO)
structure.
 Homogeneous means that all the values stored in a
queue are of the same type
 First-in, first-out means that the first value inserted
into the queue would be the first one we would be
able to extract
 We insert new values at the tail of the queue and
remove values from the head of the queue.
Queues
 An empty queue is one that contains no values.
We cannot remove from an empty queue.
 In some implementations, it is necessary to
define a full queue as one that has no more
room for any more values. We cannot insert
into a full queue.
 The current value in a queue is the first of the
values in the queue that we inserted.
Queues
template <class Type>
class Queue

???
{
public:
Queue();
~Queue();
bool is_empty() const;
bool is_full() const;
bool insert (const Type &value);
bool remove (Type &value);
bool remove ();
Type current () const; // value at head
// of queue
friend ostream &operator << <Type>(ostream &out,
const Queue<Type> &queue);

private:
friend functions
 Introducing friend functions

 Sometimes we have functions that are not actual


methods that we would like to define for a class. For
example, suppose we want to overload the operator << to
allow “dumping” the contents of a Queue.
friend functions
 But << is an operator on ostreams, not Queues.

 We introduce the notion of friend functions or


operators, which have the ability to see private
information, just like a method.

 friends must be declared in the class definition,


nowhere else. That’s because the class decides who is
and who isn’t a friend.
friend functions
 For example, suppose we decided (incorrectly) that we
would like to supply a friend function that returns the
size of a queue.

 Let's call it size_of

 friend int size_of <Type> (Queue<Type>)

 Usage: count = size_of(q);


operator overloading
 We also introduce the concept of operator overloading.
 It's like function overloading, but we redefine a C++
operator instead.
 We can re-define any existing C++ operator to work on
new kinds of objects. Merely declare:
MyType &operator + (const MyType &operand)

instead of
MyType &add_operand (const MyType &operand)
operator overloading
and call by
my_number = x + y;
instead of
my_number = x.add_operand(y);
Queues
 As with stacks, there are many ways to
implement a queue: e.g. - arrays, Vectors,
linked lists.
 Array implementation: the traveling problem.
Unlike stacks, which have one movable end
(the top), queues have two movable ends (the
head and the tail)
Array implementation of Queues
private:
static const int MAX_SIZE = 100;
int head, tail;
int size;
Type queue[MAX_SIZE];
}

We maintain a size this time, for convenience (as we will see later)
Insertion is done by: tail++; queue[tail] = value;
removal is done by: value = queue[head]; head++;

What happens when we alternately insert and remove?


Array implementation of Queues
0 head

1
tail
2

n-4

n-3

n-2

n-1
Array implementation of Queues
 This is known as drift. The head and tail have
a tendency to move down the array and tail
reaches the end of the array, not allowing any
more insertions even though there’s room in
the area before the head.
 Solution: Circular storage. Imagine that the
two ends of the array are wrapped around so
they meet. The “next” location after the last
one is the first one!!!
Array implementation of Queues
n-1 0
1
n-2
2

Circular Storage
Array implementation of Queues
template <class Type>
Queue<Type>::Queue()
{
size = 0;
head = 0; tail = -1;
}

template <class Type>


Queue<Type>::~Queue()
{
}
Array implementation of Queues
template <class Type>
bool Queue<Type>::is_empty() const
{
return size == 0;
}

template <class BaseType>


bool Queue<Type>::is_full() const
{
return size == MAX_SIZE;
}
Array implementation of Queues
template <class Type>
bool Queue<Type>::insert(const Type &value)
{
bool success = !is_full();

if (success)
{
size++;
tail = (tail + 1) % MAX_SIZE;
queue[tail] = value;
}
return success;
}
Array implementation of Queues
template <class Type>
bool Queue<Type>::remove(Type &value)
{
bool success = !is_empty();

if (success)
{
size--;
value = queue[head];
head = (head + 1) % MAX_SIZE;
}
return success;
}
Array implementation of Queues
template <class Type>
bool Queue<Type>::remove()
{
bool success = !is_empty();

if (success)
{
size--;
head = (head + 1) % MAX_SIZE;
}
return success;

}
Array implementation of Queues
template <class BaseType>
BaseType Queue<BaseType>::current() const
{
return queue[head];
}
Array implementation of Queues
template <class Type>
ostream &operator << <Type>(ostream &out,
const Queue<Type> &x)
{
out << "head: " << x.head << ", tail: "
<< x.tail << ", size: " << x.size << endl;
for (int k = x.head, i = 0; i < x.size;
k = (k+1) % x.MAX_SIZE, i++)
out << k << ": " << x.queue[k] << endl;

return out;
}
Homework
 Write a C++ program that reads a series of
numbers from cin. Upon completion, write
the non-negative values to cout in the reverse
order from how they came in. Then write the
negative values in the same order as they came
in.

You might also like