CS115 Lab: C++ Overloading


Highlights of this lab:

In this lab you will learn how to overload functions and operators. You will be able to recognize and write overloaded functions and you will be able to define your own operators. Specifically, the following topics will be covered:

Lab Exercise:

Click the little computer above for a detailed description.
NOTE: Your lab instructor will tell you what will be marked for this lab.

1. What is Overloading

Overloading is a component of polymorphism in C++. It lets a programmer use one function name for multiple functions. The compiler chooses what implementation to use based on the arguments provided in the call. Functions can be overloaded to handle different types of data and different numbers of parameters. You can also overload certain operators, including +, /, <<, and even [ ]. This can add power and ease of use to user defined data types.

2. Function Overloading

Briefly, function overloading means two or more functions share the same name but their parameters are different.  In this situation, the functions that share the same name are said to be overloaded and the process is called function overloading .  The number and types of a function's parameters are called the function's signature. Together a function's name and its signature uniquely identify it.

    2.1 Parameter Type Overloading

    This refers to the situation where the overloaded functions have the same number of parameters, but the types are different.

    // File name: /pub/class/170/ftp/cpp/Inheritance/OverloadSingle.cpp
    // Purpose:   Demonstrate single parameter overloading
    
    //Overloaded functions with same number of parameters, but 
    //different types.
    void timesTwo(int &num);
    void timesTwo(double &num);
    
    int main(void)
    {
       int    A = 1;
       double B = 1.1;
    
       timesTwo(A); // Changes A to 2
       timesTwo(B); // Changes B to 2.2
    }
    
    // handle int type
    void timesTwo(int &num)
    {
       num = num * 2;
    }
    
    // handle double type
    void timesTwo(double &num)
    {
       num = num * 2.0;
    }
    
    

    In the above example, there are two versions of the timesTwo function; one deals with integers and the other deals with double.  When we call the function with different data types, the compiler is able to decide which version of the timesTwo() function to call by comparing the type of the argument in the function call with the type of the parameter of the available function versions. If the call argument is of type int then the function version with an int parameter is called. Similarly if the call argument is of type double then the function version with a double parameter is called.

    2.2 Parameter Number Overloading

    This refers to the situation where the types of parameters of the overloaded functions may or may not be the same and the number of parameters is different.

    Let's look at the following example:

    // File name: /pub/class/170/ftp/cpp/Inheritance/OverloadMultiple.cpp
    // Purpose:   Demonstrate multiple parameter overloading 
    
    #include <iostream>
    using namespace std;
    
    void add(int i, int j);
    void add(int i, double j);
    void add(int i, int j, int k);
    
    int main(void)
    {
       int    A = 1, B = 2, C = 3;
       double D = 1.1;
    
       add(A, B); //  1 + 2 => add prints 3
       add(A, D); //  1 + 1.1 => add prints 2.1
       add(A, B, C); // 1 + 2 + 3 => add prints 6
    }
    
    void add(int i, int j)
    {
    	cout << "Result: " << i + j << endl;
    }
    void add(int i, double j)
    {
    	cout << "Result: " << i + j << endl;
    }
    void add(int i, int j, int k)
    {
    	cout << "Result: " << i + j + k << endl;
    }
    

    The above program gives the following results:

    mercury[18]% CC -o OverloadMultiple OverloadMultiple.cpp
    mercury[19]% OverloadMultiple
    Result: 3
    Result: 2.1
    Result: 6
    mercury[20]% 

    2.3 Overloading Member Functions

    Nothing special is required to overload member functions. You simply need to declare and define member functions with the same name but different signatures. For instance, in the Matrix class below, the function called addMatrix has two definitions with different behaviours:
    //In matrix.h
    class Matrix
    {
      private:
       int doubleArray[MAXROWS][MAXCOLS];
       int rows;
       int cols;
    
      public:
        .
        .
        .
       //add an array to current data in "doubleArray"
       void addMatrix(int [][MAXCOLS]); 
       //add two arrays together and store the results in "doubleArray"
       void addMatrix(int [][MAXCOLS], int [][MAXCOLS]); 
        .
        .
        .
    //In matrix.cpp
    void Matrix::addMatrix(int array1[][MAXCOLS])
    { 
    ...
    }
    void Matrix::addMatrix(int array1[][MAXCOLS], int array2[][MAXCOLS]) 
    {
    ...
    }
    

    3. Operator Overloading

    You can overload operators in the same way that you overload functions. There are only three things you need to know:

    1. Can this operator be overloaded?
    2. How many parameters does the operator require (1 - unary, 2 - binary)?
    3. Where do I overload the operator?

      3.1 What operators can be overloaded

      Once you know whether you can overload an operator you name the overloaded function by adding operator infront of the symbol. For example to overload + you would use the function name operator+.

      The following table (taken from C++ Primer, Lippman and Lajoie) is the predefined set of C++ operators that may be overloaded:

      Table of Overloadable Operators
      + - * / % ^ & | ~
      ! , = < > <= >= ++ --
      << >> == != && || += -= /=
      %= ^= &= |= *= <<= >>= [] ()
      -> ->* new new[] delete delete[]      

      You cannot overload ::, .*, . or ?:.

      NOTE: the point of operator overloading is to be intuitive. You do not want to assign a confusing operator name. For instance, you do not want to overload the "+" when the function actually performs subtraction.

      3.2 Number of Parameters

      The number of parameters an operator takes depends on the operator and where you overload it. We will start with operator overloading outside of a class.

        3.2.1 Non-member Operator Overloading

        Most operators you can overload are either binary or unary. Binary operators have a left-hand side and a right-hand side usually written LHS and RHS. For non-member functions each side is a parameter. The LHS is the first parameter, the RHS is the second.

        return_type operatorbinary(type LHS, type RHS)
        
        Unary operators have only one parameter.
        return_type operatorunary(type param)
        

        Some operators can be both binary and unary. Which overloaded definition is used is determined by context and function signature. Suppose you wanted to overload subtraction and negation. These expressions:

        matrix1 - matrix2
        -matrix1
        
        are transformed into function calls like these by the compiler:
        operator-(matrix1,matrix2)//subtraction
        operator-(matrix1)        //negation
        

        The following example shows how you would overload operators += and ++ on a matrix using the addMatrix member function:
        //For Reference, this is the definiton of addMatrix in matrix.cpp
        void Matrix::addMatrix(int otherArray[][MAXCOLS])
        {
           for (int i=0; i< rows; i++)
           {
              for(int j=0; j< cols; j++)
              {
                 doubleArray[i][j] += otherArray[i][j];
              }
           }
        }
        
        // Code could be in main.cpp 
        #include <iostream>
        #include "matrix.h"
        
        using namespace std;
        
        
        //operator +=
        Matrix operator+=(Matrix &mat,  int arr[MAXROWS][MAXCOLS]);
        
        
        //Prefix operator ++
        Matrix operator++(Matrix &mat);
        
        
        int main()
        {
            int Num1[MAXROWS][MAXCOLS] = 
               {
        	  {1,2,3},
        	  {4,5,6}
               };
        
            Matrix matrix1;
        
            cout << "matrix1 initialized:" << endl;
            matrix1.printMatrix();
            
            cout << "Num1 added to matrix1 with += " << endl;
            matrix1 += Num1;
            matrix1.printMatrix();
        
            cout << "1 added to all elements of matrix1 with ++ " << endl;
            ++matrix1;
            matrix1.printMatrix();
        
            cout << "\n" << endl;
            return 0;                
        }
        
        Matrix operator+=(Matrix &mat,  int arr[MAXROWS][MAXCOLS])
        {
           mat.addMatrix(arr);
           return mat;
        }
        
        Matrix operator++(Matrix &mat)
        {
           //Build an array of 1's with correct dimensions
           int arr[MAXROWS][MAXCOLS];
           for(int i = 0; i < MAXROWS; i++)
              for(int j = 0; j < MAXCOLS; j++)
        	 arr[i][j] = 1;
        
           //Add it to the matrix
           mat.addMatrix(arr);
           return mat;
        }
        

        Notice that non-member functions do not have access to private data. That's why operator++ uses addMatrix and an array initialized with 1's to do its work. Normally you would need to use observer and transformer member functions on an object. C++ has the keyword friend you can use as a workaround to this restriction, you may wish to look it up.

        Also notice that operator++ is a prefix operator. To change it to a postfix operator add an unnamed second parameter of type int.

        On Returning Classes

        A class is a legal return type for a function. . Arrays are not a legal return type — that's one reason we built our matrix class.

        3.2.2 Member Operator Overloading

        When you overload an operator as a member function the LHS of binary operations and the only parameter of unary operations is implied. That parameter is assumed to be the calling object. If subtract and negate were defined as member operators of the matrix class then

        matrix1 - matrix2
        -matrix1
        
        would become
        matrix1.operator-(matrix2)
        matrix1.operator-()
        
        Those are legal member function calls.

        The changes you need to overload the non-member operators += and ++ above as member operators follow.

        In matrix.h add these operator prototypes
        Matrix operator+=(int arr[MAXROWS][MAXCOLS]);
        Matrix operator++(/*No parameters*/);
        
        Now change the function definitions as follows
        Matrix Matrix::operator+=(int arr[MAXROWS][MAXCOLS])
        {
           addMatrix(arr);
           return *this;  //We will talk about this in CS210
        }
        
        Matrix Matrix::operator++(/*No parameters*/)
        {
           //No need for array
           //Add 1 to the all elements of the matrix
           for(int i = 0; i < MAXROWS; i++)
              for(int j = 0; j < MAXCOLS; j++)
        	 doubleArray[i][j] += 1;
           //No need for extra call
           return *this;  //We will talk about this in CS210
        }
        

        Notice that a member function has access to the private data members of parameters that belong to the same class.

      3.3 Where to overload an operator

      Usually it is up to you to decide where to overload an operator. Sometimes you have no choice; you are required by some limitation to define an operator as a class member or as a non-member.

      Forced Non-Member Operator Overloading
      You must use a non-member implementation when the LHS is either not an object (int, double) or you do not have access to its class definition (cin, strings).
      Forced Member Operator Overloading
      Assignment =, subscript [ ], call (), and member access (-> and ->*) operators must be defined as member functions.

      Some programmers prefer to define operators on object/non-object pairs as non-member functions. This way the definition that cannot be a member is defined similarly to the one that can like this:

      #include <math.h>
      
      //...
      
      bool operator<(twoD b, int a)
      {
         if (sqrt(b.x*b.x+b.y*b.y) < a) return true;
         return false;
      }
      bool operator<(int a, twoD b)
      {
         if (sqrt(b.x*b.x+b.y*b.y) > a) return true;
         return false;
      }
      

    4. Lab Exercise — Overloading

    Part 1

    Complete the following worksheets.

    Question 1 — Overloading Functions

    Remember that a function's "signature" refers to the number and type of parameters passed to a function.

    Consider two functions with the same name. How do each of the following differ in signature? (Indicate "same" or "different" in the following tables.)

    Function Prototype Function Signature
    # of param's type of param's
    add(int i, int j); 
    add(int i, double j);
       
    add(int i, int j); 
    add(int i, int j, int k);
       
    add(int i, int j, int k); 
    add(double i, double j, double k);
       
    add(double i, double j, double k); 
    add(int i, double j, int k);
       

    Question 2 — Overloading Operators

    Given m1 and m2 of type Matrix and a1 of type int [][] are the following operators unary or binary? Can they be member or non member functions or both? Indicate all that apply.

    Expression arity Class
    Membership
    unary binary M N-M
    m1+m2
           
    m1=a1
           
    m1++
           
    a1*m1
           
    -m1
           

    Part 2

    Explanation

    For this part you will overload some member and non-member operators for our Matrix class.

    Instructions

    The old version of this exercise, just in case you thought this one was hard.


    This page last modified:
    Thursday, 06-Oct-2011 12:13:32 CST

    CS Dept Home Page
    CS Dept Class Files
    CS115 Lab Files

    Copyright: Department of Computer Science, University of Regina.