Print

Virtual Functions

Lohit Seth
Written by Lohit Seth. Posted in C & C++ on 08 February 2010.
Hot 2136 hits 0 favoured

 

In this article I am going to write about virtual function – another type of polymorphism. Polymorphism is another essential feature of an Object – Oriented Programming language. It is the polymorphism that enables us to deal with different types of object as if they are all of the same type. We have already seen two types of polymorphism – function overloading and operator overloading. The third type of polymorphism simplifies the syntax of performing the same operation with different classes through virtual functions.

This type of polymorphism simplifies the syntax of performing the same operation with a hierarchy of classes. With the help of polymorphism we can easily keep the interface to the classes clearly. And in this case we do not need to have unique function names for similar operation on each derived classes. This type of polymorphism is supported by virtual keyword. For the new C++ programmer the concept of virtual function may be difficult to understand. But it would be harmful if you skip this topic because there is no analog to virtual function in procedural languages. Thus just read on, the concept of virtual function would soon become clearer.


Virtual Functions

Let us understand this concept by taking a simple program – v1.cpp that has normal member functions in base class as:

// Program – v1.cpp: Without virtual function

#include
class father
{
public:
void display()
{
cout<< “\nI am your father”;
}
};
class baby1 : public father
{
public:
void display()
{
cout << “\nI am your first baby”;
}
};

class baby2 : public father
{
public:
void display()
{
cout << “\nI am your second baby”;
}
};

void main()
{
baby1 b1;
baby2 b2;
father *fptr;
fptr=&b1;
fptr-> display();
fptr=&b2;
fptr -> display;
}


In this program we have derived two classes baby1 and baby2 from the base class father. Each of these three classes has a number functions display (). In main () we have created two objects b1 and b2 from two derived classes baby1 and baby2 respectively and a pointer fptr to base class father.

In first assignment statement we have

fptr =&b1;


Should this not give us an error, since we are assigning an address of one type to a pointer of another? No, because unlike normal pointers, the compiler does not flash an error message. In inheritance a pointer or a reference to a base class type can refer to an object of a derived class type. In this case the pointers to object of a derived class are type compatible with pointer to objects of the base class. Such type of casting is called a upcasting. Similarly a reference to base is compatible with any object of a derived class.

When we execute next statement

fptr-> display()

One may ask - Which function gets called – display() of derived class baby1 or display () of base class, father.

The same question arises in the last statement

fptr-> display()

In these cases the function in the base class gets called. Thus when we execute this program we get the following output….

I am your father
I am your father

It happens so because the compiler ignores the contents of the pointer and selects the member function that matches the type of the pointer. It is good sometimes but it is does not like that what we have discussed at the beginning of this article that is accessing functions of different classes using the same statement. Here we are going to discuss the concept of polymorphism, in which we manipulate more than one derived type with a base class pointer or reference. 

If we make a small change in this program by using the keyword virtual in front of the function display() in the base class, we can get the desired output.

Program v2.cpp illustrates this concept.

// Program – v2.cpp : With virtual function

#include 
class father
{
public:
virtual void display()
{
cout << “\nI am your father”;
}
};
class baby1 : public father
{
public:
void display()
{
cout << “\nI am your first baby”;
}
};

class baby2 : public father
{
public:
void display()
{
cout << “\nI am your second baby”;
}
};

void main()
{
baby1 b1;
baby2 b2;
father *fptr;
fptr=&b1;
fptr-> display();
fptr=&b2;
fptr ->display();
}


Now if we execute this program, the out put will be as:

I am your first baby
I am your second baby

Using a base class pointer, when we refer to any member function it retrieves the actual derived type of an object. From the output of this program, it is clear that the same function call

fptr->display ();

executes different functions, depending on the contents of fptr.

Thus a non virtual pointer sets up in any derived class always points to the member function in the base class only. Whereas when virtual functions are defined, the virtual pointer points to member function of the derived class. That’s why in this program the compiler has selected the function based on the contents of the pointer fptr, not on the type of the pointer.

When the compiler compiles this program, it does not know which object’s address fptr may contain. It could be the address of an object of the baby1 class or of the baby2 class. So it does not know which version of display() would get called. In this case the compiler leaves the decision until the program is running. At run  time after receiving  the address of an  appropriate object, it calls the appropriate function. This is called late binding or dynamic binding. On the other hand if the functions are chosen during compile time, the binding is called as early or static binding. 

We can also achieve the same by using references instead of pointer as:


// Program – v3.cpp : With virtual function

#include 
class father
{
public:
virtual void display()
{
cout << “ I am your father”;
}
};
class baby1 : public father
{
public:
void display()
{
cout << “I am your first baby”;
}
};

class baby2 : public father
{
public:
void display()
{
cout << “I am your second baby”;
}
};
void main()
{
baby1 b1;
baby2 b2;

father &f1ref=b1;
f1ref.display();
father &f2ref=b2;
f2ref.display();
}

When we execute this program, we get the same output as previous one.

Pure Virtual Function

A pure virtual function is a virtual function with no body. In a situation, where the function of the base class are not used we use the notation=0 in the function declaration.
For example the statement

virtual void display()=0;

declare the function display() to be a pure virtual function. Here the value 0 is not assigned to anything. It is used to simply tell the compiler that function will be pure that is have no body.

Here any intelligent person may think that if we remove the body of the virtual function then  can we not remove the function of together? But unfortunately it would be wrong. Because without a function display() in base class, the statement like

fptr-> display()

would be invalid. Since fptr is a pointer to a base class. And we know that a base class does not have any knowledge of its derived class’s members.

Program v4.cpp illustrates this concept of pure virtual function.


// Program – v4.cpp : Pure virtual function

#include
class father
{
public:
virtual void display()=0;
};
class baby1 : public father
{
public:
void display()
{
cout << “I am your first baby”;
}
};

class baby2 : public father
{
public:
void display()
{
cout << “I am your second baby”;
}
};
void main()
{
baby1 b1;
baby2 b2;
father &f1ref=b1;
father &f2ref=b2;
f1ref.display();
f2ref.display();
}

A More Practical Example

We have already seen that an upcasting can convert a pointer that referes to an object of class type to a pointer to a class in the same class hierarchy. It is achieved by using virtual functions. A virtual function appears to exits but does not exist in reality. A virtual function is defined and declared in the base class as being virtual. It is then used by several derived functions by setting up pointers.

Let us assume that our program defines a class library to represent the different areas of shape. Let we have two different classes called Triangle and Rectangle. Each of these two classes has a member function area_display() to display the are of relevant shape on the screen. If we want to display the are of numerous triangles and rectangles, we can create an array that holds pointers to all the different objects in the class hierarchy.

The array might be defined as:

Shape * sptr[5];


And if we want to display the areas of different shapes, we will use the following loop:

for(i=0; i<20; i++)
sptr[i]->area_display();


Earlier we have seen that how the same function call is responsible for the execution of completely different function. It means that when sptr[i] contains address of the Triangle object, it would call

Triangle :: area_display()


function. When it contains the address of the Rectangle object it would call the

Rectangle :: area_display() 


function. It happens so because the array sptr[] has been defined to contain shape pointer and not triangle or rectangle pointers. This concept is called the polymorphism. Here the functions have the same name, area_display(), but different functions from different classes are executed. And it depends upon the contents of sptr [i]

To achieve this, following conditions must be met:

  1. All the different classes Triangle and Rectangle must be derived from a single base class, Shape.
  2. The area_display() function must be declared to be virtual in the base class, Shape. Once a function is declared virtual in a base class, its definition remains virtual in all derived classes; even when the keyword virtual is not repeated in the definition of the derived class.


For this we define a base class Shape and from this base class we derive two classes Triangle and Rectangle as:

class Shape
{
// ….
};
class Triangle : public Shape
{
// ….
};
class Rriangle : public Shape
{
// ….
};


Now let us see how to implement this in practical use.

Let we create a base class Shape which stores two double type data members that could be used to compute the area of figure. From this base class, we derive two classes Triangle and Rectangle. In base class we define a member function getdata() to initialize base class data members and another member function area_display() to compute and display the area of figure. Make the function area_display() as a virtual function and redefine these functions in the derived classes to match their requirements.

In case of Triangle class, these two values of base class will treated as base and height whereas in Rectangle these two values of base class will treated as length and breadth of rectangle. The area is computed as:

Area of triangle = (x0 * y0) / 2;
Area of rectangle = x0 * y0;


Following program does this.

// Program – v5.cpp

#include
class Shape
{
protected :
double x0, y0;
public :
void getdata(double x, double y)
{
x0 = x;
y0 = y;
}
virtual void area_display() = 0;
};
class Triangle : public Shape
{
public :
void getdata(double x, double y)
{
Shape :: getdata(x,y);
}
void area_display()
{
double area = (x0 * y0)/2.0;
cout << "\n Area of triangle = " << area;
}
};
class Rectangle : public Shape
{
public :
void getdata(double x, double y)
{
Shape :: getdata(x,y);
}
void area_display()
{
double area = (x0 * y0);
cout << "\n Area of rectangle = " << area;
}
};
void main()
{
Triangle *tptr;
Rectangle *rptr;
Shape *sptr[2];
int i;
tptr = new Triangle;
rptr = new Rectangle;
tptr->getdata(20.0,30.0);
rptr->getdata(20.0,20.0);
sptr[0] = tptr;
sptr[1] = rptr;
for(i=0; i<2; i++)
sptr[i]->area_display();
}


The output of this program is

Area of triangle =300
Area of rectangle =400

Abstract Classes

In program v5.cpp we have not created any object of class Shape because there is no need to make objects of Shape class. Here Shape class is used just to derive Triangle and Rectangle classes. Such class is known as an abstract class.

Thus an abstract class is a class which is not used to create any objects, rather than it is used to derive the classes. An abstract classes one that contain at least one pure virtual function and it defines properties common to other classes derived from it. We can also create a pointer or a reference to an abstract class. This allows an abstract class to be used as a base class, pointers to which can be used to select the proper virtual function.

Here one should remember that it is necessary to override a pure virtual function in all the derived classes from this abstract class. If you do not override then the derived class is also treated as an abstract class. However if you try to create an object of the abstract class then the compiler will certainly flag an error message.

For example, if you try to execute the following program

// Program - v6.cpp

#include 
class Father
{
public :
virtual void display() = 0;
};
class Baby1 : public Father
{

};
class Baby2 : public Father
{

};
void main()
{
Father *fptr;
Baby1 b1;
Baby2 b2;
fptr = &b1;
fptr->display();
fptr = &b2;
fptr->display();
}


then the compiler reports the following error messages:

Cannot create instance of abstract class 'Baby1'
Cannot create instance of abstract class 'Baby2'

Here one should remember that an object of a derived class can be assigned to a base class object. When you do this, the compiler copies only the base portion of the derived object. It means that it has no knowledge of data members or member functions which are defined in derived classes only.

For example let we have a base class First, which has two data members a and b and a derived class Second also has two data members c and d. It means that the derived class Second now has total four data members whereas the base class First has only two. Now if you assign the object of a derived class Second to an object of base class First then it simply copies a and b of Second class object and truncates the derived portion of the derived object, which is c and d. It happens so because there is no way to find in advance that a derived class can have how many number of data members in advance.

Following program illustrates this concept.

// Program - v7.cpp

#include 
class First
{
protected :
int a, b;
public :
First(int aa=0, int bb=0)
{
a=aa;
b=bb;
}
virtual void display()
{
cout << "\n" << a << "\t" << b;
}
};
class Second : public First
{
private :
int c, d;
public :
Second(int aa=0, int bb=0, int cc=0, int dd=0) : First(aa,bb)
{
c=cc;
d=dd;
}
virtual void display()
{
cout<<"\n"<<<"\t"<<<"\t"<<<"\t"<
}
};
void main()
{
First f(10,20);
Second s(100,200,300,400);
f.display();
s.display();
f=s;
f.display();
}


When you execute this program you get the following output....

10    20
100    200    300    400   
100    200

From this output it is clear that the base class object removes only the derived portion of the derived class object rather that simply changing the meaning of an address as when using a pointer or a reference.

Like this we know that a pointer of base class can contain the address of a derived class object. Similarly a pointer to a base class object has no knowledge of member functions that exist only in derived classes. It has knowledge of member functions, which are declared as virtual in base class. Following program illustrates this concept. 

// Program - v8.cpp

#include 
class Father
{
public :
virtual void display() = 0;
};
class Baby1 : public Father
{
public :
void display()
{
cout << "\nI am derived from Father.";
}
void child1()
{
cout << "\nI am pure baby 1.";
}

};
class Baby2 : public Father
{
public :
void display()
{
cout << "\nI am derived from Father.";
}
void child2()
{
cout << "\nI am pure baby 2.";
}

};
void main()
{
Father *fptr;
Baby1 b1;
Baby2 b2;
fptr = &b1;
fptr->display();
fptr->child1();
fptr = &b2;
fptr->display();
fptr->child2();
}


When you execute this program you get the following output....

'child1' is not a member of 'Father1;
'child2' is not a member of 'Father1;

If you want to call a function at any level in a class hierarchy through a base class pointer then that function must be declared as virtual in the base class, such as shown in following program

// Program - v9.cpp

#include 
class Father
{
public :
virtual void display() = 0;
virtual void child() = 0;
};
class Baby : public Father
{
public :
void display()
{
cout << "\nI am derived from Father.";
}
void child1()
{
cout << "\nI am pure baby.";
}

};
void main()
{
Father *fptr;
Baby b;
fptr = &b;
fptr->display();
fptr->child();

}

Now when we execute this program, we will get the following output….

I am derived from Father.
I am pure baby.

Virtual Destructors

We already know that when an object of a derived class is created then the compiler calls the base class constructor first and then derived class constructor. And the process of calling destructors is just reverse. The destructor of a derived class is called before the base class destructor. For a multi level inheritance, the constructor of the base class is called first and then the more derived constructor and so on. The destructor starts at the most derived class and works its way down to the base class. This process of calling constructor  and destructors is handled by the compiler automatically.

It is good as well as safe. Since the derived class can access the class members of the base class while the reverse is not true. Therefore it is good and safe to set up the class data members of a base class constructor before the derived class constructor. Similarly in order to destroy an object safely the derived class destructor is called before the base class destructor.

But thing are different when we create a derived class object using a new operator and then assigns the address of this object to a pointer to a base class object, as illustrated in program v10.cpp

// Program - v10.cpp

#include 
#include
class Father
{
public :
Father()
{
cout << "\nFather Constructor.";
}
~Father()
{
cout << "\nFather Destructor.";
}
};
class Baby : public Father
{
public :
Baby()
{
cout << "\nBaby Constructor.";
}
~Baby()
{
cout << "\nBaby Destructor.";
}

};
void main()
{
Father *fptr;
Baby *bptr = new Baby;
fptr = bptr;
delete fptr;
}


When you execute this program, you get the following output….

Father Constructor
Baby Constructor
Father Constructor

This program does not call the derived class destructor, because the statement

delete fptr;

calls the base class destructor only even though it points to the object of Baby class. The delete operator does not know at run time the type of the object pointed by fptr. Therefore if you the execution of the derived class destructor prior to the base class destructor then declare it a virtual destructor as:

virtual ~Father()
{
cout << "\nFather Destructor.";
}


Program – v11.cpp illustrates the use of virtual destructor.

// Program - v11.cpp

#include 
#include
class Father
{
public :
Father()
{
cout << "\nFather Constructor.";
}
virtual ~Father()
{
cout << "\nFather Destructor.";
}
};
class Baby : public Father
{
public :
Baby()
{
cout << "\nBaby Constructor.";
}
~Baby()
{
cout << "\nBaby Destructor.";
}

};
void main()
{
Father *fptr;
Baby *bptr = new Baby;
fptr = bptr;
delete fptr;
}


When you execute this program, you get the following output….

Father Constructor
Baby Constructor
Baby Destructor
Father Constructor

Thus if we delete an object identified by a pointer or reference then the destructor for a base class object should be virtual functions. After using virtual destructors, it is guaranteed that the program will first call the object destructor instead of the destructor associated with the pointer or a reference.

After declaring a base class destructor, the compiler calls the correct type of destructor functions irrespective of the type of the pointer. Now when we compile this program, the delete operator executes the Baby destructor first and then Father destructor.

However, in this program the base class needs no special destruction, but still you must provide a virtual destructor (with an empty statement block) to permit appropriate destruction calls for dynamically allocated object. Here one main point to note is that although destructor functions can be virtual, constructor functions can not be virtual.

Virtual Functions Without Derived Overrides

Can you guess the output of a program in which the derived class has no function to override the base class’s virtual function? For example, consider the following program….  

// Program - v12.cpp

#include 
class Father
{
public :
virtual void display()
{
cout << "\nBase class virtual function.";
}
};
class Baby1 : public Father
{

};
class Baby2 : public Father
{

};
void main()
{
Father *fptr;
Baby1 b1;
Baby2 b2;
fptr = &b1;
fptr->display();
fptr = &b2;
fptr->display();
}


Here when this program is executed, the compiler executes the base class’s function display() regardless of the pointer or reference type. Thus the output of this program is as:

Base class virtual function.
Base class virtual function.

Friends

The primary objective of C++ classes is that it supports the great OOPs goal of data hiding. A class encapsulates data and member functions into a single entity and allows us to change the private data only by working through member functions. So nonmember functions are not allowed to access an object’s private or protected data. However, sometimes we have to make an exception to this rule to avoid programming inconvenience. Sometime some specific outside functions need to read and write private data members directly. For such type of situation C++ provides a keyword called friend. A friend function is a nonmember function that is granted access to a class’s private members. Similarly a friend class is a class whose member functions can access another class’s private members.

Friend Functions

One of the most common reasons for using a friend function is when we want a function to operate on objects of two different classes. Logically, such a function should be a member of both classes. But it is impossible. The only way to achieve this goal is by using a friend function that acts as a bridge between two different classes. A friend function belongs to neither, and able to access both.

Following program illustrates this concept.


// Program – v13.cpp : Friend function

#include 
class Second;
class First
{
private :
int i;
public :
First ()
{
i=10;
}

friend int funny(const First &ff, const Second &ss);
};
class Second
{
private :
int j;
public :
Second ()
{
j=15;
}
friend int funny(const First &ff, const Second &ss);
};

int funny(const First &ff, const Second &ss)
{
int temp;
temp = ff.i+ss.j;
return temp;
}
void main()
{
First f;
Second s;
int data;
data = funny (f, s);
cout << data;
}

When we run this program, we get the following output….

25

In this program we have used to classes First and Second. The constructor in these classes initialize their private data member i and j to 10 and 15 respectively. In both class we have declared a function funny() to access their private data member as:

friend int funny(const first &ff, const second &ss);

This declaration can be placed any where in the class, either in private section or in the public section. An object of each class has been passed as an argument to the function funny(). And it accesses the private the data member of both classes through these arguments.

Note that the friend declaration is necessary in both classes. Another notable point is the declaration class second; at the beginning of the program.

This forward declaration is necessary because we can not refer a class until it has been declared. The class second is being referred to in the declaration of the function funny() in class First. So Second must be declared before First. This declaration tells the compiler that the class Second is defined later.

However, If we omit this forward declaration then the compiler can not understand the Second type when it encounters the First friend declaration and therefore the compiler will report an error message. If you place the entire class Second definition before class First then the compiler would not know what the first type is when it encounters the friend declaration in the Second class type. That’s why it is necessary to use the forward declaration.

Now see the definition of this friend function. Here the definition for funny() does not use the keyword friend. The keyword friend is used only for the prototype appearing in a class using the function. Another point to note is that this function definition does not use the scope resolution operator (::). It is so because this friend function belongs to no class. It means that the friend function has file scope, rather than class scope. Thus there will only one friend function with a specific name and signature per file.

Friend Classes

Like a friend function, we can also have an entire class as a friend. If we declare an entire class as a friend then all the member functions of this class become friend functions. Following program illustrates the use of friend classes.

// Program – v14.cpp

#include 
class Second;
class First
{
private :
int i;
public :
First()
{
i = 10;
}
friend Second;
};
class Second
{
private :
int j;
public :
Second()
{
j = 20;
}
void putdata(First ff)
{
cout << "\n" << ff.i << "\t" << j;
}
void display(First ff)
{
cout << "\n" << ff.i << "\t" << j;
}
};
void main()
{
First f;
Second s;
s.putdata(f);
s.display(f);
}


In this program, we have declared an entire class Second as friend in class First:

class Second;

It means that all the member functions of Second class can access the private data members of First. When we execute this program we get the following output….

10    20
10    20

Operator Overloading and Friends

Another useful situation of using a friend function is to increase the versatility of overloading operators. Now watch the following program carefully.

// Program - v15.cpp

#include 
class Distance
{
private :
int meter;
float cm;
public :
Distance ()
{
meter = 0;
cm = 0;
}
Distance (float mm)
{
meter = int (mm);
cm = (mm - meter) * 100;
}
Distance (int mm, float cc)
{
meter = mm;
cm = cc;
}
void display()
{
cout <<"\nMeter = "<<<"\nCentimeter = "<< cm;
}
Distance operator + (const Distance &);
};
Distance Distance :: operator + (const Distance &dd)
{
Distance temp;
temp.meter = meter + dd.meter;
temp.cm = cm + dd.cm;
if (temp.cm >= 100)
{
temp.meter++;
temp.cm -= 100;
}
return temp;
}

void main ()
{
Distance d1(20, 45.85), d2, d3, d4;
d2 = 70.65;                // one argument constructor
d1.display();
d2.display();
d3 = d1 + 70.55;            // Distance + float : O.K.
d3.display();
d4 = 50.25 + d2;            // float + Distance : ERROR
d4.display();
}


When you run this program, you get the following output….

Illegal structure operation

In this program there are three constructors: zero argument, one argument and two argument constructors. There also exists an overload + operator to add two objects of type Distance.

When we use a statement like

d3 = d1 + 70.55;

This translate to the following function call,

d3 = d1.operator + (70.55);


Here it requires to define the member function operator + () in the class Distance.

The overloaded operator + is looking for objects of type Distance both on its left and right. In this case the second argument is of type float, so the compiler will use the one argument constructor to convert this float to a Distance and then carry out the addition.

But what about the following statement:

d4 = 50.25 + d2;

internally this would become

d4 = 50.25 . operator + ();


and the overloaded + operator must have a Distance variable to the left of the dot operator because it is a member of Distance class, otherwise this operator +() function must be a member of the float class (which does not exist) and that takes a type Distance argument. Therefore when we place a variable of a different type, other than a Distance variable, the compiler reports an error. Here a friend can help us out in this situation.

To overcome this problem we can use a friend function with the float number as the first argument and the Distance as the second argument:

friend Distance operator + (float dist, const Distance &dd);


And in this friend function we reverse the order of its arguments and then calls the original function as:

Distance operator + (float dist, const Distance &dd)
{
return dd+dist;
}

Now when compiler encounters the statement

d4 = 50.25 + d2;

the friend function translates this statement into the following statement

d4 = d2 + 50.25;

Thus a friend function helps in reversing the order of its arguments for overloading an operator that takes two different types of arguments.

However C++ also provides another way to achieve the same using friend. Incidentally C++ provides the facility of defining all the operator functions as friend functions. Here we would define an operator function that requires two Distance arguments, such as

friend Distance operator + (const Distance &dd1, const Distance &dd2);

This friend function is defined as:

Distance Distance :: operator + (const Distance &dd1, const Distance &dd2)
{
Distance temp;
temp.meter = dd1.meter + dd2.meter;
temp.cm = dd1.cm + dd2.cm;
if (temp.cm >= 100)
{
temp.meter++;
temp.cm -= 100;
}
return temp;
}


From this definition it is clear that a friend function always requires one more argument than equivalent normal member function. Thus the statement

d3 = d1 + d2;


is interpreted as:

d3 = d2 . operator + (d1); 
// if the operator function is defined as a normal member function
and        d3 = operator + (d1, d2);
// if the operator function is defined as a friend function


Thus in friend function the extra argument represents the object that a normal member function uses implicitly.

Following program shows how this can be achieved.

// Program – v16.cpp

#include 
class Distance
{
private :
int meter;
float cm;
public :
Distance()
{
meter = 0;
cm = 0;
}
Distance(float mm)
{
meter = int (mm);
cm = (mm - meter) * 100;
}
Distance(int mm, float cc)
{
meter = mm;
cm = cc;
}
void display()
{
cout <<"\nMeter = "<<<"\nCentimeter = "<< cm;
}
friend Distance operator + (const Distance &dd1, const Distance&dd2);
};

Distance operator + (const Distance &dd1, const Distance &dd2)
{
Distance temp;
temp.meter = dd1.meter + dd2.meter;
temp.cm = dd1.cm + dd2.cm;
if (temp.cm >= 100)
{
temp.meter++;
temp.cm -= 100;
}
return temp;
}

void main ()
{
Distance d1 (20, 45.85), d2, d3, d4;
d2 = 70.65;                // one argument constructor
d1.display();
d2.display();
d3 = d1 + 70.55;            // Distance + float : O.K.
d3.display();
d4 = 50.25 + d2;            // float + Distance : O.K.
d4.display();
}


Here is the output of this program….

Meter = 20
Centimeter = 45.85
Meter = 70
Centimeter = 65
Meter = 91
Centimeter = 0.85
Meter = 120
Centimeter = 90

In earlier version of this program the overloaded + operator took only one argument, whereas in this program it takes two arguments. This is because in earlier program, one of the object on which the + operator is the object of which it was a member, and the second is an argument. And in this program the operator function is no longer a member function of the class, rather than it is a friend of the class Distance.

A more Practical Example


Now let us see another example of friend function. Let we create two classes DM and DF which stores the value of Distances. The DM class stores Distance in meters and cm and DF in feet and inches. Here we would write a friend function that adds one object of DM with another object of DF. The object that stores the result may be DM object or DF object. The output should be in the format of meter and cm or feet and inches depending on the objects on the display.
// Program – v17.cpp

#include 
const float MTFT = 3.280833;
class DM;
class DF
{
private :
int feet;
float inches;
public :
DF()
{
feet = 0;
inches = 0;
}
DF(int ff, float hh)
{
feet = ff;
inches = hh;
}
void getdist()
{

cout << "\nEnter Feet = ";
cin >> feet;
cout << "Enter Inches = ";
cin >> inches;
}
void display()
{
cout << "\nFeet = " << feet;
cout << "\nInches = " << inches;
}
friend DM operator + (DM,DF);
};
class DM
{
private :
int meter;
float cm;
public :
DM()
{
meter = 0;
cm = 0;
}
DM(int mm, float cc)
{
meter = mm;
cm = cc;
}
void getdist()
{

cout << "\nEnter Meter = ";
cin >> meter;
cout << "Enter Centimeter = ";
cin >> cm;
}
void display()
{
cout << "\nMeter = " << meter;
cout << "\nCentimeter = " << cm;
}
operator DF()
{
int ft;
float inch, temp;
temp = (meter+cm/100)*MTFT;
ft = int(temp);
inch = (temp-ft)*12;
return DF(ft,inch);
}
friend DM operator + (DM,DF);
};
DM operator + (DM d1, DF d2)
{
DM temp;
float t;
t = d2.feet+(d2.inches)/12;
t /=  MTFT;
temp.meter = d1.meter + int (t);
temp.cm = d1.cm + (t-int(t))*100;
return temp;
}

void main()
{
DM d1,d2;
DF d3,d4;
cout << “\nEnter first Distance in meter and centimeter.”;
d1.getdist();

cout << “\nEnter second Distance in feet and inches.”;
d3.getdist();

d2 = d1+d3;
d4 = d1+d3;

cout << “\nAddition of two Distance in meter and centimeters.”;
d2.display();

cout << “\nAddition of two Distance in feet and inches.”;
d4.display();

}


The output of this program is

Enter first Distance in meter and centimeter.

Enter Meter =10
Enter Centimeter =50

Enter second Distance in feet and inches.
Enter Feet = 20
Enter Inches = 11.75

Addition of two Distance in meter and centimeters.

Meter = 16
Centimeter = 89.446304

Addition of two Distance in feet and inches.

Feet =55
Inches = 5.134964

Overloaded Operators:


Overloading the


In earlier programs we have used getdist() and display() functions to read Distance objects and write Disaply objects on the console. Now one may ask – Can’t we read user-defined objects as cin >> d1; or display user-defined object, such as cout and << operators to perform input/output for user-defined data types.

No doubt, these two operators are heavily overloaded. The standard ostream class overloads the << operator to send its objects whatever arguments the overloaded operator function receives. Remind that cout is a standard ostream object that is smart enough to recognize all the basic C++ data types. The ostream class has following overloaded operator functions:

ostream & operator << (char c);
ostream & operator << (unsigned char c);
ostream & operator << (signed char c);
ostream & operator << (short n);
ostream & operator << (unsigned short n);
ostream & operator << (int n);
ostream & operator << (unsigned int n);
ostream & operator << (float n);
ostream & operator << (double n);
ostream & operator << (long double n);
ostream & operator << (long n);
ostream & operator << (unsigned long n);
ostream & operator << (bool b);
ostream & operator << (const char *s);
ostream & operator << (const unsigned char *s);
ostream & operator << (const signed char *s);



On similar track, the istream overloads >> operator for all the basic C++ data types. If you want to use operators on user-defined data types then it is necessary to overload operators. The << operator is overloaded as:

ostream & operator << (ostream &os, const class_name &cobj)
{
os << ….        // display object’s contents
return os;
}


Similarly, the >> operator is overloaded as:

istream & operator >> (istream &is, class_name &cobj)
{
is >> ….;        // read object’s contents
return is;
}


Here class_name is the name of the class whose object is to read or display. Both of these functions return a reference to an ostream object and istream object. However it is not mandatory, but generally the overloaded >> operator functions should be made friend functions when they are used to read and write user-defined objects.

For example, if we want to read and display Distance class object then we will overload these operators as:

istream & operator >> (istream &is, Distance &dd)
{
is >> dd.meter >> dd.cm;
return is;
}


and

ostream & operator << (ostream &os, const Distance &dd)
{
os << "\nMeter = " << dd.meter << "\nCentimeter = " << dd.cm;
return os;
}


Program v18.cpp shows the usage of these two overloaded operator functions.

// Program – v18.cpp

#include 
class Distance
{
private :
int meter;
float cm;
public :
Distance(int mm=0, float cc=0.0)
{
meter=mm;
cm=cc;
}
friend ostream & operator << (ostream &os, const Distance &dd);
friend istream & operator >> (istream &os, Distance &dd);
};
ostream & operator << (ostream &os, const Distance &dd)
{
os << "\nMeter = " << dd.meter << "\tCentimeter = " << dd.cm;
return os;
}
istream & operator >> (istream &is, Distance &dd)
{
is >> dd.meter >> dd.cm;
return is;
}
void main()
{
Distance d;
cout << "Enter meters and centimeter = ";
cin >> d;
cout << d;
}


Here is the output of this program….

Enter meters and centimeter = 40    30.75
Meter =40    Centimeter = 30.75

In this program we have defined two friend functions operator (). Since these functions access the private members of Distance class only, not the private members of ostream class. Therefore these functions are declared as friends in Distance class only.

Here when the compiler encounters the statements

cin >> d; and 
cout << d;


they are converted into the following statements:

operator >> (cin, dd);    and
operator << (cout, dd);


respectively. Another main point to note is that the objects in these operator functions are collected as reference, in order to prevent creation of another copies of these objects. These operator functions returns the istream and ostream object by reference to permit cascading, such as:

cin >> d1 >> d2;    and
cout << d1 << d2;


Friends or No Friends

Is it always necessary to declare operator () functions as friend functions? Its answer is no. If they access the private members of both classes directly then these functions should be friends to both classes. If these two operator functions access the private data members of either class then it is necessary to define these two functions as a  friend function in that particular class.

If we look at the last program then it is clear that these functions access the private data members of Distance objects directly but only uses the istream and ostream objects as a whole. Therefore it is necessary to define these two functions friend to Distance class only, not in istream and ostream class. However if these two operator functions do not access data member functions directly then there is no need to declare them friends in either class. For example, let we overload these operator functions as:

ostream & operator << (ostream &os, Distance &dd)
{
os << "\nMeter = " << dd.getmeter() << "\nCentimeter = " << dd.getcm();
return os;
}
istream & operator >> (istream &is, Distance &dd)
{
int mm;
float cc;
is >> mm >> cc;
dd.setdist(mm,cc);
return is;
}


where getmeter(), getcm() and setdist() are member functions which are defined in Distance class as:

void setdist(int mm, float cc)
{
meter = mm;
cm = cc;
}
int getmeter()
{
return meter;
}
float getcm()
{
return cm;
}


Following program illustrates this concept.

// Program – v19.cpp

#include 
class Distance
{
private :
int meter;
float cm;
public :
Distance(int mm=0, float cc=0.0)
{
meter=mm;
cm=cc;
}
void setdist(int mm, float cc)
{
meter = mm;
cm = cc;
}
int getmeter()
{
return meter;
}
float getcm()
{
return cm;
}

};
ostream & operator << (ostream &os, Distance &dd)
{
os << "\nMeter = " << dd.getmeter() << "\nCentimeter = " << dd.getcm();
return os;
}
istream & operator >> (istream &is, Distance &dd)
{
int mm;
float cc;
is >> mm >> cc;
dd.setdist(mm,cc);
return is;
}
void main()
{
Distance d;
cout << "Enter meters and centimeter = ";
cin >> d;
cout << d;
}


Here is the output of this program….

Enter meters and centimeter = 30     50.75

Meter = 30
Centimeter = 50.75



Lohit Seth

Author: Lohit Seth

195 14585 3
Saari umar hum Mar mar ke jee liye Ek pal to ab humein jeene do Jeene do Saari umar hum Mar mar ke jee liye Ek pal to ab humein jeene do Jeene do Saari umar hum Mar mar...
  • No comments found
Powered by CjBlog