Unary and Binary Operators

In C++, operators play a fundamental role in performing various operations on data. They can be broadly categorized into two types: unary operators, which take a single parameter, and binary operators, which work with two parameters. Each type of operator can serve a specific purpose in manipulating custom data types and their expressions.

Let’s explore these two categories in depth to gain a comprehensive understanding of how to effectively overload these functions and apply them to a user-defined data type.

Overloading unary operators

Unary operators act on a single operand. Here are some common unary operators:

  • Prefix and postfix increment (++, --)

  • Unary plus and minus (+, -)

  • Logical NOT (!)

  • Function call operator (())

  • Others (*, &, ~)

We can overload unary operators both inside and outside a class. When overloading inside a class, it’s common to use constant (const) member functions to ensure the left argument is received as const in the this pointer.

Example: Overloading the unary minus (-) operator inside and outside a class

#include <iostream>
using namespace std;
class Number
{
private:
int value;
public:
Number(int val) : value(val) {}
Number operator-() const
{
return Number(-value);
}
void display() const
{
cout << "Value: " << value << endl;
}
};
int main() {
Number num(10);
Number negNum = -num; // Here we have called the -ve operator
num.display(); // Output: Value: 10
negNum.display(); // Output: Value: -10
return 0;
}
Overloading the unary minus (-) operator inside a class

In the UnaryInside.cpp file:

  • Line 5–9: The Number class has a private member variable value to store an integer value. The constructor initializes this value based on the provided argument.

  • Line 11: The unary minus operator (-) is overloaded as a member function of the Number class. It is declared as a constant member function using the const keyword at the end of the function signature. This means that the operator function does not modify the state of the object on which it is called.

  • Line 13: Inside the overloaded operator function, a new Number object is created with the negation of the original value. The negated value is obtained by using the unary minus operator (-) on the value member variable.

  • Line 16–19: The display() member function is also defined within the Number class to display the value of the Number object. It is declared as a constant (const) member function to ensure it does not modify the object’s state.

  • Line 23–24: In the main() function, an instance of the Number class called num is created with 10 as the initial value. Then, the unary minus operator is applied to num using the syntax -num. The result is assigned to another Number object called negNum.

  • Line 26–27: Finally, the display() function is called on both num and negNum to print their respective values. The output demonstrates that the unary minus operator correctly negates the value of num, resulting in -10 for negNum.

Note: Why are we using the const keyword? By defining the overloaded unary minus operator as a constant member function using the const keyword, the operator can be used on constant objects and guarantees that the object's state remains unaltered. The this pointer, which is an implicit pointer to the current object, refers to the object on which the operator is invoked. The const qualifier ensures that the this pointer is received as a const pointer, enabling the invocation of const member functions and access to const data members within the operator function. It is always recommended to use operators functions as constants so that the callee object (the left of the operand) does not get updated even by mistake (yielding a logical error).

In the UnaryOutside.cpp file:

  • Line 26–35: The class contains getter and setter functions to modify and access the value member variable. Additionally, the global function const Number operator-(const Number&) for the unary operator takes one parameter.

    • The operator-() function works by creating a new Number object and assigning values using either the setter or the constructor. To retrieve the value inside the passed parameter object, the getter function is utilized.

These are the two general ways of creating a unary operator. We may define the above-listed operators in any way for the user-defined data types. Don’t forget to change the definition of the parameter list passed corresponding to each unary operator.

We’ll look at some more examples of unary operator overloading when we discuss the string library and implement the Matrix case study. For now, we must comprehend the idea after making a unary operator function.

Overloading binary operators

Binary operators work on two operands. Some common binary operators include:

  • Arithmetic operators (+, -, *, /, %)

  • Relational and equality operators (==, !=, <, >, <=, >=)

  • Logical operators (&&, ||)

  • Assignment (=) and compound assignment operators ( +=, -=, *=, /=, %= )

  • Array subscript operator ([])

  • Function call operator (())

  • Others (<<, >>)

Like unary operators, binary operators can be overloaded inside and outside a class.

#include <iostream>
using namespace std;
class Number
{
private:
int value;
public:
Number(int val) : value(val)
{}
int getValue() const
{
return value;
}
Number operator+(const Number& num2)const
{
return Number(this->getValue() + num2.getValue());
}
};
int main()
{
Number num1(5);
Number num2(10);
Number sum = num1 + num2;
cout << "Sum: " << sum.getValue() << endl; // Output: Sum: 15
return 0;
}
Overloading the binary operator (+) inside a class

In the OperatorOutside.cpp file:

  • We define a class called Number with an integer member variable called value. We then overload the addition (+) operator outside the class using a nonmember function. The operator function takes two Number objects as const references and returns a new Number object with the sum of their values. In the main() function, we create two Number objects num1 and num2 with values 5 and 10, respectively. We then use the addition operator to add num1 and num2, storing the result in the sum variable. Finally, we display the sum using the getValue() member function.

Overloading compound assignment operators

Compound assignment operators combine arithmetic or logical operations with an assignment. Examples include +=, -=, *=, /=, %=, |=, &= etc. Overloading compound assignment operators can simplify code and make it more readable.

Example: Overloading the compound addition assignment (+=) operator

#include <iostream>
using namespace std;
class Number
{
private:
int value;
public:
Number(int val) : value(val) {}
Number& operator+=(const Number& other)
{
value += other.value;
return *this;
}
void display() const
{
cout << "Value: " << value << endl;
}
};
int main() {
Number num(5);
num += Number(10);
num.display(); // Output: Value: 15
return 0;
}
Overloading the compound addition assignment (+=) operator inside a class

In the Operator+=Outside.cpp file:

  • We’ve added the following global function:

Number& operator+=(Number& left, const Number& other)
  • The first parameter, left, is a nonconstant reference because it modifies the value of the left operand. The second parameter, other, is a constant reference because it is not modified. Notice that for this, we need to have getters and setters implemented inside the class.

In the Operator+=Inside.cpp file:

  • The operator+= function is a nonconstant member function.

Number& operator+=(const Number& other)
  • It operates on the current object (*this) and updates its value. It’s not declared const because it modifies the callee object on which the function is invoked. We don’t need getters and setters for this implementation as the function is written inside the class, and the member attributes of both can be accessed through other.value and this->value.