Advanced Function and Operator Overloading in C++

The following are the main point to keep in mind while dealing with Function and Operator Overloading in C++ –

  • must differ in the type, number of parameters
  • could not overload a function using a pointer and array as parameters.

i.e

void f(int *p); //error, *p is same as p[]

void f(int p[]);

#include <iostream>

using namespace std;

int myfunc(int i);
float myfunc(float i);


int main()
{
    cout<<"Integer" << myfunc(10);
    cout<<"Float" << myfunc(1.1);
    return 0;
}

int myfunc(int i){
    return i;
}

float myfunc(float i){
    return i;
}

The above program will give error since we left out implementing the double version of overloaded function. Ambiguity occurs when compiler is not able to choose between the overloaded functions.

Output:
main.cpp: In function ‘int main()’:
main.cpp:20:32: error: call of overloaded ‘myfunc(double)’ is ambiguous
     cout<<"Float" << myfunc(1.1);

The above program need to be modified as below to be complete –

#include <iostream>

using namespace std;

int myfunc(int i);
float myfunc(float i);
double myfunc(double i);

int main()
{
    cout<<"Integer" << myfunc(10);
    cout<<"Float" << myfunc(1.1);
    return 0;
}

int myfunc(int i){
    return i;
}

float myfunc(float i){
    return i;
}

double myfunc(double i){
    return i;
}

Output:

Integer::10 

Float::1.1

Overloading Constructor Functions

Overloading constructor functions allows an object to be created by using the most appropriate and natural means for each circumstance. Overloading increases flexibility of the constructor function. Below is a more detailed example of constructor overloading in C++.

#include <iostream>

using namespace std;

class date{
    int day, month, year;
    public:
        date (char *d);
        date(int d, int m, int y);
    void show_date();
};

date :: date (char *d){
    scanf(d, "%d %*c %d %*c %d %*c, &day, &month, &year");
}
date :: date(int d, int m, int y){
    day = d; month = m; year = y;
}
void show_date(){
    cout << "Date: " << day << ":" << month << ":" << year;
}

int main()
{
    char S[80];
    cout << "Enter Date>";
    cin >> S;
    date ob1 (S);
    ob1.show_date();
    return 0;
}

While using pointers to functions (overloaded), we must make note tht the pointer to function declaration should match the function parameters.

eg:

int (*fp)(int a);

fp = myfunc;

fp(5) // will call only int myfunc(int a) and not int myfunc(int a, int b)

Creating a member operator function – Operator Overloading

Operator overloading means that we could assign a particular operation for any desired operator (+, -, =, +=, &) by defined an operator overloaded function. It is common for an overloaded operator function to return an object of the class it operates upon.

Syntax Definition: <ret-type><class_name> :: operator<overloaded operator> (<org_list>))

A detailed example of usage is depicted using below program –

#include <iostream>

using namespace std;

class loc{
    int x,y;
    public:
     loc();
     loc(int i, int j){
         x = i;y=j;
     }
     void show(){
         cout << x << "," << y;
         cout << "\n";
     }
     /*declaration of overloaded operator function*/
     loc operator+ (loc op2);
     loc operator- (loc op2);
     loc operator++ ();
     loc operator= (loc op2);
};

loc loc::operator+ (loc op2){
    loc temp;
    temp.x = op2.x + x;
    temp.y = op2.y + y;
    return temp;
}

loc loc::operator- (loc op2){
    loc temp;
    temp.x = x - op2.x;
    temp.y = y - op2.y;
    return temp;
}

loc loc::operator++ (){
    x++;y++;
    return *this;
}

loc loc::operator= (loc op2){
    loc temp;
    x = op2.x;
    y = op2.y;
    return *this;
}

int main()
{
   loc ob1(10,20), ob2(5,30), ob3(90,90);
   ob1.show();
   ob2.show();
   ++ob1;
   ob1.show();
   ob2 = ++ob1;
   ob1.show();
   ob2.show();
   ob1 = ob2 = ob3;
   ob1.show();
   ob2.show();
    return 0;
}

Creating prefix and postfix forms of the increment & decrement operator

//declarations

type operator++() // prefix operator

type operator++(int x) // postfix operator

type operator++() // prefix operator

type operator–(int x) // postfix operator

Overloading short hand operators +=, -=

loc loc::operator+= (loc op2){
    
    x = x + op2.x;
    y = y + op2.y;
    return *this;
}

Operators like ., ::, .*, ? cannot be overloaded

Operator overloading using friend functions

We could overload ++, –, +, – operators using friend overloaded operator function.

\\declaration
friend loc operator+(loc op1, loc op2); // added
//definition
loc operator+(loc op1, loc op2){ // removed loc::, two parameters required
       body;  //all other syntax are similar to the previous program
}

You cannot overload =, (), {} or -> operators.

Overloading ++, —

While overloading ++ and — operators using friend functions, we introduce reference parameters since friend functions could not return *this. So the declaration and definition of such a friend overloaded operator function is as shown below –

// declaration
friend loc operator++ (loc &op);
friend loc operator-- (loc &op);
//definition
loc operator++(loc &op){
     op.x++;
     op.y++;
}
//postfix version of ++
friend loc operator++(loc &op, int x)

Flexibility of friend operator function

Friend operator functions are extremely useful in cases when a like 100+ob arises and the overloaded function for + operator is a member function, then the compiler will not execute the statement since not operation between an int and an object of ob’s type is defined however ob + 100 is valid. Now, in application where the object type (ob) could not also be placed at the left, as above case, the fried operations could be used to do the job.

Now, to allow the int + object and obj + int operation to be possible, we may declare and define friend operator functions as follows –

// declaration
friend loc operator+ (loc op1, int op2);
friend loc operator+ (int op2, loc op1);
//definition
loc operator+(loc op1, int op2){
     loc temp;
     temp.x = op1.x + op2;
     temp.y = op1.y + op2; 
     return temp;
}

Overloading new and delete

‘new’ and ‘delete’ are overloaded for some special allocation method.

/*shows overloading of new and delete used relative to a class and for operation on an array of objects*/
# include <iostream.h>
# include <stdlib.h>

class loc{
      int x, int y;
      public:
         loc(){int x = y = 0;}
         loc(int x1, int y1){x = x1; y = y1;}
         void show(){
           cout << x << " ";
           cout << y << "\n";
         }
}

/*overload declarations of new, delete */
void * operator new(size_t size);
void operator delete(void *p);

void * operator new[](size_t size); // overloaded for arrays
void operator delete[](void *p); // overloaded for arrays

void * loc :: operator new(size_t size){
       return malloc (size);
}

void * loc :: operator delete(void *p){
       free(p);
}

void * loc :: operator new[](size_t size){
       return malloc (size);
}

void * loc :: operator delet[]e(void *p){
       free(p);
}

main(){
   loc *p1, *p2;
   int i;
   //allocate object
   p1 = new loc(10,20);
   if(!p1){
      cout << "Allocation error\n";
      exit(1);
   }
   p1 = new loc[10];
   if(!p2){
      cout << "Allocation error\n";
      exit(1);
   }
   p1->show();
   for(i = 0; i<10; i++){
      p2[i].show();
      delete p1;
      delete []p2;
      return 0;
   }
}

Overloading special operators [], (), ->

You cannot use such overloaded operator functions as friend functions and static member function.

C++ considers [] as a binary operator and thus is declared and defined as –

//definition
type class_name::operator[](int i){ }
type &class_name::operator[](int i){ }//returns as reference to the array element indexed by i 
//declaration
type operation[](int i);
type &operation[](int i);

One advantage of using [] overloaded function is that it is a safe array indexing in C++. As the following program shows how [] overloaded function could be used for setting the array size and handle the array size out-of-range conditions. See below program to see an example –

//overload2.cpp
#include <iostream.h>
class atype{
      int a[3];
    public:
       atype(int i, int j, int k){
           a[0] = i;
           a[1] = j;
           a[2] = k;
       }
    int & operator[](int i);
};

/*provide range checking for arrays*/
int & atype::operator[](int i){
    if(i < 0 || i > 2){
       cout << "Boundary Error\n";
       exit(1);
    }
    return a[i];
}

main(){
   atype ob1(1,2,3);
   cout << ob[1];//displays 2
   ob[1] = 25;
   cout << ob[1];//display 25
   ob[3] = 44; // run time error since array size exceeds 2
   return 0;
}

Overloading () operator

Overloading () operator has similar meaning as we pass arguments to function parameters. The specified arguments are simply copied to these parameters. As always, the object that generates the call (0 in this example) is pointed to by the ‘this’ pointer. eg: double operator [](int a, float f, char *s)

//Overload3.cpp

class loc{
    int x,y;
    public:
     loc();
     loc(int x1, int y1){
         x = x1;y=y1;
     }
     void show(){
         cout << x << "," << y;
         cout << "\n";
     }
     /*declaration of overloaded operator function*/
     loc operator+ (loc op2);
     loc operator() (int i,int j);     
};

loc loc::operator() (int i, int j){
    x = i; y=j;

    return *this;
}

loc loc::operator+ (loc op2){
    loc temp;
    temp.x = op2.x + x;
    temp.y = op2.y + y;
    return temp;
}

int main()
{
   loc ob1(10,20), ob2(1,1);//constructor initialization
   ob1.show(); //10,20
   ob1.(7,8); /*uses the overloaded function*/   
   ob1.show(); //7,8   
   ob1 = ob2 + ob1(10,10);//overloaded function initialized x & y */
   ob1.show();//11, 11   
}
              

Overloading -> Operator

The operator ->() function returns a pointer to an object of the class that the operator ->() operates upon. Thus when -> is overloaded, the statements ob.i and ob->i may be made similar as the following program illustrates.

#include<iostream.h>
class myclass{
    public:
       int i;
    myclass *operator->(){return this;}
};

main(){
   myclass ob;
   ob->i = 10; //same as ob.i
   return 0;
}

Overloading comma operator

loc loc::operator,(loc op2){
   loc temp;
   temp.x = op2.x;
   temp.y = op2.y;
   cout << op2.x << op2.y << "\n";
   return temp;
}

ob1 = (ob1,ob2+ob3, ob3);

First, the value of (ob2 + ob3) is assigned to ob1 displaying ob1. Secondly the value ob3 is assigned to ob1 discarding ob2 + ob3.