[c++] Operator overloading example

Overloaded auto-increment and decrement operators

Intger num(2);
num + + ;
 + + num;

The overloading of the increment operator should distinguish between prefix and postfix. There is a question that needs to be considered before overloading, whether num++ returns a temporary variable or the ontology of the num object.

In order to solve this problem, you can consider implementing an Inc_() function and _Inc() function to imitate the behavior of post + + and pre + + respectively.

Integer Inc_(){<!-- -->
    int t=this->_num;
    this->_num + =1;
    return Integer(t);
}
Integer & _Inc(){<!-- -->
    this->_num + + ;
    return *this;
}

Call these two functions separately

Integer num(3);
auto t=num.Inc_();
std::cout<<t.getValue();

Call Inc()_The output results are as follows

Integer num(3);
auto t=num._Inc();
std::cout<<t.getValue();

The output result of calling _Inc() is as follows

From this example, it is not difficult to see that the postfix + + returns a temporary object, and the prefix + + returns the ontology.

Next, start reloading the prefix + + and the postfix + +

Integer operator + + (){<!-- -->//Postfix + +
        int t=this->_num;
        this->_num + =1;
        return Integer(t);
}
Integer & amp; operator + + (){<!-- -->//prefix + +
    this->_num + + ;
    return *this;
}

But if you write like this, an error will be reported, because function overloading is distinguished by the parameter list, not by the return value type.

Usually for the post + + you need to use the position parameter

Integer operator + + (int){<!-- -->//Postfix + +
    int t=this->_num;
    this->_num + =1;
    return Integer(t);
}

Overloaded logical operators and unary operators

Logical AND

bool operator & amp; & amp;(const Integer & amp;right)const
{<!-- -->
    return this->_num & amp; & amp;right._num;
}
std::cout<<std::boolalpha<<(Integer(0) & amp; & amp;Integer(1));

Monocular negative

Integer operator -(){<!-- -->
    return Integer(-this->_num);
}

Bracket operator

() function call operator

By overloading this operator, the object has the behavior of a function (functor)

class Program
{<!-- -->
public:
    void operator()(){<!-- -->//The first bracket is the operator
        std::cout<<"hello world";
    }
};
Program p;
p();//Call the bracket operator

Overloaded parentheses operators can have parameters

class Program
{<!-- -->
public:
    void operator()(int i){<!-- -->//The first bracket is the operator
        std::cout<<"i="<<i<<"hello world";
    }
};


Description

  • The function call operator can only be overloaded with non-static member functions
  • The function call operator can have any number of parameters, but it cannot have default parameters.

[]Subscript access operator

class String
{<!-- -->
private:
    int size;
    char*str;
public:
    
    String(const char*str)
    {<!-- -->
        size=strlen(str);
        this->str=new char[size + 1]{<!-- -->0};
        strcpy(this->str,str);
    }
    int Size(){<!-- -->return size;}
};
int main()
{<!-- -->
    String str="hello world";
    return 0;
}

Suppose we want to have such an operation now

//Loop through the string inside str
for(int i=0;i<str.Size();i + + )
{<!-- -->
    //to do
}

At this time, how to overload the subscript access operator can traverse the internal string through str[i], is this operation very convenient?

char & amp; operator[](int i){<!-- -->
    return str[i];
}

Description

  • [] is a binary operator with two parameters: array name and subscript. The array name is implicitly passed by the compiler through the this pointer, so only one parameter needs to be provided in the parameter list.
  • [] can appear on both the left and right sides of the assignment operator, so overloading often returns references, because functions that return references can appear on the left side of = and be called.
  • Can only be overloaded with non-static member functions

Operators related to pointers

int main()
{<!-- -->
    int*a=new int[5];
    return 0;
}

The above program will cause a memory leak if there is no delete after performing the new operation, but what if you forget to delete? This problem can be solved by implementing a class yourself.

class Auto_Ptr
{<!-- -->
public:
    Auto_Ptr(int*ptr):m_ptr(ptr){<!-- -->}
    ~Auto_Ptr(){<!-- -->delete m_ptr;}
private:
    int*m_ptr;
};
int*a=new int[5];
Auto_Ptr pa(a);

The Auto_Ptr class helps us complete the delete operation, so we don’t have to worry about forgetting to delete after creating new.
So how to make Auto_Ptr class more like a pointer, then we need to overload some operators

class People
{<!-- -->
private:
    int age_;
    std::string name_;
public:
    
    People(const std::string name,int age)
:name_(name),age_(age)
{<!-- -->}
    
    std::string name()const{<!-- -->return name_;}
};
classAuto_Ptr
{<!-- -->
public:
    Auto_Ptr(People*ptr):m_ptr(ptr){<!-- -->}
    ~Auto_Ptr(){<!-- -->delete m_ptr;}
    People *operator->(){<!-- -->
        return m_ptr;
    }
    People *operator & amp;(){<!-- -->
        return m_ptr;
    }
    People operator*(){<!-- -->
        return m_ptr[0];
    }
private:
    People*m_ptr;
};

Summary

  1. Purpose: Give new meanings to existing operators to facilitate our use.
  2. Essence: A form of function overloading, or a special function.
  3. rule:
    • Only operators already defined in C++ can be overloaded.
    • New operators cannot be created.
    • After an operator is overloaded, its precedence, associativity, etc. remain unchanged.
    • The syntactic structure of operators cannot be changed.
  4. Function prototype of overloaded operator: return type operator operator (parameter list).
  5. Parameters of overloaded operators: There can be one or more parameters. When an operator has multiple parameters, they are separated by commas.
  6. The return type of an overloaded operator: can be any data type, including basic data types and custom types.
  7. Operator overloading methods:
    • Prefix form: For example, when overloading the + + operator, the function name is operator + + ().
    • Suffix form: For example, when overloading the + + operator, the function name is operator + + (int). Note that the int here is a virtual parameter and does not represent the real type.
      Precautions:
  8. The overloaded operator must match the input used by the user. If the user uses different input, the overloaded operator will not be called.
  9. Care should be taken to use complex operator overloading, as this can lead to reduced readability and maintainability of the program. If you find that you are using too many parameters or making overly complex operations when overloading operators, you may want to rethink your design.
  10. When overloading an operator, you need to ensure that the semantics and syntax rules of the original operator are not violated. For example, you cannot change the precedence or associativity of an operator.
    When overloading an operator, you need to ensure that the overloaded operator coexists harmoniously with the code that uses it. For example, you cannot let overloaded operators destroy the structure or logic of the original code.

Overall, while operator overloading provides a great deal of flexibility, it needs to be used with caution to avoid introducing unnecessary complexity. When overloading operators, you should try to maintain the original semantics and syntax rules to ensure the readability and maintainability of the code.