data structures - الصفحة الرئيسية · 2016-10-31 · • if several items are removed...

Post on 14-Mar-2020

0 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Data Structures

Dr Ahmed Rafat AbasComputer Science Dept, Faculty of Computer and

Information, Zagazig University

arabas@zu.edu.eg

http://www.arsaliem.faculty.zu.edu.eg/

Stacks and Queues

Chapter 8

(Cont.)

Example: Show the effect of the use of virtual functions in a

base class when other classes are derived from it.

(Q1 Chap. 6)

Consider the class definitions:

class BaseClass

{

protected:

int Num1;

public:

BaseClass () : Num1(0) {}

void TestFunc() { Num1 += 10;}

};

class DerClass : public BaseClass

{

public:

void TestFunc() { Num1 += 20; }

};

After the code:

BaseClass Object1;

Object1.TestFunc ();

What is the value of Object1.Num1?

10 function call answered by parent class.

After the code:

DerClass Object2;

Object2.TestFunc ();

What is the value of Object2.Num1?

20 function call answered by child class.

After the code:

BaseClass *Object3 = new BaseClass;

Object3->TestFunc ();

What is the value of Object3.Num1?

10 function call answered by parent class.

After the code:

BaseClass *Object4 = new DerClass;

Object4->TestFunc ();

What is the value of Object4.Num1?

10 function call answered by parent class.

The declaration of TestFunc () in BaseClass is now replaced

by the line:

virtual void TestFunc () { Num1 += 10; }

After the code:

BaseClass *Object5 = new DerClass;

Object5->TestFunc ();

What is the value of Object5.Num1?

20 function call answered by child class.

Note:• defining an object on a derived class makes no problem

when function overloading occurs.

• However, defining a pointer to the base class and assigning to it an address to the derived class requires the overloaded functions in the base class to be virtual in order to run the overloading functions in the derived class.

Example: show the order of execution of constructor and

destructor functions of classes that contain member objects

and are related by inheritance.

class base { // base.h

protected:

float a;

public:

base(float s=0);

~base();

};

class base2 { // base.h

protected:

float aa;

public:

base2(float s=0);

~base2();

};

class derived:public base {

private:

float b;

base obj1;

public:

derived(float f=0,float g=0);

~derived();

};

class derived2:public derived { // base.h

private:

float c;

base2 obj2;

public:

derived2(float f=0,float g=0, float h=0);

~derived2();

};

base base2

derived

base

derived2

base2

// base.cpp

#include "base.h"

#include <iostream.h>

base::base(float d):a(d) {

cout<<"base class constructor"<<'\n'<<"a= "<<a<<endl;

return; }

base::~base() {

cout<<"base class destructor"<<'\n'<<"a= "<<a<<endl;

return; }

base2::base2(float d):aa(d) {

cout<<"base2 class constructor"<<'\n'<<"aa= "<<aa<<endl;

return; }

// base.cpp

base2::~base2() {

cout<<"base2 class destructor"<<'\n'<<"aa= "<<aa<<endl;

return; }

derived::derived(float d,float e):base(d),b(e) {

cout<<"derived class constructor"<<'\n'<<"b= "<<b<<endl;

return; }

derived::~derived() {

cout<<"derived class destructor"<<'\n'<<"b= "<<b<<endl;

return; }

// base.cpp

derived2::derived2(float d,float e,float k):derived(d,e),c(k) {

cout<<"derived2 class constructor"<<'\n'<<"c= "<<c<<endl;

return; }

derived2::~derived2() {

cout<<"derived2 class destructor"<<'\n'<<"c= "<<c<<endl;

return; }

// main.cpp

#include "base.h"

#include<iostream.h>

showorder();

main() { // main.cpp

showorder();

return 0; }

showorder() {

derived2 dcc(2,4,6);

cout<<endl;

cout<<"||||||||||||||||||||||||||||||||||||||||||||||"<<endl<<endl;

return 0; }

Note:The order of execution of constructor functions of classes

with member objects and related by inheritance is:

• Initialization list of the derived class

• Initialization list of the base class

• Member objects of the base class

• Constructor of the base class

• Member objects of the derived class

• Constructor of the derived class

8.3 Queues

elm1

elm2

elm3

elm4

elm5

elm6

Head Pointer

Tail Pointer

8.3.1 Implementing a queue – Queues can also be implemented using

arrays or lists.

– For the present, the implementation of a

queue using arrays is considered.

– Define a class containing an array for storing

the queue elements, and two pointers:

• one pointing to the head of the queue, and

• the other to the first empty space following the tail.

• Adding an item to the queue:

– check whether the tail pointer points to a valid

location,

– then add the item to the queue and

– increment tail pointer by 1.

• Removing an item from the queue:

– check whether the queue is empty and,

– if not, retrieve the item referred to by the head pointer,

– Increment the head pointer by 1.

• If several items are removed from the queue, then there

will be empty spaces at the beginning of the array.

• To make use of these spaces in adding data items to the

queue, all the stored data items in the queue should be

shifted toward the top of the array so that the head

pointer of the queue returns to the beginning of the array.

• However, the shifting process is costly in terms of

computer time, especially if the number of items stored

in the array is large.

• In order to overcome this problem, a queue can be

implemented using a circular array, in which the entire

array can be used for storing queue elements without

requiring any shifting for the data stored.

• A circular array with QSIZE elements is shown in the

following figure.

1

0QSIZE-1

A circular array.

• Circular array is stored in memory as a linear block of

QSIZE elements.

• The circular diagram is just a convenient way of

representing the data structure.

• Head and tail pointers are used to indicate the location of

the head and the location just after the tail where the

next item should be added to the queue, respectively.

• An empty queue is denoted by the condition head == tail,

as shown in the following figure.

1

0

head

QSIZE-1

tail

An empty queue.

• To identify when the queue is full, it is necessary to keep

at least one free space in the array, and therefore a

queue becomes full when the tail pointer points to the

location immediately prior to the head of the queue, as

shown in the following figure.

1

0QSIZE-1

head

tail

A full queue.

• The algorithms required for dealing with a queue represented by a circular array– Creating an empty queue: set head = tail = 0.

– Testing if a queue is empty: is head == tail?

– Testing if a queue is full: is (tail + 1) % QSIZE == head?

– Adding an item to a queue:

• if queue is not full,

• add item at location tail and

• set tail = (tail + 1) % QSIZE.

– Removing an item from a queue:

• if queue is not empty,

• remove item from location head and

• set head = (head + 1) % QSIZE.

• The use of the % operator, which is the modulus operator

in C++, ensures that head and tail wrap around the end of

the array.

8.3.2 A queue in C++

• The C++ code for the algorithms that implement

a queue using a circular array is given below.

• The following queue is used for storing integers.

// intqueue.h

#ifndef INTQUEUE_H

#define INTQUEUE_H

#include <iostream.h>

typedef int BOOL;

enum{FALSE, TRUE};

class intqueue {

protected:

int QSize; // Size of the Element array

int *Element; // For storing queue contents

int Head, Tail; // Locate head and (tail+1)%QSize

public:

BOOL Empty() const;

BOOL Full() const;

intqueue(int queuesize = 10);

virtual ~intqueue();

virtual BOOL Remove(int& TopElem);

virtual BOOL Add(const int& NewElem);

} ;

#endif

• The following are the definitions of these functions.

//intqueue.cpp.#include "intqueue.h"

BOOL intqueue::Empty() const

{

return Head == Tail? TRUE: FALSE;

}

BOOL intqueue::Full() const

{

return (Tail + 1) % QSize == Head ? TRUE: FALSE;

}

intqueue::intqueue(int queuesize) :QSize(queuesize), Element(new int[queuesize]), Head(0), Tail(0) { }

intqueue::~intqueue() {

delete [] Element; }

BOOL intqueue::Remove(int& TopElem) {

if (!Empty()) {

TopElem = Element[Head];

Head = (Head + 1) % QSize;

return TRUE;

}

else {

cout « "Queue empty: Remove failed.\n";

return FALSE;

}

}

BOOL intqueue::Add(const int& NewElem) {

if (! Full ()) {

Element [Tail] = NewElem;

Tail = (Tail + 1) % QSize;

return TRUE;

}

else {

cout « "Queue full: Add failed.\n";

return FALSE;

}

}

• In order to test this class and provide a simple application

of inheritance, a derived class testqueue which inherits

intqueue and adds a few specialized functions useful for

testing the queue is defined.

• One of the added functions is Print (), which traverses the

queue starting at Head and stepping through the queue

until it reaches the item just before Tail.

• Another function added is Menu (), which prints a menu of

options for testing the queue.

• All these functions are used in a main () function.

• The testqueue class is defined in the header file testque.h as follows.

#ifndef TESTQ_H

#define TESTQ_H

#include "intqueue.h"

class testqueue : public intqueue {

public:

testqueue(int queuesize = 10);

void Print() const;

int Menu() const;

} ;

#endif

• The new functions are defined in the file testque.cpp as follows.

//testque.cpp

#include "testque.h"

testqueue::testqueue(int queuesize) : intqueue(queuesize) { }

void testqueue::Print() const {

if (Empty () )

cout « "Queue is empty.\n";

else

for (int marker = Head; marker != Tail; marker = (marker + 1) % QSize)

cout « marker « ' ' « Element [marker] « endl;

}

int testqueue::Menu() const {

int Choice;

cout « "--------------------------\n";

cout « "Select from:\n";

cout « "1. Add integer to queue\n";

cout « "2. Remove item from queue\n";

cout « "3. Print queue contents.\n";

cout « "0. Quit.\n";

cout « "Your choice: ";

cin » Choice;

return Choice;

}

• The main () function is defined in the file queue.cpp as follows.

//main.cpp

#include "testque.h"

int main () {

testqueue Queue(5);

int Choice, Item;

while (Choice = Queue.Menu())

switch (Choice) {

case 1:

cout « "Enter item to add: ";

cin » Item;

Queue.Add ( Item) ;

break;

case 2:

if (Queue.Remove(Item))

cout « "Item" « Item « " removed.\n";

break;

case 3:

cout « "Contents of queue:\n";

Queue.Print();

break;

}

return 0;

}

top related