Broad Network


Operator Overloading in C++

Function and Operator Overloading in C++ - Part 2

Forward: In this part of the series, I explain operator overloading in C++.

By: Chrysanthus Date Published: 30 Aug 2012

Introduction

This is part 2 of my series, Function and Operator Overloading in C++. In this part of the series, I explain operator overloading in C++.

Meaning and Need for Operator Overloading
An example of an operator is +. This is the addition symbol to add integers and floats. What if you want to concatenate two strings? Would it not be nice to use this same operator? In order to do that, you would have to give the operator a new meaning, which is to join two strings together forming a longer string. Giving an operator a new meaning is called overloading.

Operators and Operands
Some operators have two operands: one on the left and the other on the right. Examples of operators with two operands are: +, -, /, *, and %. The increment operator, ++ and the decrement operator, - - , each takes one operand, either on the left or on the right. The reference & operator takes only one operand, always on the right. There are other operators, and I do not need to mention them in this tutorial.

What is overloaded
You overload an operator that already exists. You do not create a new operator before you overload it

Operator Overload Syntax
Deep down in C++, an operator is a function. So the principles of overloading an operator are similar to those of overloading a function. With functions: if functions of the same name but with different parameter lists are declared (or defined) in the same scope, that is function overloading. Same thing with operators: if operators of the same identifier (e.g. +) are declared (or defined) in the same scope, that is operator overloading. The syntax to overload an operator is:

    returnType operator identifier (parameter list);

This is the prototype syntax. For the definition, you remove the semicolon and continue with the block. If there are two operands for the operator, then there would be two parameters, separated by a comma. In this case, the first parameter is for the left operand and the second parameter is for the right operand. If there is one operand then there would be one parameter. Any name you place in the parameter list, is of your choice.

The + Operator
In the example below, the + operator will be overloaded. Each operator has its condition for overloading. The + operator can take only fundamental objects, enumerators and instantiated objects as parameters. You already know that the + operator can take two ints on either side or two floats on either sided. That is already overloading provided by C++. In the following example, two instantiated objects of the same type will be the operands of the + operator. Of course the + operator will be overloaded (receive a new definition) to receive such operands.

To overload an operator, you need to know its conditions like the ones I have given for the + operator. I will not address those conditions in this series. This part of the series is just to explain to you that operators can be overloaded and how the + operator in particular can be overloaded.

Example
I show you an example where the + operator is overloaded to concatenate two C++ core strings. Here is the code. Read it and try it; the explanation is given below:

#include <iostream>
using namespace std;

class Stringing
    {
        public:
        const char *propStr;
    };

    //array to receive returned total string content
    char totalStrPtr[500];

const char* operator+(Stringing strObj1, Stringing strObj2)
    {
        //copies for the two string pointers
        const char *strA = strObj1.propStr;
        const char *strB = strObj2.propStr;

        //counter for total number of characters in both strings
        int n = 0;
        //determine the total number of characters in both strings
        while (strObj1.propStr)
            {
                if (*strObj1.propStr == ' ')
                    break;
                ++strObj1.propStr;
                ++n;
            }
        while (strObj2.propStr)
            {
                if (*strObj2.propStr == ' ')
                    break;
                ++strObj2.propStr;
                ++n;
            }

        //create array and fill it with all the characters, joining the strings
        char totalStr[n+1];
        int i = 0; //for counting array elements
        while (strA)
            {
                if (*strA == ' ')
                    break;
                totalStr[i] = *strA;
                ++strA;
                ++i;
            }
        while (strB)
            {
                if (*strB == ' ')
                    break;
                totalStr[i] = *strB;
                ++strB;
                ++i;
            }

        //add the last nul character
        totalStr[n] = ' ';

        //put the content for global totalStrPtr pointer
        for (int j=0; j<=n; ++j)
            {
                totalStrPtr[j] = totalStr[j];
            }

        return totalStrPtr;

    }


int main()
    {
        Stringing strObjA;
        Stringing strObjB;
        strObjA.propStr = "I am the first string.";
        strObjB.propStr = " You are the second string.";

        const char *resultStr = strObjA + strObjB;

        cout << resultStr;


        return 0;
    }

Brief Explanation of Code
If you have been reading my C++ tutorials in the order given, then after the brief explanation, the details will be self-explanatory.

Let me begin with what is in the main function. In the main function, the first two statements instantiate two objects of the same type, Stringing. Stringing, is a name I have just given for a class that receives a C++ core string as one of its property (data member) value. The next two statements assign two different strings to the two different objects at their propStr same property (data member). The statement after, calls the + operator, returning the joined string. The + operator receives as operands, two instantiated object of the same class. The statement after, displays the joined string.

I now continue the explanation, beginning from the top of the program: A class called, Stringing, is defined. The class has one property called, propStr. This property is to receive a string, after the class has been instantiated. For two different strings, we need two instantiated objects (classes).

After the class description, you have the declaration of a global (file) scope array that will receive the resulting joint string. Why would you need the global scope array? Inside the definition of the operator, an array is declared that holds the joined strings toward the end of the operator definition. The problem here is, that at the end of the scope of the definition of the operator, this array will die (will be destroyed). So, just before it dies, its content is copied to the array of the global scope. Remember, the array (object) of the global scope will only die at the end of the program. In that way, it can be seen in the main function.

After the global (file) scope array declaration, you have the definition of the overloaded + operator. The definition is similar to that of a function. It receives as arguments, two instantiated objects. Each of these objects comes with its property having a string. The strings in the two objects are different. The first code segment in the definition copies the pointers of the two strings to local scope declared pointers. At that level, there are two strings and two pointers in each statement pointing at the same string (e.g. strA and strObj1.propStr pointing to the same first string).

The next code segment defines a counter and has two while loops. The aim of this segment is to determine the total number of characters in both strings. It counts the number of characters in the first string, adding the number to the number in the second string. Note: in C++, any string, such as "I am some text" has the ‘ ’ character at its end, unknown to you. The rest of the code in the segment should be self-explanatory.

The next code segment, declares an array, defines an i counter and has two while loops. The aim of this segment is to copy the two strings to the locally declared array, the second string copied immediately after the first. It uses the copied pointers, strA and strB, for the job, enabling the while loops to start from the beginning of each string, copying the characters, one after another. It is this segment that actually joins the two strings together. The rest of the code in the segment should be self-explanatory.

Next, the ‘ ’ character is added at the end of the joined (total) string in the local scope array, making the content of the array an official string.

Next, all the content of this local scope array is copied to the global scope (external) array.

Next, the global scope array is returned. We are at the end of the local scope (operator definition). The local scope array dies. However, the global scope array still has the joined string, which is seen and received in main after it is returned.

Operator Overloading Prototype
The best place to type a prototype is in a namespace in a header file. However, in the following program, the namespace and the prototype are in the same file, for simplicity.  The program is the same one above, but modified to have a namespace for the overloaded operator prototype.

#include <iostream>
using namespace std;


class Stringing
    {
        public:
        const char *propStr;
    };

namespace myName
    {
        const char* operator+(Stringing strObj1, Stringing strObj2);
    }

    //array to receive returned total string content
    char totalStrPtr[500];

const char* myName::operator+(Stringing strObj1, Stringing strObj2)
    {
        //copies for the two string pointers
        const char *strA = strObj1.propStr;
        const char *strB = strObj2.propStr;

        //counter for total number of characters in both strings
        int n = 0;
        //determine the total number of characters in both strings
        while (strObj1.propStr)
            {
                if (*strObj1.propStr == ' ')
                    break;
                ++strObj1.propStr;
                ++n;
            }
        while (strObj2.propStr)
            {
                if (*strObj2.propStr == ' ')
                    break;
                ++strObj2.propStr;
                ++n;
            }

        //create array and fill it with all the characters, joining the strings
        char totalStr[n+1];
        int i = 0; //for counting array elements
        while (strA)
            {
                if (*strA == ' ')
                    break;
                totalStr[i] = *strA;
                ++strA;
                ++i;
            }
        while (strB)
            {
                if (*strB == ' ')
                    break;
                totalStr[i] = *strB;
                ++strB;
                ++i;
            }

        //add the last nul character
        totalStr[n] = ' ';

        //put the content for global totalStrPtr pointer
        for (int j=0; j<=n; ++j)
            {
                totalStrPtr[j] = totalStr[j];
            }

        return totalStrPtr;

    }


int main()
    {
        Stringing strObjA;
        Stringing strObjB;
        strObjA.propStr = "I am the first string.";
        strObjB.propStr = " You are the second string.";

        using namespace myName;
        const char *resultStr = strObjA + strObjB;

        cout << resultStr;


        return 0;
    }

The class, Stringing, has been defined before the namespace, so that it can be seen in the namespace. In the definition interface of the operator, the namespace name followed by the scope resolution operator have been type before the reserved word, operator. Just before using the operator in the main function, the “using namespace myName;” has been typed. These changes indicate how to use an overloaded operator prototype.

Well, I hope you now believe that an operator can be overloaded. We are at the end of this tutorial. We are at the end of the series. I hope you appreciated this tutorial and the series.

Chrys

Related Courses

Storage Duration in C++
Scopes in C++
Function and Operator Overloading in C++
Specifiers in C++
Some Features of C++ Entities
C++ Preprocessing Directives
Writing a C++ Container
C++ Namespace
Writing a C++ Application

C++ Templates
Exception Handling in C++
Container Library Sequences in C++ Simplified
Associative Container in C++ Simplified
String in C++ Standard Library Simplified
Date and Time in C++ Simplified

C++ Course
Relational Database and Sybase
Windows User Interface
Computer Programmer – A Jack of all Trade – Poem

Comments

Become the Writer's Fan
Send the Writer a Message