0% found this document useful (0 votes)
2 views17 pages

Module 1 Remaining

The document discusses inheritance and virtual functions in C++, explaining how derived class objects can be passed to base class parameters and the importance of virtual functions for dynamic binding. It also covers the necessity of virtual destructors for proper memory management when dealing with derived classes and introduces abstract classes and pure virtual functions. Additionally, it outlines operations on array-based lists, including creation, insertion, and retrieval of elements.

Uploaded by

1rn22ec013.anish
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views17 pages

Module 1 Remaining

The document discusses inheritance and virtual functions in C++, explaining how derived class objects can be passed to base class parameters and the importance of virtual functions for dynamic binding. It also covers the necessity of virtual destructors for proper memory management when dealing with derived classes and introduces abstract classes and pure virtual functions. Additionally, it outlines operations on array-based lists, including creation, insertion, and retrieval of elements.

Uploaded by

1rn22ec013.anish
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Inheritance and virtual functions:

As a parameter, a class object can be passed either by value or by


reference.

Earlier chapters also said that the types of the actual and formal
parameters must match. However, in the case of classes, C++ allows the
user to pass an object of a derived class to a formal parameter of the base
class type.

class baseClass

public:

void print();

baseClass(int u = 0);

private:

int x;

};

class derivedClass: public baseClass

public:

void print();

derivedClass(int u = 0, int v = 0);

private:

int a;

};

void baseClass::print()

cout << "In baseClass x = " << x << endl;

baseClass::baseClass(int u)
{

x = u;

void derivedClass::print()

cout << "In derivedClass ***: ";

baseClass::print();

cout << "In derivedClass a = " << a << endl;

derivedClass::derivedClass(int u, int v)

: baseClass(u)

a = v;

Consider the following function in a user program (client code):

void callPrint(baseClass& p)

[Link]();

The function callPrint has a formal reference parameter p of type


baseClass. You

can call the function callPrint by using an object of either type baseClass
or type

derivedClass as a parameter. Moreover, the body of the function callPrint


calls the

member function print. Consider the following function main:

int main() //Line 1

{ //Line 2

baseClass one(5); //Line 3

derivedClass two(3, 15); //Line 4


[Link](); //Line 5

[Link](); //Line 6

cout << "*** Calling the function "

<< "callPrint ***" << endl; //Line 7

callPrint(one); //Line 8

callPrint(two); //Line 9

return 0; //Line 10

} //Line 11

Sample Run:

In baseClass x = 5

In derivedClass ***: In baseClass x = 3

In derivedClass a = 15

*** Calling the function callPrint ***

In baseClass x = 5

In baseClass x = 3

The output generated by the statements in Lines 8 and 9 shows only the
value of x, even though in these statements a different class object is
passed as a parameter. (Because in Line 9 object two is passed as a
parameter to the function callPrint, one would expect that the output
generated by the statement in Line 9 should be similar to the output in
the second and third lines of the output.)

What actually occurred is that for both statements (Lines 8 and 9), the
member function print of the class baseClass is executed. This is due to
the fact that the binding of the member function print, in the body of the
function callPrint, occurred at compile time. Because the formal parameter
p of the function callPrint is of type baseClass, for the statement [Link]();,
the compiler associates the function print of the class baseClass. More
specifically, in compile-time binding, the necessary code to call a specific
function is generated by the compiler. (Compile-time binding is also known
as static binding.)

We suppose to get the corresponding output when we pass objecttwo. C+


+ corrects this problem by providing the mechanism of virtual functions.
The binding of virtual functions occurs at program execution time, not at
compile time. This kind of binding is called run-time binding. More
formally, in run-time binding, the compiler does not generate the code to
call a specific function. Instead, it generates enough information to enable
the run-time system to generate the specific code for the appropriate
function call. Run-time binding is also known as dynamic binding.

class baseClass

public:

virtual void print(); //virtual function

baseClass(int u = 0);

private:

int x;

};

class derivedClass: public baseClass

public:

void print();

derivedClass(int u = 0, int v = 0);

private:

int a;

};

Note that we need to declare a virtual function only in the base class.

The definition of the member function print is the same as before. If we


execute the

previous program with these modifications, the output is as follows.

Sample Run:

In baseClass x = 5

In derivedClass ***: In baseClass x = 3


In derivedClass a = 15

*** Calling the function callPrint ***

In baseClass x = 5

In derivedClass ***: In baseClass x = 3

In derivedClass a = 15

#include <iostream> //Line 1

#include "derivedClass.h" //Line 2

using namespace std; //Line 3

void callPrint(baseClass *p); //Line 4

int main() //Line 5

{ //Line 6

baseClass *q; //Line 7

derivedClass *r; //Line 8

q = new baseClass(5); //Line 9

r = new derivedClass(3, 15); //Line 10

q->print(); //Line 11

r->print(); //Line 12

cout << "*** Calling the function "

<< "callPrint ***" << endl; //Line 13

callPrint(q); //Line 14

callPrint(r); //Line 15

return 0; //Line 16

} //Line 17

void callPrint(baseClass *p)

p->print();
}

Sample Run:

In baseClass x = 5

In derivedClass ***: In baseClass x = 3

In derivedClass a = 15

*** Calling the function callPrint ***

In baseClass x = 5

In derivedClass ***: In baseClass x = 3

In derivedClass a = 15

In case of passing value parameter:

However, if p is a value parameter, then this mechanism of passing a


derived class object as an actual parameter to p does not work, even if p
uses a virtual function. Recall that, if a formal parameter is a value
parameter, the value of the actual parameter is copied into the formal
parameter. Therefore, if a formal parameter is of a class type, the member
variables of the actual object are copied into the corresponding member
variables of theformal parameter. Suppose that we have the classes
defined above—that is, baseClass and derivedClass. Consider the
following function definition: void callPrint(baseClass p) //p is a value
parameter.

[Link]();

Further suppose that we have the following declaration:

derivedClass two;

The object two has two member variables, x and a. The member variable
x is inherited

from the base class. Consider the following function call:

callPrint(two);
In this statement, because the formal parameter p is a value parameter,
the member

variables of two are copied into the member variables of p. However,


because p is an

object of type baseClass, it has only one member variable. Consequently,


only the

member variable x of two will be copied into the member variable x of p.


Also, the

statement:

[Link]();

in the body of the function will result in executing the member function
print of the

class baseClass.

//*******************************************************

// This program illustrates how virtual functions and a

// pointer variable of base class as a formal parameter

// work.

//*******************************************************

#include <iostream> //Line 1

#include "derivedClass.h" //Line 2

using namespace std; //Line 3

void callPrint(baseClass p); //Line 4

int main() //Line 5

{ //Line 6

baseClass one(5); //Line 7

derivedClass two(3, 15); //Line 8

[Link](); //Line 9

[Link](); //Line 10

cout << "*** Calling the function "


<< "callPrint ***" << endl; //Line 11

callPrint(one); //Line 12

callPrint(two); //Line 13

return 0; //Line 14

} //Line 15

void callPrint(baseClass p) //p is a value parameter

[Link]();

Sample Run:

In baseClass x = 5

In derivedClass ***: In baseClass x = 3

In derivedClass a = 15

*** Calling the function callPrint ***

In baseClass x = 5

In baseClass x = 3

Note:

An object of the base class type cannot be passed to a formal


parameter of the derived class type.

Classes and Virtual Destructors

One thing recommended for classes with pointer member variables is that
these classes

should have the destructor. The destructor is automatically executed


when the class

object goes out of scope. Thus, if the object creates dynamic objects, the
destructor

can be designed to deallocate the storage for them. If a derived class


object is passed to a formal parameter of the base class type, the
destructor of the base class executes regardless of whether the derived
class object is passed by reference or by value. Logically, however, the
destructor of the derived class should be executed when the derived class
object goes out of scope. To correct this problem, the destructor of the
base class must be virtual. The virtual destructor of a base class
automatically makes the destructor of a derived class virtual. When a
derived class object is passed to a formal parameter of the base class
type, then when the object goes out of scope, the destructor of the
derived class executes. After executing the destructor of the derived class,
the destructor of the base class executes. Therefore, when the derived
class object is destroyed, the base class part (that is, the members
inherited from the base class) of the derived class object is also
destroyed.

Abstract Classes and Pure Virtual Functions

From the class shape you can derive other classes such as rectangle,
circle, ellipse, and

so on. Some of the things common to every shape are its center, using the
center to move

a shape to a different location, and drawing the shape. Among others, we


can include

these in the class shape. For example, you could have the definition of the
class

shape similar to the following:

class shape

public:

virtual void draw();

//Function to draw the shape.

virtual void move(double x, double y);

//Function to move the shape at the position (x, y).

.
};

Because the definitions of the functions draw and move are specific to a
particular shape,

each derived class can provide an appropriate definition of these


functions. Because we do not want to include the definitions of the
functions draw and move of the

class shape, we must convert these functions to pure virtual functions. In


this case,

the prototypes of these functions are:

virtual void draw() = 0;

virtual void move(double x, double y) = 0;

Once a class contains one or more pure virtual functions, then that class is
called an abstract

class. Thus, the abstract definition of the class shape is similar to the
following:

class shape

public:

virtual void draw() = 0;

//Function to draw the shape. Note that this is a

//pure virtual function.

virtual void move(double x, double y) = 0;

//Function to move the shape at the position (x, y).

//Note that this is a pure virtual function.

};

Because an abstract class is not a complete class, as it (or its


implementation file) does not contain the definitions of certain functions,
you cannot create objects of that class. Note that in addition to the pure
virtual functions, an abstract class can contain instance variables,
constructors, and functions that are not pure virtual. However, the
abstract class must provide the definitions of constructor and functions
that are not pure virtual.

Array-Based Lists

List: A collection of elements of the same type.

The length of a list is the number of elements in the list.

Following are some of the operations performed on a list:

1. Create the list. The list is initialized to an empty state.

2. Determine whether the list is empty.

3. Determine whether the list is full.

4. Find the size of the list.

5. Destroy, or clear, the list.

6. Determine whether an item is the same as a given list element.

7. Insert an item in the list at the specified location.

8. Remove an item from the list at the specified location.

9. Replace an item at the specified location with another item.

10. Retrieve an item from the list from the specified location.

11. Search the list for a given item.

To maintain and process the list in an array, we need the following three
variables:

• The array holding the list elements

• A variable to store the length of the list (that is, the number of list

elements currently in the array)

• A variable to store the size of the array (that is, the maximum number of

elements that can be stored in the array)

UML class diagram of the class arrayListType.


1, IsEmty

template <class elemType>

bool arrayListType<elemType>::isEmpty() const

return (length == 0);

template <class elemType>

bool arrayListType<elemType>::isFull() const

return (length == maxSize);

2. Listsize

template <class elemType>

int arrayListType<elemType>::listSize() const

return length;

3. maxListSize
template <class elemType>

int arrayListType<elemType>::maxListSize() const

return maxSize;

4. print

The member function print outputs the elements of the list. We assume
that the output

is sent to the standard output device.

template <class elemType>

void arrayListType<elemType>::print() const

for (int i = 0; i < length; i++)

cout << list[i] << " ";

cout << endl;

5. sItemAtEqual

template <class elemType>

bool arrayListType<elemType>::isItemAtEqual

(int location, const elemType& item) const

return(list[location] == item);

The body of function isItemAtEqual has only one statement, which is a


comparison

statement. It is easy to see that this function is of O(1).

6. InsertAt
The function insertAt inserts an item at a specific location in the list. The
item to beinserted and the insert location in the array are passed as
parameters to this function. To insert the item somewhere in the middle of
the list, we must first make room for the new item. That is, we need to
move certain elements right one array slot. Suppose that the data
member list of an arrayListType object is as shown in Figure. The number
of elements currently in the list is 6, so length is 6. Thus, after inserting a
new element, the length of the list is 7. If the item is to be inserted at, say
location 6, we can easily accomplish this by copying the item into list[6].
On the other hand, if the item is to be inserted at, say location 3, we first
need to move elements list[3], list[4], and list[5] one array slot right to
make room for the new item. Thus, we must first copy list[5] into list[6],
list[4] into list[5], and list[3] into list[4], in this order. Then we can copy
the new item into list[3].

template <class elemType>

void arrayListType<elemType>::insertAt

(int location, const elemType& insertItem)

if (location < 0 || location >= maxSize)

cerr << "The position of the item to be inserted "

<< "is out of range" << endl;

else

if (length >= maxSize) //list is full

cerr << "Cannot insert in a full list" << endl;

else

for (int i = length; i > location; i--)

list[i] = list[i - 1]; //move the elements down

list[location] = insertItem; //insert the item at the

//specified position
length++; //increment the length

} //end insertAt

7. insertEnd:

This function is called to indsert element at the end

template <class elemType>

void arrayListType<elemType>::insertEnd(const elemType& insertItem)

if (length >= maxSize) //the list is full

cerr << "Cannot insert in a full list" << endl;

else

list[length] = insertItem; //insert the item at the end

length++; //increment the length

} //end insertEnd

8. removeAt

This function is used to remove an item at a given location

template <class elemType>

void arrayListType<elemType>::removeAt(int location)

if (location < 0 || location >= length)

cerr << "The location of the item to be removed "

<< "is out of range" << endl;

else

for (int i = location; i < length - 1; i++)


list[i] = list[i+1];

length--;

} //end removeAt

9. retrieveAt

This function is used to retrieve an item at a given location

template <class elemType>

void arrayListType<elemType>::retrieveAt

(int location, elemType& retItem) const

if (location < 0 || location >= length)

cerr << "The location of the item to be retrieved is "

<< "out of range." << endl;

else

retItem = list[location];

} //end retrieveAt

10. replace

This function is used to replace an item with other item

template <class elemType>

void arrayListType<elemType>::replaceAt

(int location, const elemType& repItem)

if (location < 0 || location >= length)

cerr << "The location of the item to be replaced is "

<< "out of range." << endl;

else

list[location] = repItem;
} //end replaceAt

11. clear list

This function ius used to Clear the items in a list:

template <class elemType>

void arrayListType<elemType>::clearList()

length = 0;

} //end clearList

You might also like