Let us consider some special cases of operator
overloading
Overloading of subscript operator:
In line 26, obj2 is assigned to prefix increment of obj. So obj2 will become 10. In line 27, we are using postfix ++ operator.
obj2 is is assigned to obj first and then is incremented. So obj becomes 10 and obj2 will be 11.
Overloading new operator:
Insertion and Extraction operators (<< and >>)
These two operators are used to read and write objects from streams.
You should ensure that these two member functions are friends if you want them to access data directly.
Once >> is overloaded you can read the object directly using cin and once << is overloaded you can display the object directly using cout.
Number obj1(10);
cout<<obj1;/*displays 10*/
cin>>obj1;/*reads num of obj1*/
Conversion Operators:
These operator functions convert objects to other PODs or other type of objects. They are written without a return type. Instead, return type is the name of the operator. For example, to convert object to int, we write an overloaded operator with name int and no return type.
Function call operator:
Output
1000
Overloading of assignment operator:
Here the operator function checks if there is self assignment by using this !=&obj1
Overloading of subscript operator:
Subscript
operator ([]) can be
overloaded to access the dynamically allocated array elements within an
object. Using subscript operator, we can treat these like a POD array
and access ith element of the array using obj[i]
class Arr { int *arr; int size; public: int& operator [](int n) ; int operator[](int n) const; /****code *****/ }; int &Arr::operator [](int index) { return arr[index]; } int Arr::operator [](int index) const { return arr[index]; } int main() { Arr obj(10); for(int i=0;i<10;i++) obj[i] = i*i; for(int i=0;i<10;i++) cout<<arr[i]<<" "; }
Output
0 1 4 9 16 25 36
49 64 81
Why do we have two functions for this operator? Is it allowed? Is it necessary?
const version of
[] operator
function is written so that constant objects can also access array
elements. Non-constant version is helpful in modifying the array
elements. Ideally you can use const function for displaying the elements and non-const version for setting values to these elements.
But we have to make sure
that non-const
version of [] function returns a reference, so that you can assign
values to elements. Remember that a function returning a reference can be assigned a value.
Increment
and Decrement operators
Increment
and decrement operators have prefix and
postfix versions for PODs. a++
and ++a. So
how do we
overload these operators with
two versions for objects?
The
solution is - for
postfix versions, we should use a dummy integer parameter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class Number { int num; public: Number & operator++();/*prefix version*/ Number operator++(int a);/*postfix version*/ /****code***/ }; Number & Number::operator++() { num++; return *this; } Number Number::operator++(int dummy) { Number temp = *this; num++; return temp; } int main() { Number obj(9); Number obj2(1000); obj2 = ++obj;/*calls prefix*/ obj = obj2++;/*calls postfix*/ } |
In line 26, obj2 is assigned to prefix increment of obj. So obj2 will become 10. In line 27, we are using postfix ++ operator.
obj2 is is assigned to obj first and then is incremented. So obj becomes 10 and obj2 will be 11.
Overloading new operator:
new operator takes size_t parameter
(unsigned int) and returns a void
pointer. The easy way of overloading new
operator would be
void *Number::operator new(size_t bytes) { void *ptr = malloc(nbytes); return ptr; } void Number::operator delete(void *ptr) { free(ptr); }
Note
that compiler provided
new operator will
be sufficient in most situations.
You
need to overload new
and delete
operator, only if you want to use your
own block of memory for dynamic allocation.
Insertion and Extraction operators (<< and >>)
These two operators are used to read and write objects from streams.
These
should be overloaded
as non-member functions because their first parameter will not be
the object of the given class.
>> must have istream object as first parameter and << must have ostream object as first parameter.
>> must have istream object as first parameter and << must have ostream object as first parameter.
These two operator
functions must return reference to
iostream objects in order to use chaining in i/o operation. And it is
necessary to use second parameter also as reference parameter in
extraction operator as the function modifies the object.
class Number { int num; public: friend ostream & operator <<(ostream& out, Number & obj); friend istream & operator >>(istream& in, Number & obj); /****code***/ }; ostream & operator <<(ostream& out, Number & obj) { out<<obj.num; return out; } istream & operator >>(istream& in, Number & obj) { in>>obj.num; return in; }
You should ensure that these two member functions are friends if you want them to access data directly.
Once >> is overloaded you can read the object directly using cin and once << is overloaded you can display the object directly using cout.
Number obj1(10);
cout<<obj1;/*displays 10*/
cin>>obj1;/*reads num of obj1*/
Conversion Operators:
These operator functions convert objects to other PODs or other type of objects. They are written without a return type. Instead, return type is the name of the operator. For example, to convert object to int, we write an overloaded operator with name int and no return type.
class Number { int a; public: operator int()/*Converts Number to int*/ { return a; } /****code*****/ }; int main() { Number obj(100); int m = obj; /* uses int conversion operator */ cout<<m; }
Function call operator:
parantheses
- () can be
overloaded to create an object which behaves like a function. Such
objects are called function objects or functors.
class Cube { public: int operator ()(int n) { return n*n*n; } }; int main() { Cube cobj; int m = cobj(10);/*this cubes 10 and returns 1000*/ cout<<m; }
Output
1000
Overloading of assignment operator:
Compiler
provides overloaded
assignment operator function for a class which copies an object to another byte by byte. If your
class does not use dynamic memory at all, then compiler provided assignment
operator would be sufficient.
But if the class uses dynamic memory you have to write your own assignment operator because you need a deep copy.
But if the class uses dynamic memory you have to write your own assignment operator because you need a deep copy.
Assignment
operator function
looks almost like a copy constructor. You allocate memory and you copy
elements. But before
that, you should release memory allocated
earlier for pointer.
There is
one more problem
however. What if you use a statement which ultimately becomes self
assignment because of references as shown below?
Integer obj1(10);
Integer &obj2 = obj1;
obj2= obj1;//self assignment
Integer &obj2 = obj1;
obj2= obj1;//self assignment
In this
case, if you delete
memory, all hell breaks loose.
To avoid
this problem, check for self
assignment, then
delete memory, allocate new memory and copy elements.
class Arr { int *arr; int n; public: A& operator =(const A &obj1); /****code *****/ }; A& A::operator =(const A & obj1) { if(this !=&obj1)/*self assignment?*/ { delete arr; arr = new int[obj1.n]; for(int i=0;i<n;i++) arr[i] = obj1.arr[i]; } return *this; }
Here the operator function checks if there is self assignment by using this !=&obj1
Comments
Post a Comment