Professional Documents
Culture Documents
Chapter 9
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.
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;
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
// classes example
#include <iostream.h>
class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void)
{
return (x*y);
}
};.
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.
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
class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void) {return (x*y);}
};
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
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);}
};
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
// class example
#include <iostream.h>
class CRectangle
{
int x, y;
public:
int set_values (int,int);
int area (void) {return (x*y);}
};
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.
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.
Member function 2
Object1 Object 2
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();
}
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
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);
}
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 [];
};
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.
class student
{
public :
void getdetails();
void printdetails();
private :
int rollno;
char name[25];
int marks[6];
float percent;
};
percent = total / 6;
}
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
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();
}
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.
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.,
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();
}
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.
};
class Bclass
{
public :
Bclass(int v)
{
Bvar = v;
}
};
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.
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.
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();
}
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
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).
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
As seen from the above format, the constructors should have the following characteristics.
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);
};
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:
This method is known as shorthand method, which is used very often and is easy to implement.
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
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
};
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
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.
size = s2.size;
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
#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);
}
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 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
#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
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.
4. The initial values are passed as arguments to the constructor function, in case of parameterized constructors.
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
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.
Consider the following example, which raises a given number to a given power and returns the result.
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;
}
void main(void)
{
160 Chapter 11 - Function and Operator Overloading
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.
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.
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.
class CRectangle
{
int width, height;
public:
CRectangle ();
CRectangle (int,int);
int area (void) {return (width*height);}
};
CRectangle::CRectangle ()
{
width = 5;
height = 5;
}
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;
}
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
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.
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.
#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
};
main()
{
space S;
S.getdata(10,-20,30);
cout << S: ;
S.display();
- S; //activates operator ()
cout << S: ;
s.display ();
}
// 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
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();
}
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.
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:
{
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.
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
<, <=, >, >=, ==,! =, &, ^, |, &&, ||, =, *=, /=, %=, +=, -, =, <<=,
>>=, &=,! =, ^=, ,(Comma).
. 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();
char s[100];
};
strcpy(temp.s,s);
strcat(temp.s,s2.s);
return (temp);
}
void main()
{
String s1 = welcome ;
String s2 = to the world of c++;
172 Chapter 11 - Function and Operator Overloading
String s3;
s3 = s1 + s2;
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++
float f = 3.5;
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;
};
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.
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
void display(void)
{
cout << Feet = << feet <<,;
cout << Inches = << inches << endl;
};
private :
int feet;
float inches;
};
Output:
BSIT 33 OOPS with C++ 177
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.
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.
class DistFeet
{
public :
DistFeet(void) // Constructor with no
{ // argument
feet = 0;
inches = 0.0;
};
178 Chapter 11 - Function and Operator Overloading
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;
}
private:
float metres;
};
df1 = dm1;
calls the conversion function implicitly. It could also have been called explicitly as
df1 = DistFeet(dm1);
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;
};
void ShowFeet(void)
{
cout << Feet = << feet << endl;
cout << Inches = << inches << endl;
};
};
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.
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.
2. The and of the arguments are used to select the best match from the candidiate
function.
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.
Chapter 12
Inheritance
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
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
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.,
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 base
{
public :
int number;
};
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();
}
class base
{
public :
int number;
};
Private derivations are very restrictive in terms of accessibility of the base class members and hence
this type of derivation is rarely used.
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;
};
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.
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;
};
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.
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 .
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
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();
};
x=m;
y=n;
void A :: disp()
void B :: get_dt(int m)
get_data(5,6);
p=m;
void B :: display()
194 Chapter 12 - Inheritance
disp();
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.
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
4. www.cpp.com
5. www.microsoft.com/msdn
m m m