8. Operator Overloading and Type Conversion – Object-Oriented Programming with ANSI and Turbo C++

8

CHAPTER

Operator Overloading
and Type Conversion

C
H
A
P
T
E
R

O
U
T
L
I
N
E
—•  8.1 Introduction
—•  8.2 The Keyword Operator
—•  8.3 Overloading Unary Operators
—•  8.4 Operator Return Type
—•  8.5 Constraint on Increment and Decrement Operators
—•  8.6 Overloading Binary Operators
—•  8.7 Overloading with friend Function
—•  8.8 Type Conversion
—•  8.9 Rules for Overloading Operators
—• 8.10 One Argument Constructor and Operator Function
—• 8.11 Overloading Stream Operators

8.1 INTRODUCTION

Operator overloading is an important and useful feature of C++. The concept of operator overloading is quite similar to that of function overloading.

An operator is a symbol that indicates an operation. It is used to perform operation with constants and variables. Without an operator, programmer cannot built an expression.

C++ frequently uses user-defined data types, which is a combination of one or more basic data types. C++ has an ability to treat user-defined data type like the one they were built-in type. User-defined data types created from class or struct are nothing but combination of one or more variables of basic data types. The compiler knows how to perform various operations using operators for built-in types; however, for the objects those are instance of the class, the operation routine must be defined by the programmer.

For example, in traditional programming languages the operators such as +, - , <=, >= etc., can be used only with basic data types such as int or float etc. The operator + (plus) can be used to perform addition of two variables, but the same is not applicable for objects. The compiler cannot perform addition of two objects. The compiler would throw an error if addition of two objects is carried out. The compiler must be made aware about addition process of two objects. When an expression including operation with objects is encountered, a compiler searches for the definition of the operator, in which a code is written to perform operation with two objects. Thus, to perform operation with objects we need to redefine the definition of various operators. For example, for addition of objects A and B, we need to define operator + (plus). Re-defining the operator plus does not change its natural meaning. It can be used for both variables of built-in data types as well as objects of user-defined data types.

Operator overloading is one of the most valuable concepts introduced by C++ language. It is a type of polymorphism. Polymorphism permits to write multiple definitions for functions and operators. C++ has number of standard data types like int, float, char etc. The operators +, -, * and = are used to carry operations with these data types. Operator overloading helps programmer to use these operators with the objects of classes. The outcome of operator overloading is that objects can be used in a natural manner as the variables of basic data types. Operator overloading provides the capability to redefine the language in which working operator can be changed.

Consider an example,

 a=c+d;
 c=a-d;

where a, c and d are variables of basic data types like int or float. The use of operators +, - and = is valid. However, if we try these operators with the object, the compiler displays error message “Illegal structure operation”.

class number
{
  public:
  int x;
  int y;
};

For example, A, B and C are three objects of class number. Each object holds individual copy of member variable x and y. We want to perform addition of A and B and store the result in C. For the sake of understanding the member variables are declared in public section. The addition of A and B means addition of member variables of A and member variables of B .The result of this operation will be stored in member variables of C. This feature can be implemented as shown below:

8.1 Write a program to perform addition of two objects and store the result in third object. Display contents of all the three objects.

# include <iostream.h>
# include <constream.h>

class number
{
   public:
   int x;
   int y;

   number( ) { } // ZERO ARGUMENT CONSTRUCTOR

number (int j,  int k ) // TWO ARGUMENT CONSTRUCTOR
   {
     x=j;
     y=k;
   }

void show( )
{
   cout <<"\n x="<<x <<" y="<<y;
}

};
void main( )
{
   clrscr ( );
   number A(2,3), B(4,5), C ;
   A.show ( );
   B.show ( );  

   C.x=A.x+B.x;   // ADDITION OF TWO OBJECTS
   C.y=A.y+B.y;  // USING MEMBER VARIABLES DIRECTLY

   C.show( );
}

OUTPUT
x=2 y=3
x=4 y=5
x=6 y=8

Explanation: In the above program, A, B and C are objects of class number. Using constructor, objects are initialized. Consider the following statements:

C.x=A.x+B.x;
C.y=A.y+B.y;

In the above statements, addition of members of objects A and B is performed and stored in C. Each member variable is accessed individually and stored in member variable of C. For example, member x of A and member x of B are added and stored in x of C. Similarly, addition of other members is carried out.

In this program we cannot perform the operation C=A+B. The operation with objects is complicated because it involves operation of one or more data member variables which are part of objects.

OPERATOR OVERLOADING

The capability to relate the existing operator with a member function and use the resulting operator with objects of its class as its operands is called operator overloading.

8.2 THE KEYWORD OPERATOR

The keyword operator defines a new action or operation to the operator.

Syntax:

Return type operator operator symbol (parameters )
{
   statementl;
   statement2;
}

The keyword ‘operator’, followed by an operator symbol, defines a new (overloaded) action of the given operator.

Example:

number operator + (number D)
  {
    number T;
    T.x=x+D.x;
    T.y=y+D.y;
    return T;
  }

Overloaded operators are redefined within a C++ class using the keyword operator followed by an operator symbol. When an operator is overloaded, the produced symbol is called the operator function name. The above declarations provide an extra meaning to the operator. Operator functions should be either member functions or friend functions. A friend function requires one argument for unary operators and two for binary operators. The member function requires one argument for binary operators and no argument for unary operators. When the member function is called, the calling object is passed implicitly to the function and hence available for member function. While using friend functions, it is essential to pass the objects by value or reference. The prototype of operator functions in classes can be written as follows:

(a)  void operator ++( );

(b)  void operator --( );

(c)  void operator - ( );

(d)  num operator+(num);

(e)  friend num operator * (int ,num);

(f)  void operator = (num);

Operator overloading can be carried out in the following steps:

(a)  Define a class which is to be used with overloading operations.

(b)  The public declaration section of the class should contain the prototype of the function operator( ).

(c)  Define the definition of the operator ( ) function with proper operations for which it is declared.

8.2 Write a program to perform addition of two objects using operator keyword.

# include <iostream.h>
# include <constream.h>

class number {    public:    int x;    int y;
   number( ) { } // ZERO ARGUMENT CONSTRUCTOR
number(int j, int k ) // TWO ARGUMENT CONSTRUCTOR    {      x=j;      y=k;    }
number operator + ( number D)    {    number T;    T.x=x+D.x;    T.y=y+D.y;    return T;    }
void show( ) {    cout <<"\n x="<<x <<" y="<<y; }
}; void main( ) {    clrscr ( )    number A(2,3),B(4,5),C;    A.show ( );    B.show ( );    C=A+B;    C.show( ); }

OUTPUT
x=2 y=3
x=4 y=5
x=6 y=8

Explanation: In the above program, A, B and C are objects of class number. Here, the addition has been performed using statement C=A+B. Remember, in the last program we were not able to execute this statement. Instead of this, two separate statements were used to perform addition.

In this program, the statements that perform addition operation of each individual member of objects are written in function operator. The operator has return type and single argument. It also uses a local object (T) to hold addition as long as the operator function is active. Whenever the statement C=A+B is executed, the compiler searches for definition of operator +. The object A invokes the operator function and object B is passed as argument. The copy of object B is stored in the formal argument D. The member variables of A are directly available in operator function as the function is invoked by the same object. The addition of individual members are carried out and stored in member variable of object T. The return type of operator function is same as that of its class. The function returns object T and it is assigned to variable C.

8.3 OVERLOADING UNARY OPERATORS

Overloading devoid of explicit argument to an operator function is called as unary operator overloading. The operator ++, --, and – are unary operators. The unary operators ++ and -- can be used as prefix or suffix with the functions. These operators have only single operand. The examples given below illustrate the overloading of unary operators.

8.3 Write a program to increment member variables of object. Overload unary ++ operator.

# include <iostream.h>
# include <constream.h>

class num
{
   private :
   int a,b,c,d;

   public :

num ( int j, int k, int m ,int l)
{
a=j;
b=k;
c=m;
d=l;
}

void show(void);
void operator ++( );

};

void num :: show( )
{
   cout <<" A= "<<a <<" B= " <<b <<" C = "<<c <<" D = "<<d;
}

void num :: operator ++( )
{
   ++a; ++b; ++c; ++d; 
}

   main( )
   {
     clrscr( );
     num X(3,2,5,7);
     cout <<"\n Before Increment of X : ";
     X.show( );
     ++X;
     cout <<"\n After Increment of X : ";
     X.show( );
     return 0;
   }

OUTPUT
Before Increment of X : A= 3 B= 2 C = 5 D = 7
After Increment of X : A= 4 B= 3 C = 6 D = 8

Explanation: In the above example, the class num contains four integer variables a, b, c and d. The class also has two-member functions show( ) and operator ++( ) and one parameterized constructor. The constructor is used to initialize object. The show( ) displays the contents of the member variables. The operator ++( ) overloads the unary operator ++. When this operator is used with integer or float variables, its value is increased by one. In this function, ++ operator precedes each member variable of class. This operation increments the value of each variable by one.

In function main( ), the statement ++X calls the function operator ++( ), where, X is an object of the class num. The function can also be called using statement X.operator ++( ). In the output, values of member variables before and after increment operations are displayed.

8.4 Write a program to overload – operator.

# include <iostream.h>
# include <conio.h>
class num
{
   private :
   int a,b,c,d;
   public :
num ( int x, int y, int z, int w)
{
   a=x;
   b=y;
   c=z;
   d=w;
}
   void show(void);
   void operator -( );
};
void num :: show( )
{
   cout <<"A= "<<a <<" B= " <<b <<" C = "<<c <<" D = "<<d;
}
void num :: operator -( )
{
a=-a;
b=-b;

c=-c;
d=-d;
}
main( )
{
   clrscr( );
   num X(2,2,8,4);

   cout <<"\nBefore Negation of X : ";
   X.show( );
   -X;
   cout <<"\nAfter Negation of X : ";
   X.show( );
   return 0;
}

OUTPUT
Before Negation of X : A= 2 B= 2 C = 8 D = 4
After Negation of X : A= -2 B= -2 C = -8 D = -4

Explanation: The above program is same as the previous one. Here, the operator – is overloaded. The statement –X calls the function operator –( ). The function operator –( ) makes all the member variables negative. The function show( ) displays the values of member variables on the screen.

8.4 OPERATOR RETURN TYPE

In the last few examples we declared the operator ( ) of void types i.e., it will not return any value. However, it is possible to return value and assign to it other objects of the same type. The return value of operator is always of class type, because the operator overloading is only for objects. An operator cannot be overloaded for basic data types. Hence, if the operator returns any value, it will be always of class type. Consider the following program.

8.5 Write a program to return values from operator( ) function.

# include <iostream.h>
# include <conio.h>

class plusplus
{
   private :
   int num;

   public :
   plusplus( ) { num=0; }

   int getnum( ) { return num; }

   plusplus operator ++ (int)
   {
     plusplus tmp; 
     num=num+1;
     tmp.num=num;
     return tmp;
   }
};

   void main( )
   {
     clrscr( );
     plusplus p1, p2; 
     cout <<"\n p1 = "<<p1.getnum( );
     cout <<"\n p2 = "<<p2.getnum( ); 
     p1=p2++;
     cout <<endl<<" p1 = "<<p1.getnum( );
     cout <<endl<<" p2 = "<<p2.getnum( );
     p1++;
     // p1++=2;
     cout <<endl<<" p1 = "<<p1.getnum( );
     cout <<endl<<" p2 = "<<p2.getnum( );
}

OUTPUT
p1 = 0
p2 = 0
p1 = 1
p2 = 1
p1 = 2
p2 = 1

Explanation: In the above program, class plusplus is declared with one private integer num. The class constructor initializes the object with zero. The member function getnum( ) returns current value of variable num. The operator ++( ) is overloaded and it can handle as postfix increment of the objects. In case of prefix incrimination it will flag an error.

The p1 and p2 are objects of the class plusplus. The statement p1=p2++, first increments the value of p2 and then assigns it to the object p1. The values displayed will be one for the objects. The object p1 is increased. This time the values of object displayed will be two and one.

8.5 CONSTRAINT ON INCREMENT AND DECREMENT OPERATORS

When an operator (increment/decrement) is used as prefix with object, its value is incremented/ decremented before operation and on the other hand the postfix use of operator increments/decrements the value of variable after its use.

When ++ and -- operators are overloaded, no difference exists between postfix and prefix overloaded operator functions. The system has no way of determining whether the operators are overloaded for postfix or prefix operation. Hence, the operator must be overloaded in such a way that it will work for both prefix and postfix operations. The ++ or -- operator overloaded for prefix operation works for both prefix as well as postfix operations but with a warning message, but not vice-versa. To make a distinction between prefix and postfix notation of operator, a new syntax is used to indicate postfix operator overloading function. The syntaxes are as follows:

Operator ++( int ) // postfix notation
Operator ++( ) // prefix notation

The argument followed by operator (++ or --) should have type ‘int’. When a postfix operator ++ or operator -- is declared, the last parameter must be declared with the type int. No other types such as float, long etc., are allowed. We can use this operator with all types of variables including float, long etc. Declaring int does not mean that it is only for integer type. The following program illustrates overloading of ++ operator in postfix and prefix style.

8.6 Write a program to overload ++ and -- operator for prefix and postfix use.

# include <iostream.h>
# include <constream.h>

class number
{
   float x;
   public :
   number ( float k) {  x=k;  }

void operator ++ (int) // postfix notation 
{  x++;  }

void operator -- ( ) // prefix notation
{  --x;  }

void show( ) {   cout <<"\n x="<<x; }

};

void main( )

{
   clrscr( );
   number N(2.3);
   cout <<"\n Before Incrimination: ";
   N.show ( );
   cout <<"\n After Incrimination: ";
   N++; // postfix increment
   N.show( );
   cout <<"\n After Decrementation:";
   --N; // prefix decrement
   N.show( );
}

OUTPUT
Before incrimination:
x=2.3
After incrimination:
x=3.3
After decrementation:
x=2.3

Explanation: In this program, operator ++ and -- are overloaded. The ++ operator is overloaded for postfix use and -- operator is overloaded for prefix use. You can see the keyword (int) is followed by the operator ++, which is necessary for postfix notation of operator. The operator -- is overloaded for prefix operation. Here, a value of float member variable is incremented and decremented.

8.6 OVERLOADING BINARY OPERATORS

Overloading with a single parameter is called as binary operator overloading. Like unary operators, binary operator can also be overloaded. Binary operators require two operands. Binary operators are overloaded by using member functions and friend functions.

(1) Overloading Binary Operators Using Member Functions

If overloaded as a member function they require one argument. The argument contains value of the object, which is to the right of the operator. If we want to perform the addition of two objects o1 and o2, the overloading function should be declared as follows:

operator(num o2);

Where, num is a class name and o2 is an object.

 

To call function operator the statement is as follows:

o3=o1+o2;

We know that a member function can be called by using class of that object. Hence, the called member function is always preceded by the object. Here, in the above statement, the object o1 invokes the function operator( ) and the object o2 is used as an argument for the function. The above statement can also be written as follows:

o3=o1.operator +(o2);

Here, the data members of o1 are passed directly and data members of o2 are passed as an argument. While overloading binary operators, the left-hand operand calls the operator function and right-hand operand is used as an argument.

(2) Overloading Binary Operators Using Friend Functions

The friend can be used alternatively with member functions for overloading of binary operators. The friend function requires two operands to be passed as arguments.

o3=o1+o2;

o3=operator +(o1,o2);

Both the above statements have the same meaning. In the second statement, two objects are passed to the operator function.

The use of member function and friend function produces the same result. Friend functions are useful when we require performing an operation with operand of two different types. Consider the statements:

X =Y+3;

X =3+Y;

Where, X and Y are objects of same type. The first statement is valid. However, the second statement will not work. The first operand must be an object of the same class. This problem can be overcome by using friend function. The friend function can be called without using object. The friend function can be used with standard data type as left-hand operand and with an object as right-hand operand.

Following programs are illustrated based on the above discussion.

8.7 Write a program to overload + binary operator.

# include <iostream.h>
# include <conio.h>

class num
{
   private :
   int a,b,c,d;

   public :

   void input(void);
   void show(void);
   num operator+(num);

};


void num :: input( )
{
   cout <<"\n Enter Values for a,b,c and d : ";
   cin >>a>> b>>c>>d;
}


void num :: show( )
{
   cout <<" A= "<<a <<" B= " <<b <<" C = "<<c <<" D = "<<d <<"\n";
}

num num :: operator +(num t)

{
   num tmp;

   tmp.a=a+t.a;
   tmp.b=b+t.b;
   tmp.c=c+t.c;
   tmp.d=d+t.d;
   return (tmp);
}

main( )
{
   clrscr( );
   num X,Y,Z;
   cout <<"\n Object X";
   X.input( );
   cout <<"\n Object Y";
   Y.input( );
   Z=X+Y;
   cout <<"\nX : ";
   X.show( );
   cout <<"Y : ";
   Y.show( );
   cout <<"Z : ";

   Z.show( );
return 0;
}

OUTPUT
Object X
Enter Values for a,b,c and d : 1 4 2 1

Object Y
Enter Values for a,b,c and d : 2 5 4 2

X : A= 1 B= 4 C = 2 D = 1
Y : A= 2 B= 5 C = 4 D = 2
Z : A= 3 B= 9 C = 6 D = 3

Explanation: In the above program, binary operator + is overloaded. Using the overloading operator +, addition of member variables of two objects are performed and results are assigned to member variables of third object. In this program X, Y and Z are objects of class num. The statement Z=X+Y invokes the operator function. In this statement the object y is assigned to object t of operator function and member variables of X are accessed directly. The object tmp is used for holding the result of addition and it is returned to object Z after function execution. The function show( ) displays the values of three objects.

8.8 Write a program to perform multiplication using an integer and object. Use friend function.

# include <iostream.h>
# include <conio.h>

class num
{
   private :
   int a,b,c,d;

   public :

   void input(void);
   void show(void);
   friend num operator * (int ,num); // friend function declaration

};

void num :: input( )

{
   cout <<"\n Enter Values for a,b,c and d : ";
   cin >>a>> b>>c>>d;
}

void num :: show( )
{
   cout <<" A= "<<a <<" B= " <<b <<" C = "<<c <<" D = "<<d <<"\n";
}

num operator * (int a, num t)
{
   num tmp;

   tmp.a=a*t.a;
   tmp.b=a*t.b;
   tmp.c=a*t.c;
   tmp.d=a*t.d;
   return (tmp);
}
main( )
{
   clrscr( );
   num X,Z;
   cout <<"\n Object X";
   X.input( );
   Z=3*X;
   cout <<"\nX : ";
   X.show( );
   cout <<'Z : ";
   Z.show( );


return 0;
}

OUTPUT
Object X

Enter Values for a,b,c and d : 1 2 2 3
X : A= 1 B= 2 C = 2 D = 3
Z : A= 3 B= 6 C = 6 D = 9

Explanation: In the above program, the equation Z=3*X contains integer and class object. We know that the left-hand operand is always used to invoke the function and the right-hand operand is passed as an argument. In such type of equations member functions are not useful because the left-hand operand is integer and cannot invoke the function. Hence, the function operator * ( ) is declared as friend. The friend function calls the operator *( ) and carries the multiplication of each member variable by three. The results are displayed in the output.

8.7 OVERLOADING WITH friend FUNCTION

Friend functions are more useful in operator overloading. They offer better flexibility which is not provided by the member function of the class. The difference between member function and friend function is that the member function takes arguments explicitly. Quite the opposite, the friend function needs the parameters to be explicitly passed. The syntax of operator overloading with friend function is as follows:

friend return-type operator operator-symbol(variable1, variable2)
{
   statement1;
   statement2;
}

The keyword friend precedes function prototype declaration. It must be written inside the class. The function can be defined inside or outside the class. The arguments used in friend functions are generally objects of the friend classes. A friend function is similar to normal function. The only difference is that friend function can access private members of the class through the objects. Friend function has no permission to access private members of a class directly. However, it can access the private members via objects of the same class.

8.9 Write a program to overload unary operator usingfriend function.

# include <iostream.h>
# include <constream.h>

class complex
{
   float real,imag;
   public:

complex( )    // zero argument constructor
{
   real=imag=0;
}

complex (float r, float i) // two argument constructor
{
   real=r;
   imag=i;
}

friend complex operator - ( complex c)
{
   c.real=-c.real;
   c.imag=-c.imag;
   return c;
}

void display( )
{
   cout <<"\n Real : "<<real;
   cout <<"\n Imag : "<<imag;
}
};

void main( )
{
   clrscr( );
   complex c1(1.5,2.5),c2;
   c1.display( );
   c2=-c1 ;
   cout <<"\n\n After Negation \n";
   c2.display( );
}

OUTPUT
Real : 1.5
Imag : 2.5
After Negation
Real : -1.5
Imag : -2.5

Explanation: In the above program, operator -- is overloaded using friend function. The operator function is defined as friend. The statement c2=-c1 invokes the operator function. This statement also returns the negated values of c1 without affecting actual value of c1 and assigns it to object c2.

The negation operation can also be used with an object to alter its own data member variables. In such a case the object itself acts as a source and destination object. This can be accomplished by sending reference of object. Program 8.10 illustrates this.

8.10 Write a program to pass reference of an object to operator function.

# include <iostream.h>
# include <constream.h>

class complex
{
   float real,imag;
   public:
complex (float r, float i) // two argument constructor
{
     real=r;
     imag=i;
   }


   friend void operator - ( complex & c)
   {
     c.real=-c.real;
     c.imag=-c.imag;
   }

   void display( )
   {
     cout <<"\n Real : "<<real;
     cout <<"\n Imag : "<<imag;
   }
};


   void main( )
   {
     clrscr( );
     complex c1(1.5,2.5);
     c1.display( );
     -c1;
     cout <<"\n\n After Negation \n";
     c1.display( );
   }

OUTPUT
Real : 1.5
Imag : 2.5

After Negation

Real : -1.5
Imag : -2.5

Explanation: In the above program, the object c1 itself acts as source and destination object. The reference of object is passed to operator function. The object c is a reference object of c1. The values of object c are replaced by itself by applying negation.

8.8 TYPE CONVERSION

When constants and variables of various data types are clubbed in a single expression, automatic type conversion takes place. This is so for basic data types. The compiler has no idea about the user-defined data types and about their conversion to other data types. The programmer should write the routines that convert basic data types to user-defined data types or vice versa. There are three possibilities of data conversion as given below:

(1)  Conversion from basic data type to user-defined data type ( class type).

(2)  Conversion from class type to basic data type.

(3)  Conversion from one class type to another class type.

(1)   Conversion from Basic to Class Type

The conversion from basic to class type is easily carried out. It is automatically done by the compiler with the help of in-built routines or by applying typecasting. In this type the left-hand operand of = sign is always class type and right-hand operand is always basic type. The program given below explains the conversion from basic to class type.

8.11 Write a program to define constructor with no argument and with float argument. Explain how compiler invokes constructor depending on data type.

# include <iostream.h>
# include <conio.h>
   class data
   {   int x;
       float f;

   public :
         data ( )
         {

           x=0; 
           f=0;
         } 

   data ( float m)
   { 
     x=2; 
     f=m;
   } 

void show( )
   { 
   cout <<"\n x= "<<x <<" f = "<<f;
   cout <<"\n x= "<<x <<" f = "<<f;
   }
};
   int main( )
   { 
     clrscr( ); 
     data z; 
     z=1; 
     z.show( ); 
     z=2.5; 
     z.show( ); 
     return 0;
} 

OUTPUT
x= 2 f = 1
x= 2 f = 1
x= 2 f = 2.5
x= 2 f = 2.5

Explanation: In the above program, the class data has two member variables each of integer and float types respectively. It also has two constructors one with no argument and second with float argument. The member function show( ) displays the contents of the data members. In function main( ), z is an object of class data. When z is created the constructor with no argument is called and data members are initialized to zero. When z is initialized to one the constructor with float argument is invoked. The integer value is converted to float type and assigned member variable f. Again when z is assigned to 2.5, same process is repeated. Thus, the conversion from basic to class type is carried out.

(2)   Conversion from Class Type to Basic Data Type

In the previous example, we studied how compiler makes conversion from basic to class type. The compiler does not have any knowledge about the user-defined data type built using classes. In this type of conversion, the programmer explicitly needs to tell the compiler how to perform conversion from class to basic type. These instructions are written in a member function. Such type of conversion is also known as overloading of type cast operators. The compiler first searches for the operator keyword followed by data type and if it is not defined, it applies the conversion functions. In this type, the left-hand operand is always of basic data type and right-hand operand is always of class type. While carrying this conversion, the statement should satisfy the following conditions:

(1)  The conversion function should not have any argument.

(2)  Do not mention return type.

(3)  It should be a class member function.

8.12 Write a program to convert class type data to basic type data.

# include <iostream.h>
# include <conio.h>

   class data
   {
     int x;
     float f;

   public :

       data( )
       {
         x=0;
         f=0;
       }
   operator int( )
   {
     return (x);
   }

    operator float( )
    { return f; }

data ( float m)
{

   x=2;
   f=m;
}

void show( )
   {
   cout <<"\n x= "<<x <<" f = "<<f;
   cout <<"\n x= "<<x <<" f = "<<f;
   }
};

   int main( )
   {
     clrscr( );
     int j;
     float f;
     data a;
     a=5.5;

     j=a; // operator int ( ) is executed
     f=a; // operator float ( ) is executed

     cout <<"\n Value of j : "<<j;
     cout <<"\n Value of f : "<<f;
     return 0;
}

OUTPUT
Value of j : 2
Value of f : 5.5

Explanation: In the above program, the class data has two member variables each of integer and float data type. It also contains constructors as per described in the last example. In addition, it contains overloaded data types int and float. These functions are useful for conversion of data from class type to basic type. Consider the following statements:

(a) j=a;

(b) f=a;

In the first statement object a is assigned to integer variable j. We know that class type data is a combination of one or more basic data types. The class contains two-member functions operator int( ) and operator float( ). Both these functions are able to convert data types from class to basic. In statement (a) the variable j is of integer type, the function operator int( ) is invoked and integer value data member is returned. In statement (b), f is of float type, the member function operator float( ) is invoked.

(3)  Conversion from One Class Type to Another Class Type

When an object of one class is assigned to object of another class, it is necessary to give clear-cut instructions to the compiler. How to make conversion between these two user-defined data types? The method must be instructed to the compiler. There are two ways to convert object data type from one class to another. One is to define a conversion operator function in source class or a one-argument constructor in a destination class. Consider the following example:

  X=A;

Here, X is an object of class XYZ and A is an object of class ABC. The class ABC data type is converted to class XYZ. The conversion happens from class ABC to XYZ. The ABC is a source class and XYZ is a destination class.

We know the operator function operator data-type( ). Here, data type may be built-in data type or user-defined data type. In the above declaration, the data type indicates target type of object. Here, the conversion takes place from class ABC (source class) to class XYZ (destination class).

8.13 Write a program to convert integer to date and vice versa using conversion function in source class.


# include <iostream.h>
# include <stdlib.h>
# include <string.h>
# include <conio.h>

class date
{
   char d[10];
   public :

date( ) { d[0]=NULL; }

date(char *e) { strcpy(d,e); }

void show( ) { cout <<d; }
};

class dmy
{

   int mt,dy,yr;

   public:

dmy ( ) { mt=dy=yr=0; }

dmy (int m, int d, int y)
{
   mt=m;
   dy=d;
   yr=y;
}
operator date( )
{
   char tmp[3],dt[9];


   itoa(dy,dt,10);
   strcat(dt,"-");

   itoa(mt,tmp,10);

   strcat(dt,tmp);
   strcat(dt,"-");

   itoa(yr,tmp,10);
   strcat(dt,tmp);
   return (date(dt));
}

   void show( )
{
   cout <<dy<<" "<<mt<<" "<<yr;
}
};


int main( )
{
clrscr( );
date D1;
dmy D2(1,7,99);

D1=D2;
cout <<endl<<"D1=";
D1.show( );
cout <<endl<<"D2=";
D2.show( );
return 0;
}

OUTPUT

D1=7-1-99
D2=7 1 99

Explanation: In the above program, date and dmy are two classes declared. In function main( ), D1 is an object of class date and D2 is an object of class dmy. The object D2 is initialized.

The statement D1=D2 initializes D1 with D2. Here, both the objects D1 and D2 are of different types hence the conversion function date( ) is called to perform the conversion from one object to another object.

8.14 Write a program to convert integer to date and vice versa using conversion function in destination class.

# include <iostream.h>
# include <conio.h>
# include <stdlib.h>
# include <string.h>

class dmy
{
   int d,m,y;

   public :

dmy( ) { d=m=y=0; }

dmy(int da, int ma, int ya) { d=da; m=ma; y=ya; }

int day( ) { return (d); }

int month( ) { return (m); }

int year( ) { return (y); }


void show( ) { cout <<d<<" "<<m<<" "<<y; }
};

class date
{
   private :
   char dts[9];
   public :
   date( ) { dts[0]=0; }

date ( char *e) { strcpy (dts,e); }

void show( ) { cout <<dts; }

date ( dmy k)
{
   int d=k.day( );    // first member function
   int m=k.month( );  // second member function
   int y=k.year( );   // third member function

   char tmp[3];
   itoa(d,dts,10);
   strcat(dts,"-");
   itoa(m,tmp,10);
   strcat(dts,tmp);
   strcat(dts,"-");
   itoa(y,tmp,10);
   strcat(dts,tmp);
}

};

int main( )
{
   clrscr( );
   date D1;
   dmy D2(1,3,77);
   D1=D2;
   cout<<endl<<"D1=";

   D1.show( );
   cout <<endl<<"D2=";
   D2.show( );
   return 0;
}

OUTPUT
D1=1-3-77
D2=1 3 77

Explanation: In the above program, as soon as the statement D1=D2 is executed, the one-argument constructor defined in the class date is invoked. The one argument constructor carries the conversion. The constructor calls three-member function of dmy class to get the day, month, and year of the date. Here, in this program conversion is done by using the constructor in destination object.

8.15 Write a program to convert class data type to another class data type.

# include <iostream.h>
# include <conio.h>
   class minutes
   {
    int m;

   public :

   minutes( )

   { m=240; }


   get( )
   { return (m);}


   void show( )
   { cout <<"\n Minutes = "<<m; }


};

class hours
{
int h;
public :
void operator = (minutes x);
void show ( )
    { cout <<"\n Hours = "<<h; }


};

void hours:: operator = (minutes x)
{
    h=x.get( )/60;
}

   int main( )
   {
     clrscr( );
     minutes minute;
     hours hour;
     hour=minute;
     minute.show( );
     hour.show ( );
return 0;
}

OUTPUT
Minutes = 240
Hours = 4

Explanation: In the above program, two classes are declared. The class minutes has one integer member variable m and two member functions get( ) and show( ). It also contains constructor without argument. The class hours has one integer member variable and show( ) member function. The class hours contains overloaded operator function. In function main( ), minute is an object of class minutes and hour is an object of class hours. The program converts minutes to hours. The equation hour=minute invokes the operator function. In function operator( ), x is an object of class minutes. The object x invokes the member function get( ) that returns total number of minutes. Number of hours is obtained by dividing the total number of minutes by 60. The equation h=x.get( )/60 performs this task and assigns result to h. Thus, the result of the program is as per given above.

8.9 RULES FOR OVERLOADING OPERATORS

• Overloading of an operator cannot change the basic idea of an operator. When an operator is overloaded, its properties like syntax, precedence, and associativity remain constant. For example A and B are objects. The following statement
A+=B;
assigns addition of objects A and B to A. The overloaded operator must carry the same task like original operator according to the language. The following statement must perform the same operation like the last statement.
A=A+B;

• Overloading of an operator must never change its natural meaning. An overloaded operator + can be used for subtraction of two objects, but this type of code decreases the utility of the program. Remember that the aim of operator overloading is to comfort the programmer to carry various operations with objects. Consider the following program.

8.16 Misuse of operator overloading. Perform subtraction using + operator.

# include <iostream.h>
# include <constream.h>

class num
{
   int x;
   public:

   num( ) { x=0; }
   num( int k) { x=k; }

num operator + ( num n)
{
   num s;
   s.x=x-n.x;
   return s;
}

   void show( ) { cout <<"\n x="<<x; }

};

void main( )

{
   clrscr( );
   num a(10), b(5),c;
   c=a+b;
   c.show( );
}

OUTPUT
X=5

Explanation: In the above program, the operator + is overloaded. It performs subtraction. Such type of misuse must be avoided while overloading operators. The programmer thought that it would perform addition but in reality, it performs subtraction.

 

• The overloaded operator should contain one operand of user-defined data type. Overloading operators are only for classes. We cannot overload the operator for built-in data types.

• Overloaded operators have the same syntax as the original operator. They cannot be prevailing over the original operators.

• There is no higher limit to the number of overloading for any operator. An operator can be overloaded for a number of times if the arguments are different in each overloaded operator function.

• Operator overloading is applicable within the scope (extent) in which overloading occurs.

• Only existing operators can be overloaded. We cannot create a new operator.

• C++ has wide range of operators. However, few operators cannot be overloaded to operate in the same manner like built-in operators. The operators given in Table 8.1 cannot be overloaded.

Table 8.1 Non-overloadable operators

Operator Description
. Member operator
.* Pointer to member operator
:: Scope access operator
?: Conditional operator
Sizeof( ) Size of operator
# and ## Preprocessor symbols

For example, operators such as ?:, :: and .* are combinations of more than one symbol. The condition operator needs three arguments. It is only one operator that requires three arguments. Hence, above operators cannot be overloaded.

• The operators given in Table 8.2 cannot be overloaded using friend function.

• When unary operators are overloaded using member functions, it requires no explicit friend argument and returns no value whereas when it is overloaded using friend function, it requires one reference argument.

• When binary operators are overloaded using friend functions, it requires two arguments whereas when overloaded using member function, it requires one argument.

Table 8.2 Non-overloadable operators with friend function

Operator Description
( ) Function call delimiter/operator
= Assignment operator
[] Subscripting operator
-> Class member access operator

8.10 ONE ARGUMENT CONSTRUCTOR AND OPERATOR FUNCTION

We know that a single argument constructor or an operator function could be used for conversion of objects of different classes. A large range of classes as class libraries are available with the compiler, however, they are linked with the main program. Their source code is invisible to us. Only objects of these classes can be used. The user cannot change the in-built classes. The problem occurs when programmer attempts conversion from object of class declared by him/her to type of in-built class. This problem can be avoided by defining conversion routine in the user-defined class. The conversion routine may be single argument constructor or an operator function. It depends on the object whether it is source or destination object. Table 8.3 describes conversion type and place of routine to be defined, followed by description.

Table 8.3 Conversion types

(A)  In case both the source and destination objects are of user-defined type, the conversion routine can be carried out using operator function in source class or using constructor in destination class.

(B)  If the user-defined object is a destination object, the conversion routine should be carried out using single argument constructor in the destination object's class.

(C)  In case the user-defined object is a source object, the conversion routine should be carried out using an operator function in the source object's class.

Defining multiple conversion routines puts the complier in an uncertain condition. The compiler fails to select appropriate conversion routines. For example, if one argument constructor is present in destination class and operator function in source class, the complier cannot select appropriate routines. Hence, while defining conversion routines, follow the conditions given in Table 8.3.

8.11 OVERLOADING STREAM OPERATORS

The predefined objects cin and cout are used to perform various input/ output operations in C++. The extraction operator (>>) is used with cin object to carry out input operations. The insertion operator (<<) is used with cout object to carry out output operations. In Chapter “Input and Output in C++”, we learnt how to create objects similar to cin and cout. It is also possible to overload both these extraction and insertion operator with friend function.

The syntax for overloading (<<) insertion operator is as follows:

friend ostream & operator << ( ostream & put , v1)
{
   // code
   return put;
}

The keyword friend precedes the declaration. The ostream is an output stream class followed by reference and keyword operator. The put is an output stream object like cout. The v1 is a user-defined class object. The following program explains overloading of insertion operator with friend function.

8.17 Write a program to overload insertion operator (<<) with friend function.

# include <iostream.h>
# include <constream.h>

class string
{
   char *s;
   public:

   string ( char *k)
   {
     s=k;
   }

friend ostream & operator << (ostream &put, string & k) {
     put <<k.s;
     return put;
   }
};

int main( )

{
   clrscr( );
   string s("INDIA");
   cout<<s;
   return 0;
}

OUTPUT
INDIA

Explanation: In the above program, the insertion operator << is overloaded with friend function. The overloaded operator allows us to display contents of objects directly using cout statement. The statement cout<<s; displays contents of object s on the screen.

In the same way, extraction operator can be overloaded. The syntax for overloading extraction operator follows below:

friend istream & operator >> ( istream & get, v2)
{
   // code
   return get;
}

The following program explains overloading of extraction operator.

8.18 Write a program to overload extraction operator usingfriend function.

# include <iostream.h>
# include <constream.h>

class string
{
   char *s;

   public:

friend istream & operator >> (istream &get, string & k)
   {
     cout <<"\n Enter a string : ";
     get >>k.s;
     return get;
   }

};

int main( )
{
     clrscr( );
     string s;
     cin>>s; // input string
     return 0;

}

OUTPUT
BEST LUCK

Explanation: This program is the same as the last one. Here, extraction operator is overloaded. The object s is directly used with cin statement. After execution of this statement, the overloaded operator is invoked.

SUMMARY

(1) Operator overloading is one of the most helpful concepts introduced by the C++ language. Operator overloading provides the capability to redefine the language in which working of operator can be changed.

(2) Overloaded operators are redefined within a C++ class using the keyword operator followed by an operator symbol. When an operator is overloaded, the produced symbol is called the operator function name.

(3) Overloading of operator cannot change the basic idea of an operator. When an operator is overloaded, its properties like syntax, precedence, and associativity remain constant.

(4) The keyword operator defines a new action or operation to the operator.

(5) The operators ++, --, and – are unary operators. The unary operators ++ and -- can be used as prefix or suffix with the functions. These operators have only single operand.

(6) Binary operators require two operands. Binary operators are overloaded by using member functions and friend functions.

(7) The conversion routine may be single argument constructor or an operator function. It depends on the object whether it is source or destination object.

(8) Defining multiple conversion routines puts the complier in an uncertain condition. The compiler fails to select appropriate conversion routines.

(9) There are three possibilities of data conversion. They are given below:

(i) Conversion from basic data type to user-defined data type (class type)- The conversion from basic to class type is easily carried out. It is automatically done by the compiler with the help of in-built routines or by applying type casting.

(ii) Conversion from class type to basic data type- The compiler does not have any knowledge about the user-defined data type built using classes. In this type of conversion the programmer, needs explicitly to tell the compiler how to perform conversion from class to basic type. These instructions are written in a member function. Such type of conversion is also known as overloading of type cast operators.

(iii) Conversion from one class type to another class type- When an object of one class is assigned to an object of another class it is necessary to give clear-cut instructions to the compiler about how to make conversion between these two user-defined data types. Using constructor or conversion this function can be performed.

(10) The conversion routine may be single argument constructor or an operator function. It depends on the object whether it is source or destination object. Table 8.3 describes conversion type and place of routine to be defined.

EXERCISES

[A] Answer the following questions.

(1)  What do you mean by operator overloading?

(2)  What is the use of the keyword operator?

(3)  What are the rules for overloading operators?

(4)  What is the difference between operator overloading and function overloading?

(5)  What is the difference between overloading of binary operators and unary operators?

(6)  How are friend functions used to carry out overloading of operators? In which situation are they helpful?

(7)  Explain conversion of data from basic to class type. Explain the role of compiler.

(8)  Explain conversion from class to basic type.

(9)  Explain conversion from class type to class type.

(10)  What are source and destination objects?

(11)  List the keywords that cannot be overloaded.

(12)  Describe rules for operator overloading.

(13)  Write syntaxes for overloading extraction and insertion operator with friend function.

[B] Answer the following by selecting the appropriate option.

(1)  The keyword operator is used to overload an

(a)  operator

(b)  function

(c)  class

(d)  none of the above

(2)  Consider the equation Z=3*X, to overload the * operator one of the following function A is used. Z and X are objects of the same class.

(a)  friend

(b)  virtual

(c)  public

(d)  none of the above

(3)  Which one of the following conversions is automatically carried by the compiler

(a)  conversion from basic data type to user-defined data type (class type)

(b)  conversion from class type to basic data type

(c)  conversion from one class type to another class type

(d)  none of the above

(4)  Which one of the following operator cannot be overloaded

(a)  dot operator (.)

(b)  plus operator ( + )

(c)  & ampersand operator

(d)  -- operator

(5)  In postfix overloading of operator (++ or --), the last argument should have type

(a)  int

(b)  void

(c)  float

(d)  long

(6)  A, B and C are objects of same class. To execute the statement C=A+B, the operator must be overloaded.

(a)  +

(b)  =

(c)  both (a) and (b)

(d)  none of the above

(7)  The operator function returns value of

(a)  basic type

(b)  void type

(c)  class type

(d)  all types

[C]  Attempt the following programs.

(1)  Write a program to overload < operator and display the smallest number out of two objects.

(2)  Write a program to overload = operator. Assign values of data members of one object to another object of the same type.

(3)  Write a program to overload = = operator. Compare two objects using overloaded operator.

(4)  Write a program to carry conversion from class type to basic data type.

(5)  Write a program to carry conversion from one class data type to another class type.

(6)  Write a program to evaluate the equation A=B*3, where A and B are objects of same class. Use friend function.

(7)  Write a program to pass reference of object to operator function and change the contents of object. Use single object as source and destination object.

(8)  Write a program to declare two classes rupees and dollars. Declare objects of both the classes. Convert rupees to dollars and vice versa. Perform conversion using user-defined conversion routines.

(9)  Write a program to convert square to square root and vice versa.

(10)  Write a program to declare matrix class and perform addition of matrix class objects.

[D]  Find bugs in the following programs.

(1)
    Class num
    {   int x;
        public:

        num ( int k) { x=k; }

        int operator + ( num n)
          {   num s(0);
              s.x=x-n.x;
              return x; }
    };

    void main ( )
    {   num a(1), b(5),c(0);
        c=a+b; }

{2)
    struct num
    {   int x;
        num ( int k) { x=k; }

        void operator ++ ( )
        { ++x; }
    };

    void main ( )
    {
       num b(2);
       b++;
       cout <<b.x;
    }