C++ Extending Operators for User Defined Types

Discussion in 'C++' started by BiplabKamal, May 26, 2016.

  1. We have discussed about inbuilt data types and inbuilt operators which operate on inbuilt data types only. We also discussed about user defined data types. Don’t you think that the operators should work for user defined data types also. You know that we cannot create new operators or change the implementation of inbuilt operators. In the following example we have a structure data type and we want to be able to add two objects of that structure;

    Code:
    struct MyStruct
    {
        int i{ 0 };
        float f{ 0 };
    };
    
    int main()
    {
        MyStruct obj1;
        
        MyStruct obj2;
        
        // Using + operator will give compiler error that operator + does not match with arguments type
        MyStruct obj3 = obj1 + obj2;
    }
    
    In the main function when we try to add two objects it does not allow to do that because the + operator does not accept parameters of types other than inbuilt types. You can solve the purpose by adding a member function in the structure which behave like + operator. After all operators are also functions. Let us add a member function named Add:
    Code:
    struct MyStruct
    {
        int i{ 0 };
        float f{ 0 };
        MyStruct Add(MyStruct op2)
        {
            MyStruct s;
            s.i = i + op2.i;
            s.f = f + op2.f;
            return s;
        };
    };
    int main()
    {
        MyStruct obj1;
        obj1.i = 10;
        obj1.f = 20.75;
        MyStruct obj2;
        obj2.i = 100;
        obj2.f = 200.5;
        MyStruct obj3 =obj1.Add(obj2); // Adding two objects and returning a new object.
        //obj3 will have 110 and 221.25
    }
    
    But won’t you prefer to use + operator over a method like Add(). The operator + is more expressive and easier to use. Also you cannot add methods in an enumeration type. The implementers of C++ kept those in mind and provided the facility to redefine an operator for a new type created by the programmer using enum and class/struct keywords. When you define an operator for a new user defined type, compiler does not discard the inbuilt versions, rather it adds the new version and use when applicable. This feature of the compiler is called operator overloading which we will discuss while discussing C++ overloading in detail. Now we will focus on defining (overloading) the + operator for the struct MyStruct created above. Before overloading an operator we should know the original behavior of the operator. Though we are allowed to change the behavior but that should not be the standard. We can do a multiplication with overloaded version of addition (+) operator but that should be prohibited. Normally function overloading allows to have multiple functions with same function name but different number and types of arguments and different return types, but for operators number of arguments(operands) are fixed and at least one operand should be a class or enumeration for overloading. The addition (+) operator must take two operands and return the sum of the two operands. Let us write a new overloaded version of + operator which takes two objects of and add their data to new instance and return that new instance. Operator overloading is done similar to functions definition but the keyword ‘operator’ is used just before the operator symbol. Return type and function parameters have similar syntax of a normal function.
    Code:
    struct MyStruct
    {
        int i{ 0 };
        float f{ 0 };
    };
    //Overloading + operator for MyStruct type
    MyStruct operator +(MyStruct op1, MyStruct op2)
    {
        MyStruct s;
        s.i = op1.i + op2.i;
        s.f = op1.f + op2.f;
        return s;
    }
    
    int main()
    {
        MyStruct obj1;
        obj1.i = 10;
        obj1.f = 20.75;
        MyStruct obj2;
        obj2.i = 100;
        obj2.f = 200.5;
        MyStruct obj3 = obj1 + obj2;
        // obj3 will have the values 110 and 221.25
    }
    
    Here we have defined the overloaded operator as separate function. Now suppose we have 100 of classes and few operators to overload. So it will be unmanageable to have hundreds of independent overloaded operator functions. Fortunately C++ class/struct allows the operators to be members of the class/struct. When you overload an operator as a member function one argument is reduced because the associated class type becomes the first argument implicitly. For binary operator the type of the first operand is the associated class and for unary operator the operand is the class it self. Let us now redefine the above operator as a member function of the structure Here is the new structure:
    Code:
    struct MyStruct
    {
        int i{ 0 };
        float f{ 0 };
    
        //Overloading + operator
        MyStruct operator +(MyStruct op2)
        {
            MyStruct s;
            s.i = i + op2.i;
            s.f = f + op2.f;
            return s;
        }
    };
    
    We can also add another overloaded version of the + operator to add an integer with an object of MyStruct. Here is the new structure with 3 operators overloaded
    Code:
    //#include<iostream>
    struct MyStruct
    {
        int i{ 0 };
        float f{ 0 };
    
        //Overloading + operator for MyStruct type
        MyStruct operator +(MyStruct op2)
        {
            MyStruct s;
            s.i = i + op2.i;
            s.f = f + op2.f;
            return s;
        }
        //Overloading + operator for MyStruct type
        MyStruct operator +(int op2)
        {
            MyStruct s;
            s.i = i + op2;
            s.f = f + op2;
            return s;
        }
        //Overloading prefix increment (++) operator
        MyStruct operator ++()
        {
            ++i;
            ++f;
            return *this; // 'this' is pointer defined by the compiler which points to the object itself
        }
    };
    int main()
    {
        MyStruct obj1;
        obj1.i = 10;
        obj1.f = 20.75;
        MyStruct obj2;
        obj2.i = 100;
        obj2.f = 200.5;
        MyStruct obj3 = obj1 + obj2;// obj3 will have the values 110 and 221.25
        //std::cout << obj3.i << " " << obj3.f << std::endl;
        MyStruct obj4= obj3 + 3;// obj4 will have the values 113 and 224.25
        //std::cout << obj4.i << " " << obj4.f << std::endl;
        ++obj4;//obj4 will have the values 114 and 225.25
        //std::cout << obj4.i << " " << obj4.f << std::endl;
    }
    
    To overload an operator you should first know the original behavior of the in built version and decide on the nearest matching behavior of the overloaded member operator and define it accordingly. We will defer discussing the details of overloading of individual operators. We will prefer overloading operators as member functions over non-member functions. There are also some operators listed below cannot be overloaded.
    1. .(Dot operator or member access operator)
    2. ?: (Conditional operator)
    3. :: (Scope resolution operator)
    4. .* (Pointer to member operator)
    5. sizeof (Object size operator)
    6. typeid (Object type operator)
    There are few operator functions which cannot be overloaded as non-member function for different reasons:
    1. The assignment operator (=): Non-member overload will not be called because compiler provides a member version
    2. The function call operator(()): To avoid making the function call and overloading complicated
    3. The subscript operator([])
    4. The class member access operator(->)
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice