Operator overloading in C ++. Operator function. Keyword operator. Overload of basic arithmetic operators

Operator overloading in C++. Operator function. Keyword operator. Overload of basic arithmetic operators +, , *, /. Examples of the implementation of built-in operator functions

This topic displays the capabilities of the C ++ language in the implementation of operator overloading. Not all modern programming languages support operators overloading. A good understanding of the programming process of overloaded operators is an indicator of the professionalism and high skill of a modern programmer.

Before considering this topic, it is recommended to familiarize yourself with the following topic:


Contents


1. What are unary and binary operators?

There are three main types of operators: unary, binary and n-ary (n> 2).

Unary operators are operators that require one operand to be calculated, which can be placed to the right or to the left of the operator itself.

Examples of unary operators:

i++
--a
-8

Binary operators are operators that require two operands to compute.
Example. The following are fragments of expressions with binary operators +, , %, *

a+b
f1-f2
c%d
x1*x2

n-ary operators require more than two operands for calculations. In C ++, there is a ternary operation ?:, which requires three operands for its operation. More about this ternary operation ?: is described here.

 

2. What is the essence of operator overloading? What is an operator function?

The C++ language has ample opportunity to overload most operators. Operator overloading means using the operator to operate on class objects. Operator overloading is a way to declare and implement an operator in such a way that it processes objects of particular classes or performs some other actions. When an operator is overloaded in a class, the corresponding operator function is called, which performs actions that relate to this class.

If the operator is “overloaded”, then it can be used in other methods in its usual form. For example, element-wise summation of two arrays a1 and a2

a1.add(a2);
a3 = add(a1, a2);

better to invoke in a more natural way:

a1 = a1 + a2;
a3 = a1 + a2;

In this example, the ‘+’ operator is considered overloaded.

 

3. What are some ways to implement an operator function for a given class? What are the different kinds of operator functions?

For a given class, an operator function in a class can be implemented:

  • inside the class. In this case, the operator function is a class method;
  • outside of class. In this case, the operator function is declared outside the class as “friendly” (with the friend keyword). More details about the implementation of “friendly” functions outside the class are described here.

 

4. The general form of the operator function, which is implemented in the class. Keyword operator

The general form of an operator function implemented in a class is as follows:

return_type ClassName::operator#(arguments_list)
{
    // some operations
    // ...
}

here

  • return_type – type of value that is returned by an operator function;
  • ClassName – the name of the class in which the operator function is implemented;
  • operator# – keyword defining an operator function in a class. The # symbol is replaced by a C++ operator that is overloaded. For example, if the overloaded ‘+’ operator, you need to specify the operator+;
  • argument_list – a list of parameters that receives an operator function. If the binary operator is overloaded, argument_list contains one argument. If the unary operator is overloaded, then the list of arguments is empty.

 

5. An example of overloading unary and binary operators for a class that contains single data. The operator function is implemented inside the class.

A Point class is declared that implements a point on the coordinate plane. The class implements:

  • two internal variables x, y, which are the coordinates of a point;
  • two constructors of class;
  • access methods to internal variables of class GetX(), GetY(), SetX(), SetY();
  • two operator functions operator+() and operator-().
// A class that implements a point on the coordinate plane
// the class contains two operator functions
class Point
{
private:
    int x, y; // point coordinates

public:
    // constructors
    Point()
    {
        x = y = 0;
    }

    Point(int nx, int ny)
    {
        x = nx;
        y = ny;
    }

    // methods of access to class data
    int GetX(void) { return x; }
    int GetY(void) { return y; }
    void SetX(int nx) { x = nx; }
    void SetY(int ny) { y = ny; }

    // overloaded binary operator '+'
    Point operator+(Point pt)
    {
        // p - a temporary object that is created using the constructor without parameters
        Point p;
        p.x = x + pt.x;
        p.y = y + pt.y;
        return p;
    }

    // overloaded unary operator '-'
    Point operator-(void)
    {
        Point p;
        p.x = -x;
        p.y = -y;
        return p;
    }
};

As can be seen from the above code, the operator function operator+() receives one parameter. This means that this function implements the binary operator ‘+’. This parameter corresponds to the operand, which is located on the right side of the binary operator ‘+’. The operand that is located on the left side of the ‘+’ operator is implicitly passed to the operator function using the this pointer of this class.

The call to the operator function is performed by an object that is located on the left side of the assignment operator.
Demonstration of using overloaded Point class operators in another method:

// variables declaration - objects of the CPoint class
Point P1(3,4);
Point P2(5,7);
Point P3;
int x, y; // variables

// 1. Using overloaded binary operator '+'
P3 = P1 + P2; // object P1 calls the operator function

// checking
x = P3.GetX(); // x = 8
y = P3.GetY(); // y = 11

// 2. Using overloaded unary operator '-'
P3 = -P2;
x = P3.GetX(); // x = -5
y = P3.GetY(); // y = -7

In the above code, in the summation operation ‘+’, the P1 object calls an operator function. That is, a string fragment

P1 + P2

is replaced by the call

P1.operator+(P2)

Implement the operator function operator+() in the class can be in another way

Point operator+(Point pt)
{
    // constructor call with two parameters
    return Point(x+pt.x, y+pt.y); // a temporary object is created, which is then copied
}

In the above function, a temporary object is created in the return statement by invoking a two-parameter constructor implemented in the class. If (in this case) the constructor with two parameters is removed from the class body

// constructor with two parameters
Point(int nx, int ny)
{
    // ...
}

then the above variant of the function operator+() will not work, since this function uses a constructor with two parameters to create an object of type Point. In this case, the compiler will display a message.

Point::Point : no overloaded function takes 2 arguments

 

6. An example of overloading the ‘*’ operator that processes a class that contains an array of real numbers. The operator function is implemented inside the class.

The example implements the operator function operator*(), which multiplies elementwise values of the internal arrays of objects of the ArrayFloat class. If the size of the arrays is not the same, then only the number of elements that is the minimum between the two sizes of the arrays is multiplied.

// array of real numbers
class ArrayFloat
{
private:
    float A[10]; // array of real numbers, fixed array size
    int size;

public:
    ArrayFloat()
    {
        size = 0;
    }

    ArrayFloat(int nsize, float nA[])
    {
        size = nsize;
        for (int i=0; i<nsize; i++)
            A[i] = nA[i];
    }

    // access methods
    float GetAi(int i)
    {
        if ((i>=0) && (i<=size-1))
            return A[i];
        else
            return 0;
    }

    void SetAi(int i, float value)
    {
        if ((i>=0) && (i<=size-1))
            A[i] = value;
    }

    // overloaded operator '*'
    ArrayFloat operator*(ArrayFloat AF)
    {
        ArrayFloat tmp;
        int n;

        if (size<AF.size)
            n = AF.size;
        else
            n = size;

        for (int i=0; i<n; i++)
            tmp.A[i] = A[i] * AF.A[i];
        tmp.size = n;
        return tmp;
    }
};

Using the ArrayFloat class in another method

// additional variables and arrays
float x, y;
float AF1[] = { 2, 5, 7, 9, 12 };
float AF2[] = { 3, 4, 9, 8, 10, 13 };

// create objects of the ArrayFloat class
ArrayFloat A1(5, AF1);
ArrayFloat A2(6, AF2);
ArrayFloat A3;

// invoke the operator function operator*()
A3 = A1 * A2; // elementwise multiplication is performed

// checking
x = A3.GetAi(0); // x = 6
y = A3.GetAi(1); // y = 20
x = A3.GetAi(2); // x = 63
y = A3.GetAi(4); // y = 120

 

7. An example of adding two arrays. The operator function operator+() is located inside the class

An ArrayFloat class is set that implements a dynamic array of float numbers. The class implements:

  • internal variables size, A, describing the size of the array and the array itself;
  • two constructors initializing the items of the array with initial values;
  • copy constructor;
  • access methods GetSize(), SetSize(), GetAi(), SetAi(), which implement access to the internal variables of the array with the corresponding operations (memory allocation, checking for acceptable boundaries);
  • operator function operator=(), which implements copying objects;
  • operator function operator+(), which implements overloading operator ‘+’ for arrays of type ArrayFloat. The operator function adds elementwise values of arrays, which are operands of the ‘+’ operation;
  • destructor.

 

// class - an array of type float
class ArrayFloat
{
private:
    int size; // array size
    float * A; // dynamic size of array

public:
    // class constructors
    // constructor without parameters
    ArrayFloat()
    {
        size = 0;
        A = NULL;
    }

    // constructor with two parameters
    ArrayFloat(int nsize, float * nA)
    {
        size = nsize;
        A = new float[size];

        for (int i=0; i<nsize; i++)
            A[i] = nA[i];
    }

    // copy constructor
    ArrayFloat(const ArrayFloat& _A)
    {
        size = _A.size;
        A = new float[size];
        for (int i = 0; i < size; i++)
            A[i] = _A.A[i];
    }
    // access methods
    int GetSize(void) { return size; }
    void SetSize(int nsize)
    {
        if (size>0)
            delete A;

        size = nsize;
        A = new float[size]; // allocate a new part of memory

        // fill the array with zeros
        for (int i=0; i<size; i++)
            A[i] = 0.0f;
    }

    float GetAi(int index)
    {
        if ((index>=0) && (index<size))
            return A[index];
        else
            return 0;
    }

    void SetAi(int index, float value)
    {
        if ((index>=0) && (index<size))
            A[index] = value;
    }

    // copy operator operator=()
    ArrayFloat operator=(const ArrayFloat& _A)
    {
        if (size > 0)
            delete[] A;
        size = _A.size;
        A = new float[size]; // виділити пам'ять

        for (int i = 0; i < size; i++)
            A[i] = _A.A[i];

        return *this;
    }
    // overloading of operator '+',
    // operator function
    ArrayFloat operator+(ArrayFloat AF2)
    {
        int n;
        // get the minimum size of two arrays
        if (size<AF2.size) n = size;
        else n = AF2.size;

        ArrayFloat tmpA; // class object
        tmpA.SetSize(n); // a new size of array

        // elementwise summation
        for (int i=0; i<n; i++)
            tmpA.A[i] = A[i] + AF2.A[i];
        return tmpA;
    }

    // destructor
    ~ArrayFloat()
    {
        if (size > 0)
            delete[] A;
    }
};

The following demonstrates the use of the ArrayFloat class and the operator function operator+() of this class.

// additional variables, arrays
float x, y;
float F1[] = { 3.8f, 2.9f, 1.5f };
float F2[] = { 4.3f, 1.5f, 7.0f, 3.3f };

// declaring objects of ArrayFloat class
ArrayFloat AF1(3, F1);
ArrayFloat AF2(4, F2);

// checking
x = AF1.GetAi(0); // x = 3.8
y = AF2.GetAi(3); // y = 3.3

// declaration of an additional object - result
ArrayFloat AF3;
AF3 = AF1 + AF2; // invoke the operator function operator+()

// checking
x = AF3.GetAi(0); // x = 3.8 + 4.3 = 8.1
y = AF3.GetAi(1); // y = 2.9 + 1.5 = 4.4

// 
AF3 = AF1 + AF1 + AF2;

x = AF3.GetAi(1); // x = 2.9 + 2.9 + 1.5 = 7.3
y = AF3.GetAi(2); // y = 1.5 + 1.5 + 7.0 = 10.0

 

8. What restrictions are imposed on overloaded operators?

The following restrictions apply to the use of overloaded operators:

  • when an operator is overloaded, the operator’s priority cannot be changed;
  • it is impossible change the number of operands of the operator. However, in the code of an operator function, one of the parameters (operands) can be omitted;
  • it is not possible overload operators ::, ., *, ?:;
  • it is not possible call an operator function with default arguments. Exception – operator function which invokes the operator()() function.


 

9. Which operators can not be overloaded?

It is impossible to overload following operators:

  • :: – the scope resolution operator;
  • . (point) – access to the member of structure or member of class;
  • * – access by the pointer;
  • ?:  – the ternary operation.

 

10. What types of objects can an operator function return? Examples of operator functions that return objects of different types

The operator function can return objects of any type. Most often, the operator function returns an object of the class type in which it is implemented or with which it works.

Example. The Complex class is specified in which two operators are overloaded:

  • unary operator ‘+’, which returns the module of a complex number (type double);
  • binary operator ‘+’, which returns the sum of complex numbers. The operator function returns an object of type Complex;
  • binary operator ‘+’, which adds a real number to the complex number. In this case, the operator function receives a real number as an input parameter and returns an object of type Complex.

The class text is as follows:

// class Complex
class Complex
{
private:
    float real; // real part
    float imag; // imaginary part

public:
    // constructors
    Complex(void)
    {
        real = imag = 0;
    }

    Complex(float _real, float _imag)
    {
        real = _real;
        imag = _imag;
    }

    // access methods
    float GetR(void) { return real; }
    float GetI(void) { return imag; }

    void SetRI(float _real, float _imag)
    {
        real = _real;
        imag = _imag;
    }

    // declaration of operator function overloading binary '+'
    // the function returns an object containing the sum of two complex numbers
    Complex operator+(Complex c)
    {
        Complex c2; // temporary object

        // the sum of complex numbers
        c2.real = real + c.real;
        c2.imag = imag + c.imag;

        return c2;
    }

    // declaration of the operator function overloading the unary '+'
    // the function returns the module of a complex number
    float operator+(void)
    {
        float res;
        res = std::sqrt(real*real+imag*imag);
        return res;
    }

    // declaration of operator function operator+()
    // the function adds to the complex number some number which is the input parameter
    Complex operator+(float real)
    {
        Complex c2; // result object
        c2.real = this->real + real;
        c2.imag = this->imag;
        return c2;
    }
};

The following demonstrates the use of the Complex class and overloaded operator functions in some other method.

Complex c1(1,5);
Complex c2(3,-8);
Complex c3; // the resulting object
double d;

// checking
c3 = c1 + c2;
d = c3.GetR(); // d = 1 + 3 = 4
d = c3.GetI(); // d = 5 + (-8) = -3

// overloaded unary operator '+'
d = +c1; // d = |1 + 5j| = 5.09902 - the absolute value of a number
d = +c2; // d = |3 + (-8)j| = 8.544

// invoking of overloaded binary '+',
// add a number to the complex number
c3 = c1 + 5.0;
d = c3.GetR(); // d = 1 + 5 = 6

 

11. Is it possible to change the values of operands in an operator function?

Yes, it is. However, such actions are not useful from the point of view of common sense. So, for example, the multiplication operation

6 * 9

does not change the values of its operands 6 and 9. The result is 54. If the operator function operator*() changes the values of its operands, this can lead to invisible errors in the programs, since the programmer, by habit, will assume that the values of the operands are constant.

 

12. Is it possible to implement operator functions in a class that overload the same operator, get the same parameters but return different values?

No, it is not. An operator function cannot have multiple implementations in a class with the same parameter signature (when the types and number of parameters are the same).

cannot overload functions distinguished by return type alone

For example. It is impossible to overload the ‘+’ operator in the class as shown below.

class SomeClass
{
    // ...

    SomeClass operator+(SomeClass c1)
    {
        // ...
    }

    double operator+(SomeClass c1) // this is error!
    {
        // ...
    }

    // ...
}

This rule applies to any class functions.

 

13. Is it possible to implement two or more operator functions in a class that overload the same operator and receive different (different from each other) parameters?

Yes, it is. In question 10, the Complex class is implemented, in which two operator functions are implemented, which overload the ‘+’ operator in different ways.

 


Related topics