You are on page 1of 73

124 Chapter 9 - Classes and Objects

Chapter 9

Classes and Objects

The objectives of this chapter is to learn

1 Defining and instantiating classes in C++.

1 Achieving reusability through classes.


1 Nesting of the member functions.

1 Static variables and member functions in C++.

1 Creating array of objects, concept behind passing and returning of objects.


1 Need of friend functions and this pointers in C++.

9.1. INTRODUCTION

O
bject-oriented programming (OOP) is a conceptual approach to design programs. All of the
features of OOP discussed so far have one thing in common i.e., the vehicle to implement them.
This vehicle is class. It is a new data type similar to structures. Its significance is highlighted by
the fact that Stroustrup initially gave the name C with classes to his new language. It is a new way of
creating and implementing a user-defined data type.

9.2. INTRODUCTION TO CLASSES


A class is a logical method to organize data and functions in the same structure. They are declared

124 Chapter 9 - Classes and Objects


BSIT 33 OOPS with C++ 125

using keyword class, whose functionality is similar to that of the C keyword struct, but with the possibility
of including functions as members, instead of only data. Its format is as shown in the Fig. 9.1.

class class_name {
permission_label_1:
member1;
permission_label_2:
member2;
...
} object_name;

Fig. 9.1. Syntax of class definition

where class_name is a name for the class (user defined type) and the optional field object_name is
one, or several, valid object identifiers. The body of the declaration can contain members, that can be
either data or function declarations, and optionally permission labels, that can be any of these three
keywords: private, public or protected.
They make reference to the permission, which the following members acquire:

l private members of a class are accessible only from other members of their same class or from
their friend classes.
l protected members are accessible from members of their same class and friend classes, and
also from members of their derived classes.

l Finally, public members are accessible from anywhere the class is visible.

If we declare members of a class before including any permission label, the members are considered
private, since it is the default permission that the members of a class declared with the class keyword
acquire.For example:

class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void);
} rect;

declares class CRectangle and an object called rect of this class (type). This class contains four
members: two variables of type int (x and y) in the private section (because private is the default permission)
126 Chapter 9 - Classes and Objects

and two functions in the public section: set_values() and area(), of which we have only included the
prototype.
Notice the difference between class name and object name: In the previous example, CRectangle was
the class name (i.e., the user-defined type), whereas rect was an object of type CRectangle. The same
difference also exists between int and a.

int a;
int is the class name (type) and a is the object name (variable).

On successive instructions in the body of the program we can refer to any of the public members of
the object rect as if they were normal functions or variables, just by putting the objects name followed by
a point and then the class member (like we did with C structs). For example:
rect.set_value (3,4);

myarea = rect.area();

but we will not be able to refer to x or y since they are private members of the class and they could
only be referred from other members of that same class

9.3. WRITING A C++ PROGRAM WITH CLASSES


The previous example of the class Crectangle can be implemented through the following C++ program:

// classes example
#include <iostream.h>

class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void)
{
return (x*y);
}
};.

void CRectangle::set_values (int a, int b) {


x = a;
y = b;
}
BSIT 33 OOPS with C++ 127

int main ()
{
CRectangle rect;
rect.set_values (3,4);
cout << "area: " << rect.area();
}
The output of the program is area:12.
The new thing in the above program is the operator :: of scope included in the definition of set_values().
It is used to declare a member of a class outside it. Notice that we have defined the behavior of function
area() within the definition of the CRectangle class - given its extreme simplicity. Whereas set_values()
has only its prototype declared within the class but its definition is outside. In this outside declaration we
must use the operator of scope ::. The general form of a member function definition outside the class is as
shown in the Fig. 9.2.

return-type class-name : : function-name(argument declaration)


{
function body
}

Fig. 9.2. Defining member function outside the class

The membership label class-name:: tells the compiler that the function function-name belongs to the
class class-name. The scope of the function is restricted to the class-name specified in the header line.
The symbol :: is called the scope resolution operator.

The scope operator (::) specifies the class to which the member being declared belongs, granting
exactly the same scope properties as if it was directly defined within the class. For example, in the
function set_values() of the previous code, we have referred to the variables x and y, that are members of
class CRectangle and that are only visible inside it and its members (since they are private).

The only difference between defining a class member function completely within its class and to
include only the prototype, is that in the first case the function will automatically be considered inline by
the compiler, while in the second it will be a normal (not-inline) class member function.
The reason x and y are made as private members, because we have already defined a function to
introduce those values in the object (set_values()) and therefore the rest of the program does not have a
way to directly access them. Perhaps in a so simple example as this you do not see a great utility protecting
those two variables, but in greater projects it may be very important that values cannot be modified in an
unexpected way (unexpected from the point of view of the object).
128 Chapter 9 - Classes and Objects

9.4. REUSABILITY THROUGH CLASSES


One of the greater advantages of a class is that we can declare several different objects from it. For
example, following with the previous example of class CRectangle, we could have declared the object
rectb in addition to the object rect i.e.,
// class example
#include <iostream.h>

class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void) {return (x*y);}
};

void CRectangle::set_values (int a, int b) {


x = a;
y = b;
}

int main () {
CRectangle rect, rectb;
rect.set_values (3,4);
rectb.set_values (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;

}
The output of the program is : rect area: 12

rectb area: 30

In the program call to rect.area() does not give the same result as the call to rectb.area(). This is
because each object of class CRectangle has its own variables x and y, and its own functions set_value()
and area(). This forms the basis of the concept of object and object-oriented programming in which data
and functions are properties of the object, instead of the usual view of objects as function parameters in
structured programming.Hence, the class (type of object) to which we were talking about is CRectangle,
of which there are two instances, or objects: rect and rectb, each one with its own member variables and
member functions.
BSIT 33 OOPS with C++ 129

9.5. MAKING AN OUTSIDE FUNCTION INLINE


The main objective of the OOP is to achieve encapsulation. Hence the implementation should be
separated from the interface. This means we should be able to define the member functions outside using
:: operator. But the disadvantage of this approach is that the function will not be inline.

However, C++ provides a mechanism for making outside function inline by using the qualifier inline in
the header line of the function definition. Consider the previous example of rectangle being implemented
using the inline function being defined outside.

// class example
#include <iostream.h>

class CRectangle
{
int x, y;
public:
void set_values (int,int);
int area (void) {return (x*y);}
};

inline void CRectangle::set_values (int a, int b) {


x = a;
y = b;
}

int main ()
{
CRectangle rect, rectb;
rect.set_values (3,4);
rectb.set_values (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;

Here, eventhough the function set_values is defined outside the class CRectangle, the qualifier inline
makes it an inline function i.e. expanded in line when it is invoked. (The compiler replaces the function call
with the corresponding function code).
130 Chapter 9 - Classes and Objects

9.6. NESTING OF MEMBER FUNCTIONS


We have discussed that an object of that class and the dot operator invokes the member function of a
class. Another way of calling the member function is using its name inside another member function of
the same class. This mechanism is known as Nesting of functions. This feature is illustrated as shown in
the following program.

// class example
#include <iostream.h>

class CRectangle
{
int x, y;
public:
int set_values (int,int);
int area (void) {return (x*y);}
};

inline int CRectangle::set_values (int a, int b)


{
x = a;
y = b;
return(area()); //calling member function within anothernesting

int main ()
{
CRectangle rect ;
cout << rect area1 << rect.set_values (3,4) << endl;
cout << rect area2 << rect.set_values (5,6) << endl;

Here, the member function area is not invoked by the object rect, rather it is invoked within the
function set_values. This illustrates the concept of Nesting of functions.

9.7. MEMORY ALLOCATION FOR OBJECTS


The member functions are created and placed in the memory space only once when they are defined
BSIT 33 OOPS with C++ 131

as a part of class specification. Since all the objects belonging to that class use the same member functions
, no separate space is allocated for the member functions when the objects are created. Only space for
data members is allocated separately for each object. Separate memory locations for the objects are
essential because the data members will hold different data values for different objects. This is shown in
the Fig. 9.3.

Common for all objects


Member function 1

Member function 2

memory created when functions defined

Object1 Object 2

Member variable 1 Member variable 1

Member variable 2 Member variable 2

Fig.9.3. Memory Allocation for Objects

9.8. STATIC MEMBERS OF A CLASS


Each object of a class had its own set of public or private data. Each public or private function would
then access the objects own version of the data. However, it may be desirable that one or more common
data fields exist, which are accessible to all objects of the class. An example of such a situation is the
name of the startup directory in a program which recursively scans the directory tree of a disk. A second
example is a flag variable, which states whether some specific initialization has occurred: only the first
object of the class would then perform the initialization and would then set the flag to done. C++
therefore allows static data and functions, which are common to all objects of a class.

9.8.1 Static data members


A data member of a class can be declared static; be it in the public or private part of the class
132 Chapter 9 - Classes and Objects

definition. Such a data member is created and initialized only once; in contrast to non-static data members,
which are created for each object of the class. A static data member is created when the program starts
executing; but is nevertheless part of the class.
Unlike the non-static data members, a static member has the following distinguishing characteristics.

It is initialized to zero when the first object of its class is created. No other
initialization is permitted.
Only one copy of that member is created for the entire class and is shared
by all the objects of that class, no matter how many objects are created.

Since static data members are associated with the class itself rather than with any class object, they
are also known as class variables. The non-static data members are associated with each class object
rather than the class itself and are known as instance variables.
The following program illustrates the use of static data members.

#include <iostream.h>
class item
{
static int count;
public:
counter()
{

count++;
cout << counter is : << count;
}

};

main()
{
item i1,i2,i3;
i1.counter();
i2.counter();
i3.counter();
}

The output would be:

Counter is : 1
Counter is : 2
Counter is : 3
BSIT 33 OOPS with C++ 133

the static variable count is initialized to zero when the first object i.e., i1 is created. The count is incremented
when the count () is called. Since 3 objects are created and count () is called 3 times, final counter value
is 3. Here the count variable is shared among all the objects, as it is a static variable. This can be
demonstrated as shown in the Fig. 9.4.

i1 i2 i3

count

Fig.9.4. Sharing of static data member

Static data members declared public can be addressed anywhere in the program by using their name,
preceded by the class name and by the scope resolution operator. This is illustrated in the following code
fragment:

class Test
{
public:
static int public_int;
private:
static int private_int;
}

int main ()
{
Test:: public_int = 145; // ok :: is used to access the
static data member
Test:: private_int = 12; // wrong, dont touch the private parts
return (0);
}

9.8.2. Static member functions


Besides static data, C++ also allows the definition of static functions. Similar to the concept of static
data, static functions apply to all objects of the class. The static functions can therefore address only the
134 Chapter 9 - Classes and Objects

static data of a class; non-static data are unavailable to these functions. Non-static data cannot be addressed,
as they do not belong to a specific object. Static functions cannot call non-static functions of the class.
Functions which are static and which are declared in the public part of a class definition can be called
without specifying an object of the class. This is illustrated in the following code fragment:

class Directory
{
public:
// heres the static public function
static void setpath (char const *newpath);

private:
// the static string
static char path [];
};

// definition of the static variable


char Directory::path [199] = /usr/local;

// the static function


void Directory::setpath (char const *newpath)
{
strncpy (path, newpath, 199);
}

// example of the usage


int main ()
{
// Alternative (1): calling setpath() without
// an object of the class Directory
Directory:: setpath (/etc); // Use :: to call static functions
// Alternative (2): with an object
Directory dir;
dir.setpath (/etc);
return (0);
}

In the example above the function setpath() is a public static function. C++ also allows private static
functions: in that case, the function can only be called from other member functions but not from outside
BSIT 33 OOPS with C++ 135

the class. The private static functions could only (a) access static variables, or (b) call other static
functions: non-static code or data members would still be inaccessible.

9.9. ARRAYS OF OBJECTS


Class is a template, which can contain data items as well as member functions to operate on the data
items. Several objects of the class can also be declared and used. An array of objects can be declared and
used just like an array of any other data type. For e.g., the following program demonstrates the concept of
array of objects.

class student
{
public :
void getdetails();
void printdetails();
private :
int rollno;
char name[25];
int marks[6];
float percent;
};

void student :: getdetails()


{
int ctr,total;

cout << enter rollno;


cin >> rollno ;
cout << enter name;
cin >> name;
cout << enter 6 marks ;
for( ctr = 1 ;ctr <= 6 ; ctr++ )
{
cin >> marks[ctr];
total = total + marks[ctr];
}
136 Chapter 9 - Classes and Objects

percent = total / 6;
}

void student :: printdetails ()


{
cout << rollno << name << percent ;
}
void main()
{
student records[50];
int x=0;
cout << How many students ;
cin >> x;

for ( int i =1; i<= x; i++)


{
records[i].getdeatils();
}
for ( int i =1; i<= x; i++)
{
records[i].printdeatils();
}
}

Here, an array of 50 student objects is created. The members of the Student class are accessed, using
the array name qualified by a subscript. The statement,

records[y].printdetails();
invokes the member functions printdetails() for the object given by the subscript y. For different values
of subscript, it invokes the same member function, but for different objects. An array of objects is stored
inside the memory in the same way as a multi-dimensional array. For e.g., the storage for array records is
as shown in the Fig. 9.5.
BSIT 33 OOPS with C++ 137

Rollno
Name record[0]
Marks
Percentage
Rollno
record[1]
Name
Marks
Percentage
Repeats for other
. objects record[2] to
Rollno record[48]

Name
Marks record[49]

Percentage

Fig. 9.5.. Storage of data itemsof an object array

9.10. PASSING AND RETURNING OBJECTS


Objects can be passed to a function and returned back just like normal variables. When an object is
passed by value , the compiler creates another object as a formal variable in the called function and copies
all the data members from the actual variable to it. Objects can also be passed by address, which will be
discussed later. For e.g.,

class check
{
public :
check add(check);
void get()
{
cin >> a;
}
138 Chapter 9 - Classes and Objects

void put()
{
cout << a;
}
private :
int a;

};

void main()
{
check c1,c2,c3;
c1.get();
c2.get();
c3 = c1.add(c2);
c3.put();
}

check check :: add ( check c2)


{
check temp;
temp.a = a + c2.a;
return ( temp);
}

The above example creates three objects of class check. It adds the member variable of two classes,
the invoking class c1 and the object that is passed to the function , c2 and returns the result to another
object c3.
You can also notice that in the class add() the variable of the object c1 is just referred as a where
as the member of the object passed .i.e. c2 is referred as c2.a . This is because the member function will
be pointed by the pointer named this in the compiler where as what we pass should be accessed by the
extraction operator .. we may pass more than one object and also normal variable. we can return an
object or a normal variable from the function. We have made use of a temporary object in the function
add() in order to facilitate return of the object.

9.11. POINTERS TO OBJECTS


Passing and returning of objects is, however, not very efficient since it involves passing and returning
BSIT 33 OOPS with C++ 139

a copy of the data members. This problem can be eliminated using pointers. Like other variables, objects
of class can also have pointers. Declaring a pointer to an object of a particular class is same as declaring
a pointer to a variable of any other data type. A pointer variable containing the address of an object is said
to be pointing to that object. Pointers to objects can be used to make a call by address or for dynamic
memory allocation. Just like structure pointer, a pointer to an object also uses the arrow operator to access
its members. Like pointers to other data types, a pointer to an object also has only one word of memory.
It has to be made to point to an already existing object or allocated memory using the keyword new.For
e.g.,

string str; // Object


string *sp; // Pointer to an object

sp = &str; // Assigns address of an existing object


sp = new string // Allocates memory with new.

A simple example of using pointers to objects is given below.

class player
{
public :
void getstats(void);
void showstats(void);
private :
char name[40];
int age;
int runs;
int tests;
float average;
float calcaverage(void);
};.

void main()
{
player Sachin;
Sachin.getstats();
Sachin.showstats();
player *Dravid;
Dravid = new player;
140 Chapter 9 - Classes and Objects

Dravid->getstats();
Dravid->showstats();
}

9.12. FRIENDLY FUNCTIONS


One of the main features of OOP is information hiding. A class encapsulates data and methods to
operate on that data in a single unit. The data from the class can be accessed only through member
functions of the class. This restricted access not only hides the implementation details of the methods and
the data structure, it also saves the data from any possible misuse, accidental or otherwise. However, the
concept of data encapsulation sometimes takes information hiding too far. There are situations where a
rigid and controlled access leads to inconvenience and hardships.
For instance, consider a case where a function is required to operate on object of two different
classes. This function cannot be made a member of both the classes. What can be done is that a function
can be defined outside both the classes and made to operate on both. That is, a function not belonging to
either, but able to access both. Such a function is called as a friend function. In other words, a friend
function is a nonmember function, which has access to a classs private members. It is like allowing
access to ones personal belongings to a friend.
A friend function possesses certain special characteristics:

l It is not in the scope of the class to which it has been declared as friend.

l Unlike member functions, it cannot access the member names directly and has to use an object
name and dot membership operator with each member name.

l It can be declared either in the public or private part of the class without affecting its meaning.

l Usually, it has objects as arguments.


The following example defines a friend function to access members of two classes.

class Bclass; // Forward Declaration


class Aclass
{
public :
Aclass(int v)
{
Avar = v;
}
BSIT 33 OOPS with C++ 141

friend int addup(Aclass &ac, Bclass &bc);


private :
int Avar;

};

class Bclass
{
public :
Bclass(int v)
{
Bvar = v;
}

friend int addup(Aclass &ac, Bclass &bc);


private :
int Bvar;

};

int addup(Aclass &ac, Bclass &bc)


{
return( ac.Avar + bc.Bvar);
}

void main()
{
Aclass aobj;
Bclass bobj;
int total;
total = addup(aobj,bobj);
}

The program defines two classes- Aclass and Bclass. It also has constructors for these classes. A
friend function, addup(), is declared in the definition of both the classes, although it does not belong to
either. This friend function returns the sum of the two private members of the classes. Notice, that this
142 Chapter 9 - Classes and Objects

function can directly access the private members of the classes. To access the private members, the
name of the member has to be prefixed with the name of the object, along with the dot operator.
The first line in the program is called forward declaration. This is required to inform the compiler that
the definition for class Bclass comes after class Aclass and therefore it will not show any error on
encountering the object of Bclass in the declaration of the friend function. Since it does not belong to both
the classes, while defining it outside we do not give any scope resolution and we do not invoke it with the
help of any object. Also, the keyword friend is just written before the declaration and not used while
defining.
Excessive use of friend over many classes suggests a poorly designed program structure. But sometimes
they are unavoidable.

9.13. CONST MEMBER FUNCTIONS


If a member function does not alter any data in the class, we may declare it as a const member
function as follows:

Return-type function-name (argument list) const;

The qualifier const is appended to the function prototypes (both declaration and definition). The compiler
will generate an error message if such functions try to alter the data values. For e.g., the function
void add (int, int) const;
can not alter the values within the function.

9.14. this POINTER


When several instances of a class come into existence, it follows that each instance has its own copy
of member variables. Even though the class member functions are encapsulated with the data members
inside the class definition, it would be very inefficient in terms of memory usage to replicate all these
member functions and store the code for them within each instance. Consequently, only one copy of each
member function per class is stored in memory, and must be shared by all of the instances of the class.
But this poses a big problem for the compiler: How can any given member function of a class knows
which instance it is supposed to be working on ? In other words, a class member function refers to the
members directly without regard to the fact that when the instantiations occur each data member will
have a different memory address. In other words, all the compiler knows is the offset of each data
member from the start of the class.
BSIT 33 OOPS with C++ 143

The solution to this problem is that, each member function does have access to a pointer variable that
points to the instance being manipulated. Fortunately this pointer is supplied to each member function
automatically when the function is called, so that this burden is not placed upon the programmer.
This pointer variable has a special name this (reserved word). Even though this pointer is implicitly
declared, you always have access to it and may use the variable name anywhere you seem appropriate.For
e.g.

class try_this
{
public :
void print();
try_this add(int);
private :
int ivar;

};
void print()
{
cout << ivar;
cout << this -> ivar ;
}

The function print refers to the member variable ivar directly. Also, an explicit reference is made
using the this pointer. This special pointer is generally used to return the object, which invoked the member
function. For example,
void main()
{
try_this t1,t2;
t2 = t1.add(3);
t2.print();
}

try_this try_this :: add(int v)


{
ivar = ivar + v;
return ( *this);
}
144 Chapter 9 - Classes and Objects

In the above example if ivar for t1 is 10 and value in v is 2, then the function add() adds them and ivar
for t1 becomes 12 . We want to store this in another object t2, which can be done by returning the object
t1 using *this to t2. The result of t2.print() now will be 12.
Sometimes a member function needs to make a copy of the invoking instance so that it can modify
the copy without affecting the original instance. This can be done as follows :
try_this temp(*this);
try_this temp = *this ;
This is known as dereferencing this pointer.

To summarize..
In this chapter, implementation of elementary concepts of Object Oriented Programming such as
classes and objects in C++ has been explained. The concepts have been also illustrated with relevant
program examples.Along with the mechanism for creating array of objects, pointer to the objects has
been discussed. The usefulness of this pointer in C++ is highlighted.
Solve the following jumbles

SASLC

TVRAIPE

SETNGNI

CTBOEJRARYA

IVDO

NOCTS

After collecting all the letters in the boxes rearrange them so as to get a
meaningful sentence for the following

+ + I S B C E N
BSIT 33 OOPS with C++ 145

Programming Exercise
Define a class to represent a bank account. Include the following data members: Name of the depositor,
Account number, Type of account, Balance amount. Include the following member functions: To assign
initial values, To deposit an amount, To withdraw an amount after checking the balance, to display the
name and balance. Write a main program to test the program. Modify this program so that it can handle
10 customers.

Answers
146 Chapter 10 - Constructors and Destructors

Chapter 10

Constructors and Destructors

The objectives of this chapter are to learn:

l Working and usefulness of constructors and destructors in C++.

l The need of default and copy constructors.


l Using constructors to allocate memory while creating objects.

10.1. INTRODUCTION

I
n the procedural programming extensions in C++, we could initialize a variable at the point it was
declared. This is easily done for built-in types. But we have to declare objects and initialize them
separately. This can cause hard-to-find errors due to uninitialized variables, and makes it difficult to
initialize objects that are declared in static memory. Since C++ strives to support user-defined types as
well as it supports built-in types, it has a feature to enable the initialization of objects at the point of
declaration.

Another problem exists with multiple initializations of objects. A method can be written for initializing
the objects. Unfortunately, the initialization method could be called at any time, thus allowing the initialization
to be changed at some later point. To solve these problems, C++ provides mechanism for defining
constructor, methods that are automatically called whenever a new instance statically or dynamically
allocated, is created (loaded into the memory). Similarly we have destructors, methods called automatically
whenever a particular object is destroyed (removed from the memory).

146 Chapter 10 - Constructors and Destructors


BSIT 33 OOPS with C++ 147

10.2. CONSTRUCTORS
A constructor is a special member function that is invoked automatically, whenever an instance of
the class to which it belongs is created. The execution of such a function guarantees that the instance
variables of the class will be initialized properly.

A constructor function is unique from all other functions in a class because it is not called using some
instance of the class, but is invoked whenever we create an object of that class. It is called constructor as
it is used to construct the data members of the class.
A constructor is declared and defined as shown in the Fig. 10.1.

Class class_name
{
//data members

public: class_name(argument_list); // constructor declaration

// other member function


};
class_name:: class_name(argument_list) // Constructor definition
{
//Initialize the data members
}

Fig 10.1. Syntax of the constructor

As seen from the above format, the constructors should have the following characteristics.

They should be declared in the public section.


Unlike other member functions, they should have same name as that of the
class.
They do not have any return types and therefore, can not return values.
They can not be inherited, though a derived class can call the base class
Constructor.
They can have default arguments.
An object with a constructor (or destructor) can not be used as a member of a
union.
We cannot refer to their addresses.
They make implicit calls to the operators new and delete when memory
allocation is required. When a constructor is declared for a class, initialization
of the class objects become mandatory.
148 Chapter 10 - Constructors and Destructors

For e.g. the following program segment that illustrates the use of constructors
// constructor example

class CRectangle
{
int x, y;
public:
CRectangle(int,int); // constructor declaration
int area (void);
};

CRectangle :: CRectangle(int m, int n)// constructor definition


{
x=m;
y=n;
}

main()
{
CRectangle rec(4,5); //constructor called
cout << Ares is << rec.area();
}

Here, constructor CRectangle() is called when the object rec is created. Constructors may or may not
have any arguments. The constructors that can take arguments are called parameterized constructors.
In the previous example constructor CRectangle is a parameterized constructor as it takes two arguments
m and n.

In the case of parameterized constructors the initial values must be passed as arguments to the constructor
function, when an object is declared. This can be done in two ways:

l By calling the constructor explicitly


l By calling the constructor implicitly

The following declaration illustrates the first method:

CRectangle rect=CRectangle(5,6); // explicit call


This statement creates an object rect and passes the arguments 5 and 6 to the parameterized constructor
Crectangle.The second method is implemented as follows:
BSIT 33 OOPS with C++ 149

CRectangle rect(5,6); //implicit call

This method is known as shorthand method, which is used very often and is easy to implement.

10.3. DEFAULT CONSTRUCTORS


When we include a constructor that takes at least one parameter, we cant allocate an array of
structures. This is true for any type of array declaration, be it static or dynamic. For instance, with the
constructor being defined for class Rectangle, the following implementation would issue several compile-
time errors:

Rectangle boxes[10]; // Error


Rectangle* moreBoxes = new Rectangle[10]; // Error

In order to get around these restrictions, we require a default constructor. A default constructor is a
constructor that takes no arguments, by providing a default parameter for each argument. We could
define a default constructor for the CRectangle class by providing default parameters for the constructor:
class CRectangle
{
public:
CRectangle(double w = 0, double h = 0);
.
};

We can declare arrays of CRectangles, and dynamically allocate them as well. We can also declare an
object without providing any constructor arguments and it will be initialized using the default values. With
our default constructor, if we wrote the following code:

CRectangle box;
cout << The area of our box is << box.area() << .\n;
we would see this as the output:
The area of our box is 0.
Here, the default values (m=0 and n=0) are used for computation. Whenever we supply no constructor
for a class, the compiler provides a default constructor for us. This automatically generated default
constructor simply calls the default constructor for each data member of the class. If each data member
of the class does not have a default constructor, the compiler will issue an error.
150 Chapter 10 - Constructors and Destructors

10.4. INITIALIZATION LISTS


Some data members need to be initialized at the time the object is created. For example, if a class
contains a data member, which is a reference, a constant, or an object without a default constructor, the
compiler would flag an error when trying to create an instance of the object. For example, suppose we
had a class to represent a Point, which had no default constructor, and a class to represent a Color. If we
defined a class called Circle we would have the following data members.

class Circle
{
...
private:
Color& color; // Note: the color is a reference
Point center; // Note: Point has no default constructor
const double radius; // Note: the radius is a constant
};

constructor for a Circle could look something like this:


Circle::Circle(Color& c, const Point& cent, const double r)
{
color = c; // Error: too late to initialize a reference
center = cent; // Error: no default constructor for a Point
radius = r; // Error: too late to initialize a constant
}

Unfortunately, even within a constructor, it is too late to initialize these three types of members. They
must be initialized before the constructor gets called. C++ allows us to do this with initialization lists. We
can provide an initialization list for the Circle class by altering the constructor like this:
Circle::Circle(Color& c, const Point& cent, const double r)
: color(c), center(cent), radius(r)
{
// empty body, since everything is taken care of
// by the initialization list. You can have stuff here, though..
}
BSIT 33 OOPS with C++ 151

10.5. COPY CONSTRUCTOR


Initialization takes place only once when the object is created, whereas assignment can occur whenever
desired in the program. Therefore, they differ conceptually. For e.g.,
String s1(this is a string);

String s2(s1); //Initialization

The first statement declares an object s1 and passes a string as an argument to its constructor. The
data members of s1 are initialized using the String. The second statement declares another object s2 and
contains an object as its argument. Since there is no constructor having an object as its formal argument,
the compiler itself initializes the second objects data members with those of the formal object.Since one
of the members is a pointer, the problem of assignment arises here. Defining a constructor function that
takes object as its argument solves the problem of initialization. This constructor is also called copy
constructor . It is called in three contexts:
l When one object of a class is initialized to another of the same class.

l When an object is passed by value as an argument to a function.

l When a function returns an object.


All these situations result in a copy of one object to another. Hence, a copy constructor is very important
if the class contains pointer as data member. A copy constructor for the above example is given below:

String (const String &s2)


{

size = s2.size;

cptr = new char[size + 1];


strcpy(cptr,s2.cptr);

Copy constructor can be invoked as


String s1(10,hello);

String s2(s1) or String s2=s1;

It is just like a normal constructor but the argument here is an object of the class. This existing instance
should never be modified by the copy constructor function, so the keyword const should be used in the
function signature. The process of initializing the copy constructor is known as copy initialization.
152 Chapter 10 - Constructors and Destructors

10.6. DYNAMIC CONSTRUCTORS


The constructors can also be used to allocate memory while creating objects. This will enable the
system to allocate the right amount of memory for each object when the objects are not of the same size.
This leads to the efficient utilization of the memory. Allocation of the memory to the objects at the time of
their construction is known as dynamic construction of objects. This concept is illustrated in the following
program.

// Dynamic constructors program

#include<iostream.h>
#include<string.h>
class string
{
char *name;
int length;
public:
string() /constructor1
{
length=0;
name= new char[length + 1]; // one extra for \0
}
string(char *s)
{
length=strlen(s);
name=new char[length + 1];
strcpy(name,s);
}
void display(void);
{
cout << name << endl;
}
void join(string & a, string &b);
}

void string :: join(string &a,string &b)


{
length=a.length + b.length;
delete name;
name=new char [length + 1]; //dynamic allocation
strcpy(name,a.name);
BSIT 33 OOPS with C++ 153

strcat(name,b.name);
};

main()
{
char * first=hello;
string name1(first),name2(world),s1;
s1.join(name1,name2);
name1.display();
name2.display();
s1.display();

The output of the following program is:


hello
world
helloworld

The program uses two constructors. The first is an empty constructor that allows the declaration of
arrays of strings. The second initializes the length of the string, allocates the necessary space for the
string to be stored and creates the string itself. One additional character space is allocated to hold the end-
of-string character \0.

The member function join() concatenates two strings. It estimates the combined length of the string to
be joined, allocates the memory for the combined string and then creates the same using the string
functions strcpy() and strcat().

10.7. DESTRUCTORS
A destructor function gets executed whenever an instance of the class to which it belongs goes out of
existence. The primary usage of a destructor function is to release memory space that the instance
currently has reserved. The charectaristics of the destructors are as follows:

l Its name is the same as that of the class to which it belongs, except that the first character of the
name is the symbol tilde ( - ).

l It is declared with no return type ( not even void ) since it cannot ever return a value.
l It cannot be static, const or volatile.
154 Chapter 10 - Constructors and Destructors

l It takes no input arguments , and therefore cannot be overloaded.

l It should have public access in class declaration.


Generally the destructor cannot be called explicitly (directly) from the program. The compiler generates
a class to destructor when the object expires. Class destructor is normally used to clean up the mess from
an object. Class destructors become extremely necessary when class constructor use the new operator,
otherwise it can be given as an empty function. However, the destructor function may be called explicitly
allowing you to release the memory not required and allocate this memory to new resources, in Borland
C++ version 3.1.

The following example illustrates the usage of destructors:

#include<iostream.h>
int count=0;
class alpha
{
public:
alpha()
{
count++;
cout << \n No. of object created << count;
}
~ alpha()
{
cout << \n No. of object destroyed << count;
count;
}
};

main()
{
cout << Enter MAIN << endl;
alpha A1,A2,A3,A4;
{
cout << Enter BLOCK1 << endl;
alpha A5;
}
{
cout << Enter BLOCK2 << endl;
alpha A6;
}
BSIT 33 OOPS with C++ 155

cout << RE-ENTER MAIN < endl;


}

Output of the program is as follows:

ENTER MAIN
No. of object created 1
No. of object created 2
No. of object created 3
No. of object created 4
ENTER BLOCK1
No. of object created 5
No. of object destroyed 5
ENTER BLOCK2
No. of object created 5
No. of object destroyed 5
RE-ENTER MAIN
No. of object destroyed 4
No. of object destroyed 3
No. of object destroyed 2
No. of object destroyed 1

As the objects are created and destroyed, they increase and decrease the count. After the first group
of objects is created, A5 is created, and then destroyed; A6 is created, and then destroyed. Finally, the
rest of the objects are also destroyed. When the closing brace of the scope is encountered, the destructors
for each object in the scope are called.The objects are destroyed in the reverse order of creation.

To summarize..
This chapter explains the working and usefulness of Constructors and Destructors in C++. The usage
of constructors and destructors in C++ has been explained through appopriate programming examples.
The types of constructors such as copy constructor, default constructors and dynamic constructors have
also been discussed.

Say True or False


1. Constructor is invoked explicitly
156 Chapter 10 - Constructors and Destructors

2. Constructors can refer to their addresses.

3. Constructors should be declared in the public section.

4. The initial values are passed as arguments to the constructor function, in case of parameterized constructors.

5. Copy constructor can be used when the function returns an object.

6. There should always be a destructor defined in a class having constructors.

7. The objects are destroyed in the order of their creation.

Programming exercise
Define a class string that includes constructors, which enables us to create an uninitialized string and
also to initialize an object with a string constant at the time of creation. Include a function that adds two
strings to make a third string.

Answers
BSIT 33 OOPS with C++ 157

Chapter 11

Function and Operator Overloading

The objectives of this chapter is to learn

Achieving functional polymorphism through function overloading.


Working and usefulness of functional overloading.
Resolution of an appropriate function from the set of overloaded functions by the compiler.
Overloading constructors
Working and usefulness of operator overloading.
Overloading unary and binary operators.
The important rules for overloading of operators.
Different conversion functions from the object to basic data type and vice- versa.

11.1. INTRODUCTION TO FUNCTION OVERLOADING


Function overloading is a C++ feature that enables using the same function name to create functions
that perform a variety of different tasks. This is known as function polymorphism in OOP.Using the
concept of function overloading, we can design a family of functions with one function name but with
different argument lists.

11.2. DECLARING OVERLOADED FUNCTIONS


A single function name can have multiple declarations. If those declarations specify different function

BSIT 33 OOPS with C++ 157


158 Chapter 11 - Function and Operator Overloading

signatures, the function name is overloaded. The differences can be

1. In number of arguments,
2. Data types of the arguments,

3. Order of arguments, if the number and data types of the arguments are same.

For e.g., consider the following function declarations:

int add( int, int );


Number of arguments
int add( int, int, int );
Data types float add( float, float );
float add( int, float, int );
Order of arguments
float add(int,int,float);
The compiler cannot distinguish if the signature is same and only return type is different. Hence, it is
a must, that their signature is different. The following functions therefore raise a compilation error. For
e.g.,
float add( int, float, int );

int add( int, float, int );

Consider the following example, which raises a given number to a given power and returns the result.

long long_raise_to(int num, int power)


{
long lp = 1;
for( int i = 1; i <= power ; i++ )
{
lp = lp * num ;
}
return lp;
}

double double_raise_to( double num, int power)


{
double dp = 1;
for( int i = 1; i <= power ; i++ )
{
BSIT 33 OOPS with C++ 159

dp = dp * num ;
}
return dp;
}

void main(void)
{
cout<<2 raise to 8 is <<long_raise_to(2,8);
cout<<2.5 raise to 4 is <<double_raise_to(2.5,4);
}

Here, we would require to write 2 different function names. These functions do identical tasks. However
using function overloading techniques we can write the same program in C++ as shown:
long raise_to(int num, int power) // Overloaded functions
{
long lp = 1;
for( int i = 1; i <= power ; i++ )
{
lp = lp * num ;
}
return lp;
}

double raise_to( double num, int power) // Overloaded functions


{
double dp = 1;
for( int i = 1; i <= power ; i++ )
{
dp = dp * num ;
}
return dp;
}

void main(void)
{
160 Chapter 11 - Function and Operator Overloading

cout<<2 raise to 8 is <<raise_to(2,8);


cout<<2.5 raise to 4 is <<raise_to(2.5,4);
}

The main () function makes two calls to the function raise_to () once with 2 integers as arguments
and again with an integer and a double. The compiler uses the context to determine which of the functions
to invoke. Thus, first time it invokes first function and then the second one.

11.3. CALLING OVERLOADED FUNCTIONS


A function call must identify the function being called, based on its name and arguments. For example,
the simple expression f (x) might be a call to a function named f, a function call through a function pointer
named f, a function template named f, the construction of an object of class f, a conversion of x to a type
named f, or the invocation of the function call operator of an object named f. In each situation, the
compiler might use different rules for interpreting f and x.

The compiler first uses the function name and context to create a list of candidate functions. The
number and types of the arguments are used to select the best match from the candidates, and is called.
If no match is found, the compiler reports an error. If more than one match ties for best, the compiler
reports an ambiguity error. For example, consider the following versions of the function sqrt
float sqrt(float);
double sqrt(double);
long double sqrt(long double);
void sqrt(double data[ ],int count);
In a function call to sqrt (say, sqrt(x)), the compiler finds the first suitable object or function named
sqrt.The compiler collects all the overloaded sqrt functions as the candidate list, which has four elements.
The list is then pruned to eliminate functions with the wrong number of arguments. In this case, the array
version of the function is eliminated because the expression has only one argument. Finally, the type of x
is used to determine which function to call: if there is an exact match, the corresponding function is called.
If x is, say, an integer, the compiler reports an error because the three floating-point sqrt functions look
equally good. If x has class type, and the class has a conversion function to one of the floating-point types,
the compiler implicitly calls that conversion and then calls the corresponding function.

11.4. OVERLOADED CONSTRUCTORS


Like any other function, a constructor can also be overloaded with several functions that have the
BSIT 33 OOPS with C++ 161

same name but different types or numbers of parameters. The compiler matches the constructor depending
on how the class is instantiated.
You can also overload the class constructor providing different constructors for when you pass
parameters between parenthesis and when you do not (empty). The following example illustrates the use
of constructors.

// overloading class constructors


#include <iostream.h>

class CRectangle
{
int width, height;
public:
CRectangle ();
CRectangle (int,int);
int area (void) {return (width*height);}
};

CRectangle::CRectangle ()
{
width = 5;
height = 5;
}

CRectangle::CRectangle (int a, int b)


{
width = a;
height = b;
}

int main ()
{
CRectangle rect (3,4); // CRectangle(int,int) is called
CRectangle rectb; // CRectangle() is called
cout << rect area: << rect.area() << endl;
cout << rectb area: << rectb.area() << endl;
}

The output of the program is rect area: 12


rectb area: 25
162 Chapter 11 - Function and Operator Overloading

11.5. PRECAUTIONS WITH FUNCTION OVERLOADING


Function overloading is a boon to designers, since different names for similar functions need not be
thought of, which often is a cumbersome process given that many times people run out of names. But, this
facility should not be overused, lest it becomes an overhead in terms of readability and maintenance. Only
those functions, which basically do the same task, on different sets of data, should be overloaded.

11.6. INTRODUCTION TO OPERATOR OVERLOADING


All computer languages have built in types like integers, real numbers, characters and so on. Some
languages allow us to create our own data types like dates, complex numbers, co-ordinates of a point.
Operations like addition, comparisons can be done only on basic data types and not on derived (user-
defined) data types. If we want to operate on them we must write functions like compare (), add (). For
e.g.,

if (compare (v1, v2) = = 0)


:
:

where v1 and v2 are variables of the new data type and compare () is a function that will contain
actual comparison instructions for comparing their member variables. However, the concept of Operator
Overloading, in C++, allows a statement like

if (v1 = = v2)
:
:

where the operation of comparing them is defined in a member function and associated with comparison
operator(==).

The ability to create new data types, on which direct operations can be performed, is called as
extensibility and the ability to associate an existing operator with a member function and use it with the
objects of its class, as its operands, is called as Operator Overloading.

Operator Overloading is one form of Polymorphism ,an important feature of object-oriented programming
.Polymorphism means one thing having many forms, i.e. here an operator can be overloaded to perform
different operations on different data types on different contexts. Operator Overloading is also called
operational polymorphism. Another form of polymorphism is function overloading.
BSIT 33 OOPS with C++ 163

Here is a list of all the operators that can be overloaded:

+ - * / = < > += -= *= /= << >>

<<= >>= == != <= >= ++ -- % & ^ ! |

~ &= ^= |= && || %= [] () new delete

11.7. OPERATOR OVERLOADING FUNDAMENTALS


The C language uses the concept of Operator Overloading discretely. The asterisk (*) is used as
multiplication operator as well as indirection (pointer) operator. The ampersand (&) is used as address
operator and also as the bitwise logical AND operator. The compiler decides what operation is to be
performed by the context in which the operator is used.

Thus, the C language has been using Operator Overloading internally. Now, C++ has made this facility
public. C++ can overload existing operators with some other operations. If the operator is not used in the
context as defined by the language, then the overloaded operation, if defined will be carried out. For
example, in the statement

x = y + z;

If x, y and z are integer variables, then the compiler knows the operation to be performed. But, if they
are objects of some class, then the compiler will carry out the instructions, which will be written for that
operator in the class.

11.8. DEFINING OPERATOR OVERLOADING


To define an additional task to an operator, we must specify what it means in relation to the class to
which the operator is applied. This is done with the help of a special function, called operator function,
which describes the task. The general form of an operator function is:

return-type classname :: operator op(arg-list)


{
function body //tasks to be carried
}

where return-type is the type of value returned by the specified operation and op is the operation being
overloaded. The keyword operator precedes the op. Operator op is the function name. Operator functions
164 Chapter 11 - Function and Operator Overloading

must be either member functions (non-static) or friend functions. A basic difference between them is that
a friend function will have only one argument for unary operators and two for binary operators, whereas
a member function will have no arguments for unary operator and only one for binary operator. This is
because the object used to invoke the member function is passed implicitly and therefore is available for
the member function. This is not the case with the friend functions. Arguments may be passes either by
value or by reference.

Operator functions are declared in the class using prototypes as follows:

Vector operator + (vector); //vector addition


Vector operator ( ); // unary minus;
Friend vector operator + (vector,vector); //vector addition
Friend vector operator (vector); //unary minus
Vector operator (vector &a); //subtraction
Int operator == (vector); //comparison
Friend int operator == (vector,vector) //comparison

Vector is a data type of class.

11.9. MECHANICS OF OPERATOR OVERLOADING


The process of overloading involves the following steps:
1. Create a class that defines the data type that is to be used in the overloading operation.
2. Declare the operator function op() in the public part of the class. It may be either a member
function or a friend function.
3. Define the operator function to implement the required operations.
Overloaded operator functions can be invoked by the expressions such as
Op x or x op
For unary operators and
X op y
For binary operators.
Op x(x op) would be interpreted as Operator op(x)
For friend functions. Similarly, the expression x op y would be interpreted as eitherx.operator op(y)
in case of member functions , or operator op(x,y) in case of friend functions. When both the forms are
declared, standard argument matching is applied to resolve any ambiguity.
BSIT 33 OOPS with C++ 165

11.10. OVERLOADING UNARY OPERATORS


Consider the unary minus operator. We can overload this operator so that it can be applied to an object
in the similar manner as applied to int or float variable. The unary minus when applied to an object should
change the sign of each of its data items.

The program for overloading unary minus operator is shown below:

#include<iostream.h>
class space
{
int x;
int y;
int z;
public:
void getdata(int a, int b,int c);
void display(void);
void operator (); // overload unary minus
};

void space : : getdata(int a, int b, int c)


{
x=a;
y=b;
z=c;
}

void space : : display(void)


{
cout << x << ;
cout << y << ;
cout << z << endl;
}

void space : : operator () // Defining operator ()


{
x = -x;
y = -y;
z = -z;
}
166 Chapter 11 - Function and Operator Overloading

main()
{
space S;
S.getdata(10,-20,30);
cout << S: ;
S.display();
- S; //activates operator ()
cout << S: ;
s.display ();
}

The program produces the following output:


S: 10 20 30
S: -10 20 30
Here, the operator function operator () changes the sign of the data members of the object S. Since
this function is a member function of the same class, it can directly access the members of the object
when activated it.

11.11. OVERLOADING BINARY OPERATORS


The mechanism used for overloading the unary operator can be extended to overload the binary
operator. This can be illustrated by an example for adding two complex numbers. If A, B, and C are any
three complex numbers, then the arithmetic notation C= A + B can be accomplished by overloading the
operator + using an operator + () function. The following program illustrates how this is accomplished.

// Overloading + operator
#include<iostream.h>
class complex
{
float x;
float y;
public: complex() { }; // constructor 1
complex (float real, float imag) // constructor 2
{
x=real; y=imag;
}
complex operator + (complex);
void display(void);
};
BSIT 33 OOPS with C++ 167

complex complex : : operator + (complex c)


{
complex temp;
temp.x=x + c.x; //float addition
temp.y=y + c.y;
return(temp);
}

void complex :: display(void)


{
cout << x << + j << y << \n;
}

main()
{
complex C1,C2,C3; //invokes constructor 1
C1=complex(2.5,3.5); //invokes constructor 2
C2=complex(1.6,2.7); //invokes constructor 2
C3=C1 + C2; //invokes operator + ()
cout << C1 = ; C1.display();
cout << C2 = ; C2.display();
cout << C3 = ; C3.display();
}

The output of the program is:


C1 = 2.5 + j3.5
C2 = 1.6 + j2.7
C3 = 4.1 + j6.2

Here, the operator+() function adds the two complex values and returns a complex value but receives
only one value as argument. The statement C3 = C1 + C2 invokes this function. The object C1 takes the
responsibility of invoking the function and C2 plays the role of an argument that is passed to the function.
Therefore, in this function, the data members of C1 are accessed directly and the data members of C2 are
accessed using the dot operator. Thus both the objects are available for the function.

11.12. OVERLOADING BINARY OPERATORS USING FRIENDS


Friend functions may be used in the place of member functions for overloading a binary operator. The
168 Chapter 11 - Function and Operator Overloading

only difference being friend function requires two arguments to be explicitly passed to it while a member
function requires only one.
The complex number program discussed in the previous section can be modified using a friend operator
function as follows:

1. Replace the member function declaration by the friend function declaration friend complex
operator + (complex,complex);
2. Redefine the operator function as follows:

complex operator + (complex a, complex b)

{
return complex((a.x + b.x),(a.y + b.y));

Here, the statement C3 = C1 + C2 is equivalent to C3 = operator + (C1, C2); There are certain
situations where friend function is used instead of member function. For instance, the statement A = 2 +
B where A and B are the objects of the same class, will not work. This is because the left operand
responsible for invoking the member function should be an object of the same class. However, the friend
function allows both the approaches.

11.13. RULES FOR OPERATOR OVERLOADING


The important rules for overloading of operators can be summarized as follows:

l The only operators you may overload are the ones from the C++ list and not all of those are
available. You cannot arbitrarily choose a new symbol (such as @) and attempt to overload it.
l Start by declaring a function in the normal function fashion, but for the function name use the
expression:

Operator op
Where op is the operator to be overloaded. You may leave one or more spaces before op.

l The pre-defined precedence rules cannot be changed. i.e. you cannot, for example, make binary
+ have higher precedence than binary *. In addition, you cannot change the associativity of
the operators.
l The unary operators that you may overload are:
BSIT 33 OOPS with C++ 169

-> indirect member operator


! not
& address
* dereference
+ plus
- minus
++ prefix increment
++ postfix increment (possible in AT & T version 2.1)
-- postfix decrement
-- prefix decrement (possible in AT & T version 2.1)
~ ones complement

l The binary operators that you may overload are:

(), [], new, delete, *, / , %, + , - , <<,>>,

<, <=, >, >=, ==,! =, &, ^, |, &&, ||, =, *=, /=, %=, +=, -, =, <<=,
>>=, &=,! =, ^=, ,(Comma).

l The operators that can not be overloaded are:

. direct member
.* direct pointer to member

:: scope resolution

?: ternary
l No default arguments are allowed in overloaded operator functions.

l As with the predefined operators, an overloaded operator may be unary or binary. If it is normally
unary, then it cannot be defined to be binary and vice versa. However, if an operator can be
both unary and binary, then it can be overloaded either way or both.
l The operator function for a class may be either a non-static member or global friend function. A
non-static member function automatically has one argument implicitly defined, namely the address
of the invoking instance (as specified by the pointer variable this). Since a friend function has no
this pointer, it needs to have all its arguments explicitly defined).At least one of the arguments to
the overloaded function explicit or implicit must be an instance of the class to which the operator
belongs.
170 Chapter 11 - Function and Operator Overloading

Consider an example, which depicts overloading of += (Compound assignment), <, >, == (Equality),!=,
+ (Concatenation) using String class.

class String
{
public :
String ();
String ( char str [] );
void putstr();

String operator + (String);


String operator += (String s2);
int operator < (String s2);
int operator > (String s2);
int operator == (String s2);
int operator != (String s2);
private :

char s[100];
};

String::String () // CONSTRUCTOR WITH


{ // NO ARGUMENTS
s[0] = 0;
};

String:: String( char str [] ) // CONSTRUCTOR WITH


{ / ONE ARGUMENT
strcpy(s,str)
};

void String:: putstr() // FUNCTION TO PRINT STRING


{
cout << s ;
};
BSIT 33 OOPS with C++ 171

String String :: operator+(String s2)


{
String temp;

strcpy(temp.s,s);
strcat(temp.s,s2.s);
return (temp);
}

String String :: operator+=(String s2)


{
strcat(s,s2.s);
return (*this);
}

int String::operator < (String s2)


{
return (strcmp (s, s2.s ) < 0);
}
int String::operator > (String s2)
{
return (strcmp (s, s2.s ) > 0);
}
int String::operator == (String s2)
{
return (strcmp (s, s2.s ) == 0);
}
int String::operator != (String s2)
{
return (strcmp (s, s2.s ) != 0);
}

void main()
{
String s1 = welcome ;
String s2 = to the world of c++;
172 Chapter 11 - Function and Operator Overloading

String s3;

cout << endl << s1 = ;


s1.putstr();

cout << endl << s2 = ;


s2.putstr();

s3 = s1 + s2;

cout << endl << s3 = ;


s3.putstr();
String s4;
cout <<endl<< *********************;
s4 = s1 + = s2;
cout << endl << s4 = ;
s4.putstr();
String s5 = Azzzz ;
String s6 = Apple ;
if( s5 < s6 )
{
s5.putstr();
cout << < ;
s6.putstr();
}
else if( s5 > s6 )
{
s5.putstr();
cout << > ;
s6.putstr();
}
else if( s5 == s6 )
{
s5.putstr();
cout << = ;
BSIT 33 OOPS with C++ 173

s6.putstr();
}
else if( s5 != s6 )
{
s5.putstr();
cout << < ;
s6.putstr();
}
}
Output:
S1 = welcome
S2 = to the world of C++
S3 = welcome to the world of c++
**************************
S4 = welcome to the world of c++

11.14. CONVERSION FUNCTIONS


Conversion functions are member functions used for the following purposes:
1. Conversion of object to basic data type.

2. Conversion of basic data type to object.

3. Conversion of objects of different classes.


Conversions of one basic data type to another are automatically done by the compiler using its own
built-in routines (implicit) and it can be forced using built-in conversion routines (explicit). However, since
the compiler does not know anything about user-defined types (classes), the program has to define
conversion functions. For e.g.,
int i = 2, j =19;

float f = 3.5;

i = f; // i gets the value 3 , implicit conversion


f = (float) j; // f gets 19.00, explicit conversion
174 Chapter 11 - Function and Operator Overloading

11.14.1. Conversion from Basic to User-Defined variable


Consider the following example.

class Distance
{
public :
Distance(void) // Constructor with no
{ // argument
feet = 0;
inches = 0.0;
};
Distance(float metres)
{
float f; // Constructor with
f = 3.28 * metres; // one argument
feet = int(f); // also used for
inches = 12 * ( f feet);// conversion
};

void display(void)
{
cout << Feet = << feet <<,;
cout << Inches = << inches << endl;
};
private :
int feet;
float inches;
};

void main (void)


{
Distance d1 = 1.25; // Uses 2nd constructor
Distance d2; // Uses 1st constructor
float m;
d2 = 2.0 ; // Uses 2nd constructor
BSIT 33 OOPS with C++ 175

cout << 1.25 metres is : << d1.showdist() ;


cout << 2.0 metres is : << d2.showdist();
}
Output :
1.25 metres is :FEET = 4 , INCHES = 1.199999
2.0 metres is :FEET = 6 , INCHES = 6.719999

The above program converts distance in metres ( basic data type) into feet and inches ( members of
an object of class Distance ).The declaration of first object d1 uses the second constructor and conversion
takes place. However, when the statement encountered is
d2 = 2.0;

The compiler first checks for an operator function for the assignment operator. If the assignment
operator is not overloaded, then it uses the constructor to do the conversion.

11.14.2. Conversion from User-Defined to Basic data type


The following program uses the program in the previous section to convert the Distance into metres(float).
class Distance
{
public :
Distance(void) // Constructor with no
{ // argument
feet = 0;
inches = 0.0;
};

Distance(float metres)
{
float f; // Constructor with
f = 3.28 * metres; // one argument
feet = int(f); // Also used for inches = 12 * ( f feet); //conversion

};
176 Chapter 11 - Function and Operator Overloading

operator float(void) // Conversion function


{ // from Distance to float
float f;
f = inches / 12;
f = f + float (feet);
return ( f/ 3.28 );
};

void display(void)
{
cout << Feet = << feet <<,;
cout << Inches = << inches << endl;
};
private :
int feet;
float inches;
};

void main (void)


{
Distance d1 = 1.25; // Uses 2nd constructor
Distance d2; // Uses 1st constructor
float m;
d2 = 2.0 ; // Uses 2nd constructor
cout << 1.25 metres is : << d1.showdist ();
cout << 2.0 metres is : << d2.showdist ();
cout << CONVERTED BACK TO METRES ;
m = float ( d1 ); // Calls function explicitly.
cout << d1 = << m;
m = d2; // Calls function explicitly.
cout << d2 = << m;
}

Output:
BSIT 33 OOPS with C++ 177

1.25 metres is :FEET = 4 ,INCHES = 1.199999


2.0 metres is :FEET = 6 ,INCHES = 6.719999

CONVERTED BACK TO METRES


d1 = 1.25
d2 = 2.00

Actually, this conversion function is nothing but overloading the typecast operator float(). The conversion
is achieved explicitly and implicitly.

m = float (d1);
is forced where as , in the second assignment statement

m = d2;

first the compiler checks for an operator function for assignment ( = ) operator and if not found it uses
the conversion function.The conversion function must not define a return type nor should it have any
arguments.

11.14.3. Conversion Between Objects of Different Classes


Since the compiler does not know anything about the user-defined type, the conversion instructions are
to be specified in a function. The function can be a member function of the source class or a member
function of the destination class. We will consider both the cases.

Consider a class DistFeet which stores distance in terms of feet and inches and has a constructor to
receive these. The second class DistMetres store distance in metres and has a constructor to receive the
member.

Conversion function in the Source Class

class DistFeet
{
public :
DistFeet(void) // Constructor with no
{ // argument
feet = 0;
inches = 0.0;
};
178 Chapter 11 - Function and Operator Overloading

DistFeet(int ft,float in)


{
feet = ft;
inches = in;
};

void ShowFeet(void)
{
cout << Feet = << feet << ,;
cout << Inches = << inches << endl;
};
private :
int feet;
float inches;

};

class DistMetres
{
public:
DistMetres(void)
{
metres = 0 ; // constructor 1.
}
DistMetres(float m)
{
metres = m ; // constructor 2.
}
void ShowMetres(void)
{
cout << Metres = << metres << endl;
}

operator DistFeet(void) // conversion


{ // function
BSIT 33 OOPS with C++ 179

float ffeet, inches;


int ifeet;
ffeet = 3.28 * metres;
ifeet = int (ffeet);
inches = 12 * (ffeet ifeet);
return(DistFeet(inches,ifeet);
}

private:
float metres;
};

void main (void)


{
DistMetres dm1 = 1.0;
DistFeet df1;

df1 = dm1 ; // OR df1 = DistFeet(dm1);


// Uses conversion function
dm1.ShowMetres();
df1.ShowFeet();
}
In the above example, DistMetres contains a conversion function to convert the distance from
DistMetres ( source class), to DistFeet ( destination class). The statement to convert one object to
another

df1 = dm1;

calls the conversion function implicitly. It could also have been called explicitly as
df1 = DistFeet(dm1);

Conversion function in the Destination Class

class DistMetres
{
public:
DistMetres(void)
180 Chapter 11 - Function and Operator Overloading

{
metres = 0 ; // Constructor 1.
}
DistMetres(float m)
{
metres = m ; // constructor 2.
}
void ShowMetres(void)
{
cout << Metres = << metres << endl;
}

float GetMetres(void)
{
return(metres);
}

private:
float metres;
};

class DistFeet
{
public :
DistFeet(void) // Constructor1 with no
{ // argument
feet = 0;
inches = 0.0;
};

DistFeet(int ft,float in)


{
feet = ft;
inches = in;
};
BSIT 33 OOPS with C++ 181

void ShowFeet(void)
{
cout << Feet = << feet << endl;
cout << Inches = << inches << endl;
};

DistFeet( DistMetres dm) // Constructor 3


{
float ffeet;
ffeet = 3.28 * dm.GetMetres();
feet = int (ffeet);
inches = 12 * (ffeet ifeet);
};
private :
int feet;
float inches;

};

void main (void)


{
DistMetres dm1 = 1.0; // Uses 2nd constructor
// class DistMetres
DistFeet df1; // Uses 1st constructor
// class DistFeet
df1 = dm1 ; // OR df1 = DistFeet(dm1);
rd
// Uses 3 conversion function
dm1.ShowMetres();
df1.ShowFeet();
}

This program works same as previous function. Here constructor is written in the destination class.
Also, we can see a new function GetMetres() . The function returns the data member metres of the
invoking object. The function is required because the constructor is defined in the DistFeet class and since
182 Chapter 11 - Function and Operator Overloading

metres is a private member of the DistMetres class, it cannot be accessed directly in the constructor
function in the DistFeet class.
Since you can use any of the above methods, it is strictly a matter of choice which method you choose
to implement. The Table 11.1 summarizes the type conversions.

Conversion Required Operation Function Operation Function in


in Destination Class Source Class

Basic to class Constructor Not Allowed

Class to Basic Not Allowed Conversion Function

Class to Class Constructor Conversion Function


Table 12.1 Summary of Type conversions

To summarize..
This chapter has explored the two mechanisms for achieving the polymorphism in C++, namely the
function overloading and operator overloading. The mechanism and the working of both the concepts
have been discussed in detail. The application of function overloading in overloading of constructors and
the application of operator overloading in the string manipulation has been illustrated. The need of different
conversion functions and the way of performing it has been presented.

Fill in the blanks


1. A single function name can have multiple .

2. The and of the arguments are used to select the best match from the candidiate
function.

3. The compiler matches the constructors depending on how the class is .

4. The ability to create new data types, on which direct operations can be performed, is called as
and the ability to associate an existing operator with a member function and use it with the objects of its class,
as its operands, is called as .

5. A friend function will have argument for unary operator and for binary operators.

Programming exercises
1. Create a class FLOAT that contains one float data member. Overload all the four arithmetic operators so that
they operate on the objects of float.
BSIT 33 OOPS with C++ 183

2. Design a class POLAR which describes a point in the plane using polar coordinates radius and angle.

3. Create a class MAT of size mxn. Define all possible operations for MAT type of objects.

Answers(Fill in the blanks)


184 Chapter 12 - Inheritance

Chapter 12

Inheritance

The objectives of this chapter is to learn

Achieving reusability through inheritance, its concepts, working and usefulness.

Various forms of inheritance used for writing extensible programs.


Different access specifiers used in the class and their significance.

Need and working of virtual base and abstract classes.

12.1. INTRODUCTION
When programming in C, it is common to view problem solutions from a top-down approach: functions
and actions of the program are defined in terms of sub-functions, which again are defined in sub-sub-
functions, etc.. This yields a hierarchy of code: main() at the top, followed by a level of functions which
are called from main(), etc..
In C++ the dependencies between code and data can also be defined in terms of classes, which are
related to other classes. This looks like composition, where objects of a class contain objects of another
class as their data. Also, a class can be defined by means of an older, pre-existing class, which leads to a
situation in which a new class has all the functionality of the older class, and additionally introduces its
own specific functionality. Instead of composition, where a given class contains another class, we mean
here derivation, where a given class is another class.

Another term for derivation is inheritance: the new class inherits the functionality of an existing class,
while the existing class does not appear as a data member in the definition of the new class. When

184 Chapter 12 - Inheritance


BSIT 33 OOPS with C++ 185

speaking of inheritance the existing class is called the base class, while the new class is called the derived
class.
A derived class with only one base class is called single inheritance and one with several base
classes is called multiple inheritance. A class can also be inherited by more than one class, which is
known as hierarchical inheritance. The mechanism of deriving a class from another derived class is
called multilevel inheritance. The combination of different types of inheritance leads to hybrid
inheritance. The Fig. 12.1 shows various forms of inheritance that can be used for writing extensible
programs

A
A A B

B
C
B
C c) Multiple Inheritance
a) Single Inheritance A
b) Multilevel Inheritance

A B C

B C D D

d) Hierarchical Inheritance e) Hybrid Inheritance

Fig. 12.1 Forms of Inheritance

12.2. DEFINING DERIVED CLASSES


A derived class is defined by specifying its relationship with the base class in addition to its own details.
The general form of defining a derived class is:
class derived-class-name : visibility-mode base-class-name
{
..//
..// members of derived class
};
186 Chapter 12 - Inheritance

The colon indicates that the derived-class-name is derived from the base-class-name. The visibility
mode is optional and, if present, may be either private or public. The default visibility-mode is private.
Visiblity mode specifies whether the features of the base class are privately derived or publicly derived.
For e.g.,

class ABC : private XYZ //private derivation


{
// members of ABC
};

class ABC : public XYZ //public derivation


{
// members of ABC
};

class ABC : XYZ //private derivation by default


{
// members of ABC
};

12.3. PRIVATE DERIVATION


By default, private derivation is assumed. If a new class is derived privately from its parent class ,
then:

l The private members inherited from its base class are inaccessible to new member functions in
the derived class . this means that the creator of the base class has absolute control over the
accessibility of these members, and there is no way that you can override this.
l The public members inherited from the base class have private access privilege. In other
words, they are treated as though they were declared as new private members of the derived
class, so that new member functions can access them. However, if another private derivation
occurs from this derived class, then these members are inaccessible to new member functions.
For e.g.

class base
{
private :
int number;
};
BSIT 33 OOPS with C++ 187

class derived : private base


{
public :
void f()
{
++number; // Private base member not
accessible
}
};
The compiler error message is base :: number is not accessible in the function derived :: f();For e.g.,

class base
{
public :
int number;
};

class derived : private base


{
public :
void f()
{
++number; // Access to number O.K.
}
};

class derived2 : private derived


{
public :
void g()
{
++number; // Access to number is
prohibited.
}
};
188 Chapter 12 - Inheritance

The compiler error message is base :: number is not accessible in the function derived2 :: g();

Since public members of a base class are inherited as private in the derived class, the function derived
:: f() has no problem accessing it . however, when another class is derived from the class derived , this
new class inherits number but cannot access it. Of course, if derived1::g() were to call upon derived::f(),
there is no problem since derived::f() is public and inherited into derived2 as private. i.e. In derived2 we
can write,

void g()
{
f();
}

or there is another way. Writing access declaration does this.

class base
{
public :
int number;
};

class derived : private base


{
public : base :: number ;
void f()
{
++number; // Access to number O.K.
}
};

class derived2 : private derived


{
public :
void g()
{
++number; // Access to number O.K
}
};
BSIT 33 OOPS with C++ 189

Private derivations are very restrictive in terms of accessibility of the base class members and hence
this type of derivation is rarely used.

12.4. PUBLIC DERIVATION


Public derivations are much more common than private derivations. Here,
l The private members inherited from the base class are inaccessible to new members functions
in the derived class.

l The public members inherited from the base class may be accessed by new members functions
in the derived class and by instances of the derived class. For e.g.,

class base
{
private :
int number;
};

class derived : public base


{
public :
void f()
{
++number; // Private base member not
accessible
}
};

The compiller error message is base :: number is not accessible in the function derived::f();Here,
only if the number is public then you can access it.
Note : However example 2 and 3 in the above section works here if you derive them as public.

12.5. PROTECTED ACCESS RIGHTS


In the preceding example, declaring the data member number as private is much too restrictive
190 Chapter 12 - Inheritance

because clearly new members function in the derived class need to gain access to it and in order to
perform some useful work.
To solve this dilemma, the C++ syntax provides another class access specification called protected .
The working of protected is :

l In a private derivation the protected members inherited from the base class have private
access privileges. Therefore, new member functions and friend of the derived class may access
them.
l In a public derivation the protected members inherited from the base class retain their protected
status. They may be accessed by new members function and friends of the derived class .

In both situations the new members functions and friends of the derived class have unrestricted access
to protected members . however, as the instances of the derived class are concerned, protected and
private are one and the same, so that direct access id always denied. Thus, you can see that the new
category of protected provides a middle ground between public and private by granting access to new
function and friends of the derived class while still blocking out access to non-derived class members and
friend functions.

class base
{
protected :
int number;
};

class derived : public base


{
public :
void f()
{
++number; // base member access O.K.
}
};

12.6. PROTECTED DERIVATION


In the protected derivation,
BSIT 33 OOPS with C++ 191

l The private members inherited from the base class are inaccessible to new member functions in
the derived class. ( this is exactly same as if a private or public derivation has occurred.)
l The protected members inherited from the base class have protected access privilege.

l The public members inherited from the base class have protected have protected access.

Thus, the only difference between a public and a protected derivation is how the public members of
the parent class are inherited. It is unlikely that you will ever have occasion to do this type of derivation.

12.7. SUMMARY OF ACCESS PRIVILEGES


The different access privileges can be summarized as follows:

1. If the designer of the base class wants no one, not even a derived class to access a member ,
then that member should be made private .

2. If the designer wants any derived class function to have access to it, then that member must be
protected.

3. if the designer wants to let everyone , including the instances, have access to that member , then
that member should be made public .

The different derivations can be summarized as follows:

1. Regardless of the type of derivation, private members are inherited by the derived class , but
cannot be accessed by the new member function of the derived class , and certainly not by the
instances of the derived class .

2. In a private derivation, the derived class inherits public and protected members as private . A
new members function can access these members, but instances of the derived class may not.
Also any members of subsequently derived classes may not gain access to these members
because of the first rule.

3. In public derivation, the derived class inherits public members as public, and protected as protected.
A new member function of the derived class may access the public and protected members of
the base class ,but instances of the derived class may access only the public members.
In a protected derivation, the derived class inherits public and protected members as protected . A
new members function of the derived class may access the public and protected members of the base
class, both instances of the derived class may access only the public members . The different derivation
types and access specifiers can be summarized as in the Table 12.1.
192 Chapter 12 - Inheritance

Derivation Type Base Class Member Access in Derived Class


Private (inaccessible )
Private Public Private
Protected Private
Public Private (inaccessible )
Public Public
Protected Protected
Private (inaccessible )
Protected Public Protected
Protected Protected
Table 12.1 Summary of derivation types and Access specifiers

12.8. SINGLE INHERITANCE


A derived class with only one base class is called single inheritance. Consider the example of the
single inheritance shown in the Fig. 12.1 a. Let the class A contain a private data member x, public data
member y and the public member functions get_data() and disp(). Let the derived class B contain the
private member p and the public member functions get_dt() and display(). According to the concept of
single inheritance, class B(which derives class A) will also contain the public data member y and public
member function disp() and are thus accessible with the instances of class B. This is illustrated in the
following program.
#include<istream.h>

class A

int x;

public:

int y;

void get_data(int,int);

void disp();

};
BSIT 33 OOPS with C++ 193

class B : public A

int p;

public:

void get_dt(int);

void display();

};

void A :: get_data(int m, int n)

x=m;

y=n;

void A :: disp()

cout << x = << x << endl << y = < endl;

void B :: get_dt(int m)

get_data(5,6);

p=m;

void B :: display()
194 Chapter 12 - Inheritance

disp();

cout << p = << p << endl;

main()

B obj1;
obj1.get_dt(7);
obj1.display();
}
The ouput of the program is:
x =5
y=6
z=7

When the class B derives class A, all the public data members as well as public member functions are
visible and accessible to B. Even though the private data member of class A, i.e. x is not directly accessible
to B, it can be accessed through one of the public member functions of A.

To Summarize.
In this chapter, the need of inheritance has been explained. The classification of inheritance supported
by C++ has been explored. The usefulness of inheritance has been explained.

Who am I
1. I allow one class to be inhetited from many classes. I allow one level of classes share certain features of the
level above it.

2. I allow two or more classes inherit another class. I can create some sort of ambiguities to the compiler.

3. I allow the combination of different types of inheritance.


BSIT 33 OOPS with C++ 195

4. I can act as an indirect base class more than once without duplication of my data members.

5. I am helpful in creating specific classes. But sorry!you cannot create an instance of mine.

An educational institution wishes tio maintain a database of its employees. Specify all the
classes and define functions to create the database and retrieve individual information as and
when required.

Answers( Who am I)
196 Chapter 12 - Inheritance

References
1. Lipppman Stanley. B, C++ Primer, Addison Wesley, 1989

2. Stroustroup Bjarne, C++ Programming language, 2nd Edition, Addison Wesley, 1991

3. E. Balagurusamy, Object Oriented Programming with C++, TMH, 2000

4. www.cpp.com

5. www.microsoft.com/msdn

m m m

You might also like