Skip to main content

Operator Overloading -II

Let us consider some special cases of operator overloading

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.

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.

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

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

Popular posts from this blog

Ten questions in C/C++

Let us see some questions in C and C++ Write printf statement in C to print - I got 98% in Maths Can you execute a function before main() in C? If yes, how is it done? Can you write a program to find if a number is even without using modulo operator? How do you define a data member which is common to all objects of a class in C++? Can we have a single constructor for a class, but still create objects from the class passing zero/one and two parameters? What problems might occur if a class has no default constructor? Is the following statement correct? fprintf(stdout,"Hello world");   Why do we use the following statement in C++ program? using namespace std;  Can you write a single statement to check if the number is a power of 2?  What does the following statement mean in C/C++?4 if(a) b++;    So we have 10 questions. How many of these can you answer?   Do you need more questions in C and C++?    You can find t...

It is a constant

In good old days, C programmers would use preprocessor directive to define constants. e.g. #define s 10 But we know now that, as compiler never gets to see these, preprocessor statements are error prone. Hence we have const s. A constant - defined with keyword const promises that this entity is never going to change. And if we accidentally modify a const, compiler throws an error. Let us look at an example. #include<iostream> using namespace std; int main () { int a = 10 ; const int b = 12 ; a ++ ; b = 18 ; } When we compile this program, compiler tells us that default.cpp: In function ‘int main()’: default.cpp:8:7: error: assignment of read-only variable ‘b’      b = 18; So it is catching the error that we are trying to modify a const. Whenever a local variable or parameter need not be modified, declare it as a const.  Yes, we can make even parameters as constant. We can make objects constant or even me...

Abstract class

 If we can not create any objects of a class, then it is called an abstract class. A class is made abstract by adding at least one pure virtual function to it. Pure virtual function A function is said to be a pure virtual function , if it has no definition but has only declaration. Pure virtual function is defined with the keyword virtual and followed by return type, function name and "=0". class Shape { public: virtual void printarea() =0 ; }; int main () { Shape obj1; //error } Here printarea() function of Shape class is a pure virtual function as it has no body. To make a function as pure virtual function, you should use =0 at the end of virtual function declaration Abstract class When a class has at least one pure virtual function, it is incomplete and no objects can be created from that class. Such a class is called an abstract class . In the earlier example class Shape is an abstract class, and objects can not be created from t...