operator overloding. objectives operator overloading type conversion new style casts and rtti(run...
TRANSCRIPT
OPERATOR OVERLODING
Objectives
• Operator Overloading• Type Conversion• New Style Casts and • RTTI(Run time type Information)
• Overloading an operator means programming an operator to work on
operands of types it has not yet been designed to operate.
Overloading Operators—The Syntax:
• Operators are overloaded by writing operator-overloading functions.
• These functions are either member functions or friend functions of that
class whose objects are intended as operands of the overloaded operator.
• Operator overloading functions are very similar to the member functions
and friend functions. The only thing peculiar about them is their name.
• The names of operator-overloading functions are composed of the
keyword operator followed by the symbol of the operator being
overloaded.
• We can overload all the C++ operators except the following:
(i) class member access operator(., .*)
(ii) scope resolution operator (::)
(iii) size operator (sizeof) (iv) conditional operator(?:)
Overloading Operators—The Syntax:
• The syntax for member functions that overload a given operator is as follows:
• Member functions that overload operators can be private, protected, or public.
• The prototype of the operator-overloading function specifies a return type.
• The keyword operator follows the return type.
• This is followed by the symbol of the operator being overloaded.
• Finally, a pair of parentheses containing the formal arguments is specified.
Overloading Operators—The Syntax:
• The syntax for a friend function that overloads a given operator is as follows:
• Operator-overloading function can be defined and used as a member function. (See Example 1)
• Operator-overloading function can be defined as a friend function. (See Example 2).
Defining operator overloading using member function
Example1:
class <class name>
{
<return type> operator <op> (<argumentlist>); //prototype
};
<return type> <classname> ::operator <op> (<argumentlist>)//definition
{ //Function body
}
• Where return type is the type of value returned by the specified operation.
•operator op is the function name.
• Operator functions must be either member functions or friend functions.
Overloading Operators—The Syntax:
Defining operator overloading using friend function
Example2:
class <class name>
{
friend <return type> operator <op> (<argumentlist>); //prototype
};
return type operator <op> (<argumentlist>)//definition
{
//Function body
}
• Where return type is the type of value returned by the specified operation.
• operator op is the function name.
Overloading Operators—The Syntax:
Differences between member function and friend function w.r.to
operator overloading
•A basic difference between them is that a friend function will have only
one argument for unary operators and two for binary operators, while a
member function has no arguments for unary operators and only one for
binary operators.
•The friend function takes one argument more than member function
because the invoking object appears as an explicit parameter to the
friend function whereas in member functions it is passed as an implicit
parameter.
The process of operator overloading involves the following steps:
1. Create a class that defines the data type to be used in the overloading.
2. Declare the operator function operator op() in the public part of the
class. It may be either a member function or a friend function.
3. Define the operator function to implement the required operations.
• Overloaded operator functions can be invoked by expressions such as:
op x or x op : for unary operator
x op y : for binary operators
Overloading unary minus
// overloading unary minusclass space {
int x; int y; int z;
public :
void getdata(int a, int b, int c);
void display(void);
//friend void operator-(space &s); // declaration
void operator-();// using member function
};
void space :: getdata(int a, int b, int c)
{
x=a;y=b;z=c;
}
void space ::display(void)
cout<<x;
cout<<y;
cout<<z;
}
void space:: operator-()
{
x=-x;
y=-y;
z=-z;
}
int main()
{
space S;
S.getdata(10,-20,30);
cout<<"S:";
S.display();
-S;
cout<<"S";
S.display();
return 0;
}
output
10 -20 30
-10 20 -30
To overload unary minus using friend function
friend void operator-(space &s); // declaration
void operator -(space &s) // definition
{
s.x=-s.x;
s.y=-s.y;
s.z=-s.z;
}
overloading binary operators to add two complex numbers
c=sum(A,B) // functional notation
c=A+B // arithmetic notation
overloading + operator to add two complex numbers:
class complex
{
float x;//real part
float y;//imaginary part
public:
complex(){};// constructor 1
complex (float real, float imag)
//constructor 2
{
x=real;y=imag;
}
complex operator+(complex);
void display(void);
};
complex complex::operator+(complex c)
{
complex temp;
temp.x=x+c.x;
temp.y=y+c.y;
return(temp);
}
void complex::display(void)
{
cout<<x<<"+j"<<y<<"\n";
}
int main()
{
complex C1,C2,C3;
C1=complex(2.5,3.5);
C2=complex(1.6,2.7);
C3=C1+C2;
cout<< "C1=";C1.display();
cout<<"C2=";C2.display();
cout<<"C3=";C3.display();
return 0;
}
C1=2.5+i3.5;
C2=1.6+i2.7;
C3=4.1+i6.2;
The operator+() operates in the following way:
1. it receives only one complex argument explicitly
2. it returns a complex type value
3. it is a member function of complex
C3=C1+C2; // invokes operator+() function
• C1 takes the responsibility of invoking function.
• C2 is the argument object
The statement is equivalent to
C3=C1.operator+(C2);// usual function call
temp.x=x+c.x;
c.x refers to object C2 and x refers to object C1, since data
members of C1 are accessed directly.
OVERLOADING BINARY OPERATOR USING FRIENDS
The complex number program can be modified as follows:
1.Replace the member function declaration by the friend function
declaration
friend complex operator+(complex, complex);
2.Redifine the operator function as follows
complex operator+(complex a, complex b)
{
complex temp;
temp.x=a.x+b.x;
temp.y=a.y+b.y;
return(temp);
}
in this case the statement
C3=C1+C2 is equivalent to
C3=operator+(C1,C2);
Reason for using overloading using friend function:
Suppose A and B are the objects of the same class then,
A=B+2; // will work for the member function &friend function
A=2+B; //will not work for member function , but will work for
//friend function, since left-hand operand is responsible for
invoking the member function.
Compiler Interpretation of Operator-Overloading Functions• The compiler interprets operator-overloading functions is shown :
s3=s1+s2; // s1,s2 and s3 are objects of the class string
• If the operator overloading function has been declared as a member function, then the following interpretation is satisfied:
s3=s1.operator+(s2);
• If the operator overloading function has been declared as a friend function, then the following interpretation is satisfied:s3=operator+(s1, s2);
• However, we must note that the operator-overloading functions can be called directly from within the application programs :s3=s1.operator+(s2); s3=operator+(s1, s2);
• The benefit of overloading the operator will not be felt if the overloaded operators are directly called in this manner.
Manipulating string using operators
• There are no operators for manipulating the strings.
• In order to copy one string to another , the programmer must first
determine its length and allocate the required amount of memory.
• C++ allows us to create our own definitions of operators that can be
used to manipulate the strings very much similar to the decimal
numbers.• We can manipulate the strings using string class as follows:• For example , we shall able to use statements like:
string3 = string1 + string2
Example illustrating the applications of overloaded operators
#include <iostream.h>
#include <string.h>
class string
{
char *p;
int len;
public:
string () { len = 0; p=0; } //create null string
string (char *s)
{
len=strlen(s); p=new char[len+1];
strcpy(p,s);
}
friend string operator + (string &s, string &t);friend string void show(string s);
};
string operator + (string &s , string &t){
string temp;temp.len=s.len+t.len;temp.p=new char[temp.len+1];strcpy(temp.p,s.p);strcat(temp.p,t.p);return (temp);
}
void show (string s)
{
cout<<s.p<<endl;}
void main()
{
string s1 = "New"; // string s1(“New”);
string s2 = "Delhi";
string s3;
clrscr();
s3=s1+s2;
show(s1);
show(s2);
show(s3);
getch();
}
Operator overloading becomes mandatory under the following circumstances:
1. Objects of the class acquire resources dynamically during run time and no two objects should share the same copy of the resource.
Example:
string s1(“abc”), s2;
s2=s1;
2. Objects of the class acquire some resources dynamically during run time and no two objects should share even different copies of the resource.
Example:
s2=s1; // should not compile at all
The solution is to declare the function to overload the assignment operator in the private section of the class. This will produce an compile time error.
3. Objects need to be passed as parameters in function templates and the operators being used on template class objects within the template functions should work in the same way on objects of the class.
4. Change in the implementation of the class forces an undesirable change in its interface in turn necessitating the rewriting and recompiling of the application programs.
Operator overloading becomes mandatory under the following circumstances:
5. The default action of the dynamic memory management operators (new and delete) are unsuitable for the class being designed. • By default, the new operator allocates the amount of memory requested, and also stores the amount of memory allocated in the memory itself.• In memory critical applications, overloading the new operator can
prevent a huge wastage of memory.• By default, the new operator simply allocates memory for the object whose type is passed as an operand to it. To prevent the class from having more than one object, subsequent calls to the new operator should not create more objects, but merely return the address of the object that was created in response to the first call to the new operator.
6. Overloading provides better readability of code.
Rules for Operator Overloading
1. New operators cannot be created: New operators (such as **) cannot be created.
Example:
class A
{
public: void operator **(); //illegal attempt to create a new operator
};
2. Meaning of existing operators cannot be changed:
• Any operator-overloading function (member or friend) should take at least one operand of the class of which it is a member or friend. Thus, it is not possible to change the manner in which an existing operator works on operands of fundamental types (char, int, float, double).
Example:
class A
{ public: friend int operator + (int, int); //illegal attempt to modify the behavior of operators
};
• In case of member functions, this condition is automatically enforced because the address of the calling object is implicitly passed as a parameter to it.
• However, in case of friend functions, the library programmer needs to take extra care. By ensuring that at least one operand of an operator-overloading function must be of the class type, the compiler ensures that the meanings of the existing operators cannot be changed.
Rules for Operator Overloading
3.Some of the existing operators cannot be overloaded: The following operators cannot be overloaded:
:: (scope resolution)
. (member selection)
.* (member selection through pointer to member)
?: (conditional operator)
sizeof (finding the size of values and types)
typeid (finding the type of object pointed at)
4.Some operators can be overloaded using non-static member functions only:
= (Assignment operator)
() (Function operator)
[] (Subscripting operator)
-> (Pointer-to-member access operator)
These operators cannot be overloaded using friend or static functions
Rules for Operator Overloading5. Number of arguments that an existing operator takes cannot be changed: Operator
overloading functions should take the same number of parameters that the operator being overloaded ordinarily takes. For example, the division operator takes two arguments.
Example:
class A
{
public: void operator / (); //illegal attempt to modify the no. of arguments
};
6. Overloaded operators cannot take default arguments: An illegal attempt to assign a default value to an argument of an operator-overloading function causes a compile-time error.
Example:
class A
{
public: void operator / (int =0); //illegal attempt to assign a default value to opeartor overloaded function
};
Rules for Operator Overloading
7. It is highly imprudent to modify the values of the operands that are passed to the operator-overloading functions.
Example:
class string
{
char *cstr;
long int len;
public:
string operator + (string &);
};
string string :: operator + (string &ss)
{
this -> cstr=NULL; //BUG: left-hand parameter changed
// rest of the function
ss.cstr->=NULL ; //BUG
}
To guard against this mishap, the operator-overloading function can be declared.
Rules for Operator Overloading
To guard against this mishap, the operator-overloading function can be declared:
class string
{
char *cstr;
long int len;
public:
string operator + (const string &) const;
};
Overloading Various Operators1. Overloading increment and decrement operators (prefix and postfix):
The prototype and definition of the operator-overloading function for the
class Distance is shown in the example:
• The operator-overloading function should be public because it will mostly
be called from within functions that are not members of the class
Distance.
• It should not be a constant member function since it will certainly modify
the value of at least one of the data members of the calling object.
• First, the increment operator works (since it is in prefix notation).
• The explicit call to the constructor creates a nameless object of the class
distance by passing the incremented value of feet and the unaltered value
of inch as parameters.
• The operator-overloading function returns the nameless object thus
constructed.
Overloading Various OperatorsExample: Overloading increment operator for distance class in prefix notation
class distance
{
int feet;
float inch;
public:
distance () { feet=inch=0;}
distance (int x, float y) { feet=x; inch=y;}
// rest of the class
distance operator ++();
};
distance distance:: operator ++ ()
{
return distance(++feet, inch);
}
• If d1 and d2 are the 2 objects of the class distance , then the statement:
d2=++d1;
Is interpreted by the compiler as:
d2=d1.operator++();
Overloading Various Operators
• If the call to the operator-overloading function is on the right-hand side of the assignment operator, the values of the returned object will expectedly be copied to the object on the left.
• An additional operator-overloading function to overload the increment operator can be defined in postfix notation.
Example: Overloading increment operator for distance class in postfix notation
class distance
{ public:
// rest of the class
distance operator ++(int);
};
distance distance:: operator ++ (int)
{
return distance(feet++, inch);
}
Overloading Various Operators• The constructor gets called before the increment operator executes
because the increment operator has been purposefully placed in postfix
notation. Thus, a nameless object with the initial values of the calling object
is created.
• The increment operator increments the value of feet data member of the
calling object. Finally, the nameless object constructed earlier with the initial
values of the calling object is returned.
• If the call to this operator-overloading function is on the right-hand side of
the assignment operator and there is an object of the class distance on its
left, then the object on the left will get the initial values of the object on the
right. The value of the object on the right will alone be incremented.
• These two operator-overloading functions convincingly duplicate the default
action of the increment operator on intrinsic types.
Overloading Various Operators• If we provide an operator-overloading function for the increment operator in
prefix notation, we must provide one for the postfix notation also.
• Decrement operators are overloaded in the same way as the increment
operators.
• If d1 and d2 are the 2 objects of the class distance , then the statement:
d2=d1++;
Is interpreted by the compiler as:
d2=d1.operator++ (0); //integer 0
•If the compiler finds a exact matching prototype for postfix form, it
compiles without warning errors.
•However, if it finds prototype for prefix form only it gives warning but
still compiles with distance::operator+(). i.e., the compiler first looks
for a function with an integer as a formal parameter.
Overloading Various Operators
Overloading unary minus and unary plus operator:
The unary minus operator can be overloaded through a member function. (The
operator can be overloaded by a friend function also.)
Example:
class A
{ int x;
public: A(int=0);
A operator –(); // or friend A operator –( A &)
};
A A::operator-() // A operator – (A &Aobj)
{ return A(-x); // or return A (-Aobj.x) ;
}
Overloading Various Operators
Overloading arithmetic operators:
1.The syntax for overloading the arithmetic operators through member
functions is as shown:
<return type> operator <arithmetic symbol> (<parameter list>);
•An object that will store the value of the right-hand side operand of the
arithmetic operator will appear in the list of formal arguments.
•The left-hand side operand will be passed implicitly to the function since the
operator-overloading function will be called with respect to it.
2. The syntax for overloading the arithmetic operators through friend functions
is as shown:
friend <return type> operator <arithmetic symbol> (<parameter list>);
•Objects that store the values of the left-hand side and the right-hand side
operands of the arithmetic operator will appear in the list of formal arguments.
Overloading Various Operators
Overloading arithmetic operators:• How to overload the addition operator for the class Distance is as shown :
distance d1(7,8), d2(4,9),d3;
d3=d1+d2;
•This can also be done through either by member function or by friend function. •This code works fine if the right-hand side operand of the addition operator is an object of class Distance.•However, if it is a float-type value, then the preceding function will not work.
Example:
d3=d1+9.5; //will not work
Because, the compiler will interpret this statement as:
d3= d1.operator+(9.5);
Overloading Various Operators
Overloading arithmetic operators:
•However, introducing a suitable constructor that converts from float to Distance
solves the problem.
class distance
{ public:
distance (float );
distance operator +(distance);
};
distance:: distance( float p)
{
feet= (int) p;
inch=(p-feet)*12;
}
•However, if the left-hand side operand is of float type, the member function given in Listing 8.20 is replaced with a friend function. (See Listing 8.22).
Overloading Various Operators
Overloading arithmetic operators: •However, if the left-hand side operand is of float type, for example:
d3=9.5+d1; //will not work for member function
Because, the compiler will interpret this statement as:
d3= 9.5.operator+(d1);//illegal
•the member function given in previous example is replaced with a friend function.
class distance
{ public:
distance (float );
friend distance operator +(distance, distance);
};
distance:: distance( float p)
{
feet= (int) p;
inch=(p-feet)*12;
}
Overloading Various Operators
• The friend function given in previous example tackles all three conditions as follows:
1. Both the left-hand side and the right-hand side operands are objects of class Distance: The operator-overloading function is called straight away without any prior conversions.
2. The right-hand side operand is a float-type value while the left-hand side operand is an object of class Distance: The right-hand side operand is first converted into an object of the class Distance by the constructor and then the operator-overloading function is called.
3. The left-hand side operand is a float-type value while the right-hand side operand is an object of class Distance: The left-hand side operand is first converted into an object of the class Distance by the constructor and then the operator-overloading function is called.
Overloading Various Operators
• Where both operands are float-type values, the operator-overloading
mechanism will not be invoked at all. Instead, the float-type values will
simply get added to each other.
• For the class Distance, there is no function that converts a float-type value
to an object of class Distance. The constructor that takes a float-type value
as a parameter and initializes the object with it will be called. This is
despite the fact that the object is being created and initialized by two
separate statements. Such a constructor is called an implicit constructor.
• Having both a friend function and a member function will lead to ambiguity
errors.
Overloading Various Operators
Overloading assignment operator:
• The assignment operator is a binary operator. If overloaded, it must be overloaded by a non-static member function only. Thus, the syntax for overloading the assignment operator is as shown :
class_name & operator = (class_name &);
• By default, the compiler generates the function to overload the assignment operator if the class designer does not provide one. This default function carries out a simple member-wise copy. For example:
Example:
class A
{
public:
&A operator = (A&);
};
A & A::operator =(A & rhs)
{ *this=rhs;
return *this;
}
In most cases, this default assignment operator is sufficient. However, there are cases where this default behaviour causes problems if the copy constructor is not defined. (See Listing 8.30). Pg 236 further explanation required
• In most cases, the default assignment operator is sufficient. For classes that acquire resources dynamically, default causes problem.
• String s1(“abcd”); String s2= s1; • The conclusion is that the assignment operator must
be defined for a class whom the copy constructor is defined. Example
String (const String &); // copy constructor
String & operator = (const String &);
String String :: operator = (const String & ss)
{ if this != &ss)
{if(cStr != NULL) {delete[] cStr; cStr = NULL;len=0; }
Overloading Various Operators
Overloading assignment operator:
String & string ::operator =(string & ss)
{ if (this!=&ss)
{
if (cstr!=NULL)
{ delete [] cstr;
cstr=NULL;
len=0;
}
if (ss.cstr!=NULL)
{ len=ss.len;
cstr=new char[len+1];
strcpy(cstr,ss.cstr);
}
} return *this;
}
(i)If LHS.cStr =NULL and RHS.cStr =NULL
• If LHS.cStr =NULL, then the first inner ‘if’ fails and the corresponding if block does not execute. If RHS.cStr =NULL then the second inner ‘if’ fails and the block does not execute.
• The entire function does not do anything except that it returns the calling object by reference. The value of LHS operand remains unchanged.
• (ii)If LHS.cStr =NULL and RHS.cStr !=NULL
If LHS.cStr =NULL, then the first inner ‘if’ fails and the corresponding if block does not execute.
If RHS.cStr =!NULL then the second inner ‘if’ (if(ss.cStr!=0)) succeeds and the corresponding ‘if’block executes. It does the following: (i) correctly sets the value of len member of the calling object to be equal to the length of the memory block that will hold a copy of the string at which ‘cStr’ member of the RHS object is pointing. (ii) allocates just enough memory to hold a copy of the string at which the cStr member of the RHS object is pointing and makes cStr of LHS object point at it
• Copies the string at which the cStr of RHS object is pointing in to the memory block at which the cStr of LHS object is pointing.
(iii) If LHS.cStr !=NULL and RHS.cStr =NULL then the first inner ‘if’ succeeds and the corresponding if block executes.
• It deallocates the memory the block at which cStr of LHS object points, sets its value to NULL and sets the value of the ‘len’ member of LHS object to ‘0’. If RHS.cStr=NULL then the second inner ‘if’ fails and the corresponding if block does not execute.
• If LHS.cStr !=NULL and RHS.cStr !=NULL then the first inner block succeeds and executes.
It deallocates the memory the block at which cStr of LHS object points, sets its value to NULL and sets the value of the ‘len’ member of LHS object to ‘0’. If RHS.cStr!=NULL then the second inner ‘if’ succeeds and the corresponding if block executes. It does the required things.
• The preceding function accepts the argument as a const reference and returns the calling object by reference – to test and safeguard against self-assignment. The if condition ‘this == &ss’ tests to find out whether an object is being equated with itself or not.
• An object may be get equated as
String s1 ; s1 = s1; or
String s1; String &s2 = s1; s2 = s1;• Each of the above will cause an extension of the
function to overload the assignment operator. In each of the cases ‘if’ evaluates to true. For such circumstances the operator overloading function has been designed to remain unexecuted. This is necessary because in case of self assignment no action is necessary. Unnecessary allocation-deallocation should be avoided.
• The function has been designed to return by reference – to prevent chaining operation from
being inefficient, that is, to ensure an efficient execution of statements such as :
String s1, s2, s3; s3= s2 = s1; interpreted as
s3.operator = (s2.operator = (s1) );
suppose s2= s2 = s1; suppose it is returned by value and not by reference. Then value of s2 is created in stack and its address is different from that of s2. When the assignment operator executes for the second time, reference variable ‘ss’ refers to this copy and not to s2 itself.
• When the library programmer wants to overload the ‘assignment’ operator and may not want to share even different copies of the same object.
• A derived class object can be assigned to a base class object but the reverse is not true.
• A A1 ; B B1; A1=B1; //OK B1= A1; //ERROR!
• the data members exclusively in B1 will remain unchanged and may no longer match with rest of the members of ‘B1’. Hence the compiler prevents!
• If the class designer desires, he may provide an assignment operator function to the derived class. public: B& operator = (const A&); //to enable B1=A1
Overloading Various Operators
Overloading assignment operator:
• In most cases, this default assignment operator is sufficient. However, there are cases where this default behaviour causes problems if the copy constructor is not defined.
Example:
class string
{ char *cstr;
int len;
public:
string () { len = 0; p=0; } //create null string
string & operator = (string &);
};
String & string ::operator +(string & ss)
{
if (this!=&ss)
Overloading Various OperatorsOverloading relational operators
• Relational operators are binary operators.
1. The syntax for overloading them through member functions is given below:
<return type> operator <relational symbol> (<parameter list>);
• An object that will store the value of the right-hand side operand of the relational operator will appear in the list of formal arguments. The left-hand side operand will be passed implicitly to the function since the operator-overloading function will be called with respect to it. The expression:
obj1 <relational symbol> obj2
will be interpreted as:
obj1.operator <relational symbol> (obj2)
2. The syntax for overloading relational operator using friend function is as follows:
friend <return type> operator <relational symbol> (<parameter list>);
• Objects that store the values of both the left-hand side and the right-hand side operands of the relational operator will appear in the list of formal arguments.
Overloading Various OperatorsOverloading relational operators
• Suppose d1,d2 are the objects of the class distance then the following code must be compiled successfully :
if (d1>d2)
cout<<“greater than “
else
cout<<“less than”;
• For this, we must overload the ‘>’ operator for the distance class .
• The expression:
d1>d2
Will be interpreted as
d1.operator > (d2)
Overloading Various Operators
Overloading relational operators
• How to overload the greater than relational operator for the class Distance is shown in Listing.
Example:
enum bool {false, true};
class distance
{ public:
// rest of the distance
bool operator > (distance); //member function
};
bool distance:: operator > ( distance d)
{ if (feet*12+inch> d.feet*12+d.inch)
return true;
else return false;
}
Overloading Various Operators
Overloading relational operators
• The code mentioned in the previous example works fine if the right hand side operand is an object. However, if it is a float type value, for example, the expression .
d1>6.5; //will not compile
• The compiler will interpret this expression as:
d1.operator > (6.5);
• In order make the above expression to work, a suitable constructor which converts float to distance is must be defined under distance class.
• However, if the left-hand side operand is of float type, for example:
9.5>d1; //will not work for member function
• To solve this problem, the member function given in previous example is replaced with a friend function.
• Therefore , upon using the friend function, all the 3 conditions can be tackled.
• If both the definitions, i.e., member as well as friend function is present in a class definition for overloading relational operator, the compiler will be confused . This will lead to an ambiguity error.
Overloading Stream-Insertion and Stream-Extraction Operators
• Overloaded << and >> operators– Overloaded to perform input/output for user-defined types– To be consistent with the IO library , the operator should take
ostream& or istream& as its first parameter and a reference to a const object of the class type as its second parameter
– Left operand of types ostream & and istream &– Must be a non-member function because left operand is not an object
of the classFor example:
cin>>A1; // A1 is an object of class AIs interpreted as:
operator >>(cin,A1);
– Must be a friend function to access private data members
Overloading Stream-Insertion and Stream-Extraction Operators
• The syntax for overloading the insertion operator is shown:
class A
{
public: friend ostream & operator << (ostream &, A&);
// rest of class A
};• The syntax for overloading the extraction operator is shown:
class A
{
public: friend istream & operator >> (istream &, A&);
// rest of class A
};
Overloading Stream-Insertion and Stream-Extraction Operators
• The objects of the classes istream and ostream are passed and returned by reference in the preceding functions.
• The copy constructor and the assignment operator have been declared as protected members in both the classes istream and ostream. This prevents two objects from undesirably sharing even different copies of the same stream.
• For example following statements will throw compile time errors :
ostream out=cout; //ERROR
istream in = cin; //ERROR
• There is a compulsion to return by reference. If the object is returned by value, then a separate object is created in the stack. A call to the copy constructor is dispatched with respect to it and the object returned by the operator-overloading function is passed as a parameter. However, the copy constructor is a protected member. Therefore, the object must be returned by reference and not by value.
Example Program:Overloaded << and >> operators class PhoneNumber {private:char areaCode[ 4 ]; // 3-digit area code and nullchar exchange[ 4 ]; // 3-digit exchange and nullchar line[ 5 ]; // 4-digit line and null
public:friend ostream &operator<<( ostream&, const PhoneNumber & ); friend istream &operator>>( istream&, PhoneNumber & );};
ostream &operator<<( ostream &output, const PhoneNumber &num ){output << "(" << num.areaCode << ") "<< num.exchange << "-" << num.line;return output; // enables cout << a << b << c;}
Overloaded << and >> operators
istream &operator>>( istream &input, PhoneNumber &num ){
input >> num.areaCode; // input area codeinput >>num.exchange; // input exchangeinput>>num.line; // input linereturn input; // enables cin >> a >> b >> c;
}
void main(){
PhoneNumber phone; // create object phonecout << "Enter phone number \n";cin >> phone;cout << "The phone number entered was: " << phone << endl;}
Program Output
Enter phone number :800 555 1212The phone number entered was: (800) 555-1212
Overloading Various Operators
Overloading subscript operator:
• The subscript operator [] is normally used to access array elements. This operator can be overloaded to enhance the existing functionality of C++ arrays.
• Thus, the syntax for overloading the subscript operator is as shown :
class <class name>
{
public:
<return type> operator [] (<parameter list>);
};
Example: Overloading [] operator
const int SIZE=10;
class array
{
private:
int arr[SIZE];
public:
array()
{
int i;
for(i = 0; i < SIZE; i++)
{ arr[i] = i; }
}
int &operator[](int i)
{
if( i > SIZE )
{ cout << "Index out of bounds" <<endl;
// return first element.
return arr[0]; }
return arr[i];
}
int main()
{
array A;
cout << "Value of A[2] : " << A[2] <<endl;
cout << "Value of A[5] : " << A[5]<<endl;
cout << "Value of A[12] : " << A[12]<<endl;
return 0;
}
When the above code is compiled and executed, it produces the following result:
Value of A[2] : 2
Value of A[5] : 5
Index out of bounds
Value of A[12] : 0
Overloading Various Operators
Overloading subscript operator:
• The subscript operator [] is normally used to access array elements. This operator can be overloaded to enhance the existing functionality of C++ arrays.
• Thus, the syntax for overloading the subscript operator is as shown :
class <class name>
{
public:
<return type> operator [] (<parameter list>);
};
<return_type> <class_name> operator[] <param_list>
{// statements }
class String
{ public:
char& operator[] (const int) ;// for non-const objects
char& operator[] (const int) const;//for const objects };
char& String ::operator[] (const int p)
{ return cStr[p] ;}
char& String ::operator[] (const int p) const
{ return cStr[p] ;}• The function returns the corresponding element by
reference. Because the subscript operator might be used on LHS of assignment operator also.
s[1] = ‘x’ ; // assign ‘x’ to second char of s
• If you use the first function for the constant object like const String s(“abcd”); cout <<s[1];//ERROR . Hence we introduced a second function.
• Also s[1] = ‘x’; //compiles• To prevent this we should make the return type
reference.• The formal argument of the function that overloads the
‘subscript’ operator can be of any type such as int, char, float, double.
Overloading Various Operators
Overloading assignment operator:
• The subscript operator [] is normally used to access array elements. This operator can be overloaded to enhance the existing functionality of C++ arrays.
• Thus, the syntax for overloading the subscript operator is as shown :
class <class name>
{
public:
<return type> operator [] (<parameter list>);
};
Overloading ‘new’ and ‘delete’ operators
•The ‘new’ and ‘delete’ operators are usually sufficient for most programs
but there might be cases wherein you want to overload them as well.
Some important points are:
• You can overload these operators globally or you can overload them
for specific classes. If you overload using member function for a class then
we say that you have overloaded only for that specific class.
•If overloading is done outside a class (i.e. it is not a member function of a
class), the overloaded ‘new’ and ‘delete’ will be called anytime you make
use of these operators (within classes or outside classes). This is global
overloading.
• Be careful if you overload ‘new’ and ‘delete’ globally (because there
might be other parts of your program which rely on the normal operation of
these operators).
•Usually programmers do not overload them globally.
Overloading ‘new’ and ‘delete’ operators
The default action of the dynamic memory management operators (new
and delete) are unsuitable for the class being designed.
•By default, the new operator allocates the amount of memory
requested, and also stores the amount of memory allocated in the
memory itself.
• In memory critical applications, overloading the new operator can
prevent a huge wastage of memory.
• By default, the new operator simply allocates memory for the object
whose type is passed as an operand to it. To prevent the class from
having more than one object, subsequent calls to the new operator
should not create more objects, but merely return the address of the
object that was created in response to the first call to the new
operator.
•Overloading provides better readability of code.
The syntax for overloading ‘new’ and ‘delete’ are:
void* operator new (size_t n){ //allocate memory and return the address (a void pointer)}void operator delete (void *p){ //free the memory space pointer to by the pointer ‘p’}You will notice that the parameter for ‘new’ is:size_t n;•size_t is a numerical data type defined by the system to specify memory size in bytes. Type size_t is an implementation dependent unsigned integral type defined in <stddef.h>. For instance if you use ‘new’ to create a character, the value of n (i.e. the argument passed to ‘n’) will be 1. If you pass a ‘double’ quantity then the value of ‘n’ will be 8 (you needn’t worry about this).•The ‘new’ operator should return a void pointer while the ‘delete’ operator will pass a void pointer as argument.
Overloading ‘new’ and ‘delete’ operators
New and Delete operators• The behavior of these operators can be altered for operands of
specific class types. If the operators are overloaded for specific class, then the functions that overload are called when the class type is passed as a parameter to these operators. Otherwise the global new and delete operators are called.
• If ‘new’ is overloaded in class ‘X’ and not in ‘Y’ , then • X* Xptr = new X; //overloaded new where as • Y * Yptr = new Y; // global new
void * operator new(size_t); //prototype
void *<class_name> :: operator new(size_t size)
{ //definition of function }
void * operator new[](size_t); //prototype
void *<class_name> :: operator new[](size_t size)
{ //definition of function – for array }
static void operator delete(void*, size_t); //prototype for delete
void <class_name> :: operator delete(void * p, size_t size)
{ //definition of function }
static void operator delete[](void*, size_t); //prototype for delete
void <class_name> :: operator delete[](void * p, { //definition of function } size_t size)
• The new and delete functions must be static. Their prototypes need not be prefixed with ‘static’. The compiler treats these as ‘static’.
• The return type of the ‘new’ must be ‘void *’. The returned value is the address of the memory block. It should take at least on formal argument ‘size_t’.
class A
{ int x; public :
void * operator new(size_t); } ;
#include”A.h”
void * A :: operator new(size_t size)
{ cout<< sizeof(A)<<endl;
cout<<size <<endl;
void *p = ::operator new(size);
return p; }
#include “A.h”
void main()
{ A * Aptr = new A; } // output 4 4
• Class designer will insert necessary code to overload ‘new’
operator and to allocate required amount of memory and then
to return the address of the captured memory.
• The type ‘size_t’(essentially an unsigned integer) is a defined
type capable of containing the largest piece of memory that can
be allocated. ‘size’ will contain the number of bytes needed to
hold the object being allocated.
• The return type in ‘delete’ must be void, as it does not return anything. Its first argument should be of type void*. The address of the memory being deleted is passed to it. The second argument is ‘size_t’.
class A
{ int x; public : void operator delete (void * const, const size_t); } ;
#include”A.h”
void A :: operator delete (void * const p, const size_t size)
{ cout<< p<< endl ;
cout<< sizeof(A)<<endl;
cout<<size <<endl;
::operator delete(size); }
#include “A.h”
void main()
{ A * Aptr = new A; cout<<Aptr<<endl; delete Aptr;}
output :OxCCCCCC OxCCCCCC 4 4
• ‘new’ and ‘delete’ functions are static by default. The compiler
places a call to constructor (destructor) immediately
after(before) the call to ‘new’ (‘delete’) operator.
A * Aptr = new A ; is translated as
A* Aptr =A :: operator new(sizeof(A));
A :: A(Aptr) ;// nameless object created and constructor called
for that object.
• The statement delete Aptr; is translated as
A:: ~A(Aptr); //destructor called for nameless object
A:: operator delete (Aptr, sizeof (A) ); //nameless object
destroyed
The program below overloads the ‘new’ and ‘delete’ operators globally. void* operator new(size_t n) {cout<<endl<<"overloaded new"; void *ptr; ptr = ::operator new(n); return ptr;} void operator delete(void *p) {cout<<endl<<"overloaded delete"; ::operator delete p ;} int main( ) {int *p = new int; *p=20; cout<<"\n The value is : "<<*p; //value of 20 delete p; cout<<"\n The value after deleting is : "<<*p; //some invalid value return 0;}The output will be: overloaded new The value is : 20 overloaded delete The value after deleting is : 4325404
The C++ standard library comes with a set of predefined new and delete operators. The most important ones are these:
void* operator new(size_t) ; void operator delete(void*); void* operator new[](size_t) ; void operator delete[](void*);
•The first two allocate/deallocate memory for an object, the latter two for an
array of objects. If you overload operator new, you should always also
overload the matching operator delete, even if you never intend to call it.
The reason is that, if a constructor throws during the evaluation of a new
expression, the run-time system will return the memory to the operator delete
matching the operator new that was called to allocate the memory to create
the object in.
If you do not provide a matching operator delete, the default one is called,
which is almost always wrong. If you overload new and delete, you should
consider overloading the array variants, too.
• What are smart pointers? Actually, smart pointers are objects which behave like pointers but do
more than a pointer. These objects are flexible as pointers and have the advantage of being
an object (like constructor and destructors called automatically). A smart pointer is designed to handle the problems caused by using
normal pointers (hence called smart).
• What are the common problems we face in C++ programs while using pointers?
In case of ordinary pointers, there is no guarantee that the pointer being used is pointing at valid block of memory.
Memory management. Consider following code:
Overloading pointer-to-member(->) operator (Smart pointer)
char* pName = new char[1024]; … SetName(pName); … … if(null != pName) { delete[] pName; }
How many times have we found a bug which was caused because we
forgot to delete pName.
It would be great if someone could take care of releasing the memory
when the pointer is not useful .
What if the pointer itself takes care of that? Yes, that’s exactly what
smart pointers are intended to do. Let us write a smart pointer and see
how we can handle a pointer better.
Overloading pointer-to-member(->) operator (Smart pointer)
class Person{ int age; char* pName;
public: Person(): pName(0),age(0) { } Person(char* pName, int age): pName(pName), age(age) { } ~Person() { }
void Display() { printf("Name = %s Age = %d \n", pName, age); }};
Overloading pointer-to-member(->) operator (Smart pointer)
void main(){ Person* pPerson = new Person("Scott", 25); pPerson->Display(); delete pPerson;}
• Now we shall write the client code to use Person.
• Now look at this code, every time we create a pointer, we need to take care of deleting it. This is exactly what we want to avoid. We need some automatic mechanism which deletes the pointer. One thing which strikes to us is a destructor. But pointers do not have destructors, so what? Our smart pointer can have one. So we will create a class called SP which can hold a pointer to the Person class and will delete the pointer when its destructor is called. Hence our client code will change to something like this:
void main(){ SP p(new Person("Scott", 25)); p->Display(); // Dont need to delete Person pointer..}
Note the following things:
We have created an object of class SP which holds our Person class
pointer. Since the destructor of the SP class will be called when this
object goes out of scope, it will delete the Person class pointer (as its
main responsibility); hence we don’t have the problem of deleting the
pointer.
One more thing of major importance is that we should be able to call the
Display method using the SP class object the way we used to call using
the Person class pointer, i.e., the class should behave exactly like a
pointer.
Smart pointer implementation
class SP{private: Person* pData; // pointer to person classpublic: SP(Person* pValue) : pData(pValue) { } ~SP() { // pointer no longer requried delete pData; }
Person& operator* () { return *pData; }
Person* operator-> () { return pData; }};
Note the following things:
The main responsibility of smart pointer class is to hold a pointer to the
Person class and then delete it when its destructor is called. It should
also support the interface of the pointer.
Type Conversion – Class to Basic
The constructor handles the task of converting basic types to class types
very well. But you cannot use constructors for converting class types to
basic data types. Instead, you can define an overloaded casting
operator(type conversion operator) that can be used to convert a class
data type into a basic data type. The general form of an overloaded
casting operator function is shown below. This function is also known as
a conversion function.
The casting operator function should satisfy the following conditions:
•It must be a class member.
•It must not specify a return type.
•It must not have any arguments. The object that invokes this function
has its members directly accessible since this is a member function.
Type Conversion – Class to Basic
class DistConv{private: int kilometers; double meters; nbsp; const static double kilometersPerMile;public: // This function converts a built-in type (i.e. miles) to the // user-defined type (i.e. DistConv) DistConv(double mile) // Constructor with one // argument { double km = kilometersPerMile * mile ; // converts miles to //kilometers kilometers = int(km); // converts float km to //int and assigns to kilometer meters = (km - kilometers) * 1000 ; // converts to meters } DistConv(int k, float m) // constructor with two // arguments { kilometers = k ; meters = m ; } // ********Conversion Function************ operator double() // converts user-defined type i.e. // DistConv to a basic-type { // (double) i.e. meters double K = meters/1000 ; // Converts the meters to &nsp; // kilometers K += double(kilometers) ; // Adds the kilometers return K / kilometersPerMile ; // Converts to miles } void display(void) { cout << kilometers << " kilometers and " << meters << " meters" ; }}; // End of the Class Definitionconst double DistConv::kilometersPerMile = 1.609344;int main(void){ DistConv d1 = 5.0 ; // Uses the constructor with one argument DistConv d2( 2, 25.5 ); // Uses the constructor with two arguments double ml = double(d2) ; // This form uses the conversion function // and converts DistConv to miles cout << "2.255 kilometers = " << ml << " milesn" ; ml = d1 ; // This form also uses conversion function // and converts DistConv to miles d1.display(); cout << " = " << ml << " milesn" ;}
Output
2.255 kilometers = 1.25859 miles8 kilometers and 46.72 meters = 5 miles
Type Conversion – Class to Class
Now that we have understood how to convert basic data types to class
types and vice-versa, it is time to learn how to convert objects of one
class type to another class type.
The conversion between objects of different classes can be done using
either a one-argument constructor or a conversion function. The choice
depends upon whether the conversion routine has to be declared in the
source class or in the destination class. To illustrate, consider a program
that contains two classes: A and B. Also consider the statement:
object_A = object_B;
In the above statement, object_B of type B is converted to type A and
assigned to object_A. Therefore, object_B is the source and object_A is
the destination.
Type Conversion – Class to Class
For the conversion, the user can use either a conversion function or a
constructor with one argument, depending on whether it is specified in the
source class or the destination class.
In other words, if class B handles the conversion, it will hold a conversion
function. On the other hand, if class A carries out the conversion, it will do
that through a constructor that takes an argument of type class B.
Note that only one of the two conversion routines should be specified; the
compiler will yield an error if both are specified since it will not be able to
figure out which routine to call.
Type Conversion – Class to Class
class Kilometers{private: double kilometers;public: Kilometers(double kilometers): kilometers(kilometers) {} void display() { cout << kilometers << " kilometeres"; } double getValue() { return kilometers; }};
Type Conversion – Class to Class
class Miles{private: double miles;public: Miles(double miles) : miles(miles) {} void display() { cout << miles << " miles"; } operator Kilometers() { return Kilometers(miles*1.609344); } Miles(Kilometers kilometers) { miles = kilometers.getValue()/1.609344; }};
Type Conversion – Class to Class
int main(void){ /* * Converting using the conversion function */ Miles m1 = 100; Kilometers k1 = m1; m1.display(); cout << " = "; k1.display(); cout << endl; /* * Converting using the constructor */ Kilometers k2 = 100; Miles m2 = k2; // same as: Miles m2 = Miles(k2); k2.display(); cout << " = "; m2.display(); cout << endl;}
Output100 miles = 160.934 kilometeres100 kilometeres = 62.1371 miles