Operator overloading (Part I)

Posted by DEVILofDARKNESS on Sat, 15 Jan 2022 06:36:50 +0100

Operator overloading

What is an overloaded operator

  • Not only functions but also operators can be overloaded. For example, "<" is a displacement operator (shift left) in C + + bit operation, but it is also a stream insertion operator used with stream object cout in output operation, "> >" is also a displacement operator (shift right), but it is also a stream extraction operator used with stream object cin in input operation. This is operator overloading. C + + system pair "<" and "> > "Overloaded. When users use them in different situations, their functions are different. The overloaded processing of" < "and" > > "is placed in the header file stream. Therefore, if you want to use" < "and" > > as the stream insertion operator and stream extraction operator in the program, you must include the header file stream in this file module (of course, it should also include "using namespace std")

Example: complex addition is realized by function

#include<iostream>
using namespace std;
class Complex //Define the Complex class
{
public:
	Complex() { real = 0; imag = 0; } //Define constructor
	Complex(double r, double i) { real = r; imag = i; } //Overloading Constructors 
	Complex complex_add(Complex& c2); //Declare complex addition function
	void display();
private:
	double real; //real part
	double imag; //imaginary part
};
Complex Complex::complex_add(Complex& c2) //Define complex addition function
{
	Complex c;
	c.real = real + c2.real;
	c.imag = imag + c2.imag;
	return c;
}
void Complex::display()
{
	cout << "(" << real << "," << imag << "i)" << endl;
}
int main()
{
	Complex c1(3, 4), c2(5, -10), c3; //Define 3 complex objects
	c3 = c1.complex_add(c2); //Call complex addition function
	cout << "c1="; c1.display(); //Value of output c1
	cout << "c2="; c2.display(); //Value of output c2
	cout << "c1+c2="; c3.display(); //Value of output c3
	return 0;
}

Program analysis:

  • A Complex is defined in the Complex class_ The add function adds two Complex numbers and defines a Complex object C as a temporary object in the function body. The assignment statement is equivalent to c.real = this - > Real + C2 real; c.imag = this->imag + c2. imag;
  • This is a pointer to the current object. This - > real can also be written as (* this) real. Now, in the main function, Complex is called through object C1_ Add function, so the above two statements are equivalent to: c.real = C1 real + c2. real; c.imag = c1. imag + c2. imag;. Note that the return value of the function is the value of the Complex class object C.

Operation results:

Operator overloaded method

  • The method of operator overloading is to define a function of overloaded operator, so that the specified operator can not only realize the original function, but also realize the new function specified in the function. When the overloaded operator is used, the system will automatically call the function to realize the corresponding operation. That is, operator overloading is implemented by defining functions. Operator overloading is essentially a function overloading.
  • The general format of overloaded operator functions is:
Function type operator Operator name(Formal parameter table)
	{Overloading of operators}

-In the above general format, operator is a keyword, which is specially used to define the function of overloaded operator. The operator name is the predefined operator provided by C + + to the user. Note: the function name is composed of operator and operator. The above "operator +" is the function name, which means "operator + overloaded function".

#include<iostream>
using namespace std;
class Complex //Define the Complex class
{
public:
	Complex() { real = 0; imag = 0; } //Define constructor
	Complex(double r, double i) { real = r; imag = i; } //Overloading Constructors 
	Complex operator+(Complex& c2); //Function declaring overloaded operator +
	void display();
private:
	double real; //real part
	double imag; //imaginary part
};
Complex Complex::operator+(Complex& c2) //Function defining overloaded operator +
{
	Complex c;
	c.real = real + c2.real; //Realize the addition of the real parts of two complex numbers
	c.imag = imag + c2.imag; //Realize the addition of imaginary parts of two complex numbers
	return c;
}
void Complex::display()
{
	cout << "(" << real << "," << imag << "i)" << endl; //Output complex form
}
int main()
{
	Complex c1(3, 4), c2(5, -10), c3; //Define 3 complex objects
	c3 = c1+c2; //Operator + for complex operations
	cout << "c1="; c1.display(); //Value of output c1
	cout << "c2="; c2.display(); //Value of output c2
	cout << "c1+c2="; c3.display(); //Value of output c3
	return 0;
}
  • You can see that the overloaded operator is implemented by the corresponding function. From the user's point of view, although the functions realized by overloaded operators can be realized by functions, the use of operator overloading can make it easy for users to write, read and maintain.
  • When the operator is overloaded as a member function, the number of formal parameters of the operator function is one less than the number of operands specified by the operator. The reason is that the non static member functions of the class have an implicit this pointer, and the operator function can use this pointer to implicitly access the members of the class object. Therefore, the data of the object itself can be accessed directly without being passed in the formal parameter list. The missing operation object is the object itself.

Note: after the operator is overloaded, its original function remains without loss or change.

Rules for overloading operators

  1. C + + does not allow users to define new operators themselves, and can only overload existing C + + operators
  2. C + + allowed overloaded operators:
Binocular arithmetic operator+(add), - (subtract), * (multiply), / (divide),% (modulo)
Relational operator==(equal to)= (not equal to), < (less than), > (greater than), < = (less than or equal to), > = (greater than or equal to)
Logical operator||(logical or), & & (logical and),! (logical non)
unary operator +(positive), - (negative), * (pointer), & (take address)
Self increasing and self decreasing operator++(self increasing), – (self decreasing)
Bitwise Operators |(bitwise OR), & (bitwise AND), ~ (bitwise reverse), ^ (bitwise exclusive or), < < (move left), > > (move right)
Assignment Operators =,+=,-=,*=,/=,%=,|=,^=,<<=,>>=
Space application and releasenew,delete,new[ ],delete[ ]
Other Operators () (function call), - > (member access), - > * (member pointer access),, (comma), [] (subscript)

There are only 5 operators that cannot be overloaded:
①. (member access operator)
②.* (member pointer access operator)
③ Field (field operator)
④ sizeof (length operator)
⑤?: (conditional operator)
The first two operators cannot be overloaded to ensure that the function of accessing members cannot be changed. The operands of domain operators and sizeof operators are types rather than variables or general expressions, which are not overloaded

  1. Overloading cannot change the number of operator operands (that is, operands)
  • For ex amp le, relational operators ">" and "<" are binocular operators, which are still binocular operators after overloading, and two parameters are required. Operators +, -, *, & can be used as monocular operators or binocular operators. They can be overloaded as monocular operators or binocular operators respectively.
  1. Overloading cannot change the priority of an operator
  • Sometimes, if you want to change the priority of an operator in the program, you can only forcibly change the operation order of overloaded operators by adding parentheses
  1. Overloading cannot change the associativity of operators
  2. Overloaded operator functions cannot have default parameters, otherwise the number of operator parameters will be changed, which is contrary to point (3) above
  3. Overloaded operators must be used with user-defined custom type objects, and at least one of their parameters should be a class object (or a reference to a class object). That is, the parameters cannot all be standard types of C + + to prevent users from modifying the properties of operators used for standard type data.
  4. Operators used for class objects must generally be overloaded, but there are two exceptions. Operators "=" and "&" do not need to be overloaded by users
    ① The assignment operator (=) can be used for each class object and can be used to assign values to each other between similar objects.
    ② The address operator & also does not need to be overloaded. It can return the starting address of the class object in memory.
  5. Theoretically, an operator can be overloaded to perform any operation. For example, the addition operator can be overloaded to the information in the output object, and the ">" operator can be overloaded to the "less than" operation. However, this violates the original intention of operator overloading. Instead of improving readability, it makes people inexplicable and unable to understand the program. You should make the overloaded operator function similar to what it does when it acts on standard type data

Operator overloads functions as class member functions and friend functions

  • There are two ways to handle overloaded functions:
    (1) Take the operator overloaded function as the member function of the class
    (2) The function overloaded by the operator is not a member function of the class (it can be an ordinary function). It is declared as a friend function in the class

Overloaded operators are used as member functions of classes. The general form is:

class Class name{ //Class body
	...
	Return type operator Operation symbol(Formal parameter list)
	{
		Function body
	}
	...
};
//perhaps
class Class name{ //Class body
	...
	Return type operator Operation symbol(Formal parameter list)
	...
};
Return type class name::operator Operation symbol(Formal parameter list)
{
		Function body
}

When the operator overload is a friend function, the number of formal parameters of the operator function is consistent with the number of operands specified by the operator

class Class name{ //Class body
	...
	//Friend declaration
	friend Return type operator Operation symbol(Formal parameter list);
};
Return type operator Operation symbol(Formal parameter list)
{
	Function body
}

Example: overload the operator + to be applicable to Complex addition. The overloaded function is not a member function, but placed outside the class as a friend function of the Complex class

#include<iostream>
using namespace std;
class Complex //Define the Complex class
{
public:
	Complex() { real = 0; imag = 0; } //Define constructor
	Complex(double r, double i) { real = r; imag = i; } //Overloading Constructors 
	friend Complex operator+(Complex& c1,Complex& c2); //Overloaded function as friend function
	void display();
private:
	double real; //real part
	double imag; //imaginary part
};
Complex operator+(Complex& c1,Complex& c2) //Function defining overloaded operator +
{
	return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
void Complex::display()
{
	cout << "(" << real << "," << imag << "i)" << endl; //Output complex form
}
int main()
{
	Complex c1(3, 4), c2(5, -10), c3; //Define 3 complex objects
	c3 = c1+c2; //Operator + for complex operations
	cout << "c1="; c1.display(); //Value of output c1
	cout << "c2="; c2.display(); //Value of output c2
	cout << "c1+c2="; c3.display(); //Value of output c3
	return 0;
}

The difference between an operator overloaded function as a member function of a class and a friend function of a class:

  • If the operator overloaded function is used as a member function, it can freely access the data members of this class through the this pointer, so it can write less parameters of a function. However, it must be required that the first parameter of the operation expression (that is, the operand to the left of the operator) is a class object and is of the same type as the operator function. Because the member function of the class must be called through the object of the class, and the operation result is meaningful only if the return value of the operator overloaded function is of the same type as the object.
  • If the operand on the left side of the operator belongs to the C + + standard type, or is an object of another class, the operator overloaded function cannot be used as a member function, but only as a non member function. If a function needs to access a private member of a class, it must be declared as a friend function.
  • When the binocular operator is overloaded as a friend function, because the friend function is not a member function of this class, there must be two parameters in the formal parameter table column of the function, which cannot be omitted, and the order of the formal parameters is arbitrary. However, in an expression using an operator, the operand on the left side of the operator is required to correspond to the first parameter of the function, and the operand on the right side of the operator is required to correspond to the second parameter of the function.

Since the use of friends will destroy the encapsulation of classes, in principle, operators and functions should be used as member functions as much as possible. However, various factors and programmers' habits should also be considered. The following can be used for reference:

  1. C + + stipulates that assignment operator "=", subscript operator "[]", function call operator "()" and member operator "- >" must be overloaded as member functions
  2. Stream insertion "< < and stream extraction operator" > > "and type conversion operator functions cannot be defined as member functions of a class, but can only be used as friend functions
  3. Generally, unary operators and compound operators (+ =, - =, / =, * =, & =,! =, ^ =,% =, > > =, < < =) are overloaded as member functions
  4. Generally, binocular operators are overloaded as friend functions

Overloaded binocular operator

Binocular operator (or binary operator) is the most commonly used operator in C + +. A binocular operator has two operands, usually on the left and right sides of the operator. When overloading the binocular operator, it goes without saying that there should be two parameters in the function.

The binocular operator is overloaded as a member function of a class. The form is as follows:

Return type class name::operator op(const Type &obj2)
{
	... //The this pointer corresponds to the obj1 operand
}

The binocular operator is overloaded as a friend function of a class. The form is as follows:

Return type class name::operator op(const Type &obj1,const Type &obj2)
{
	... //obj1 and obj2 correspond to two operands respectively
}

Example: declare a String class String, which is used to store strings of indefinite len gt h. Overload operators "= =", "<" and ">" are used for the comparison of two strings equal to, less than and greater than

#include<iostream>
using namespace std;
class String
{
public:
	String() { p = NULL; } //Define default constructor
	String(char* str); //Declaration constructor
	friend bool operator>(String& string1, String& sting2);
	friend bool operator<(String& string1, String& sting2);
	friend bool operator==(String& string1, String& sting2);
	void display();
private:
	char* p; //Character pointer to a string
};
String::String(char* str) //Define constructor
{
	p = str; //Make p point to the argument string
}
void String::display() //Output the string pointed to by p
{
	cout << p;
}
bool operator>(String& string1, String& string2) //Define operator overloaded function
{
	if (strcmp(string1.p, string2.p) > 0) return true;
	else return false;
}
bool operator<(String& string1, String& string2)
{
	if (strcmp(string1.p, string2.p) < 0) return true;
	else return false;
}
bool operator==(String& string1, String& string2)
{
	if (strcmp(string1.p, string2.p) == 0) return true;
	else return false;
}
void compare(String& string1, String& string2)
{
	if(operator>(string1,string2)==1)
	{
		string1.display();
		cout << " > ";
		string2.display();
	}
	else if (operator<(string1, string2) == 1)
	{
		string1.display();
		cout << " < ";
		string2.display();
	}
	else if(operator==(string1, string2) == 1)
	{
		string1.display();
		cout << " == ";
		string2.display();
	}
	cout << endl;
}
int main()
{
	String string1("Hello"), string2("Book"), string3("Computer"), string4("Hello");
	compare(string1, string2);
	compare(string2, string3);
	compare(string1, string4);
	return 0;
}

Operation results:

Overloaded unary operator

  • The unary operator has only one operand. The method of overloading the unary operator is similar to that of overloading the binocular operator. However, since the unary operator has only one operand, the operator overloaded function has only one parameter. If the operator overloaded function is used as a member function, this parameter can also be omitted

The preceding unary operator is overloaded as a member function of a class, in the following form:

Return type class name::operator op()
{
	... //The this pointer corresponds to the obj operand
}

The post unary operator is overloaded as a member function of the class, with the form as follows:

Return type class name::operator op(int)
{
	... //The this pointer corresponds to the obj operand
}

The preceding unary operator is overloaded as a friend function of a class, in the form of:

Return type class name::operator op(const Type &obj)
{
	... //obj corresponding operand
}

The post unary operator is overloaded as a friend function of the class, with the form as follows:

Return type class name::operator op(const Type &obj,int)
{
	... //obj corresponding operand
}

For example, there is a Time class, which contains the data members minute and sec. It simulates a stopwatch. Each Time you walk for 1 second, you enter 1 minute after 60 seconds. At this Time, the second starts from 0. The value of minutes and seconds is required to be output

#include<iostream>
using namespace std;
class Time
{
public:
	Time() { minute = 0; sec = 0; } //Default constructor 
	Time(int m,int s):minute(m),sec(s){} //Overloading Constructors 
	Time operator++(); //Declare operator overloaded member function
	void display() { cout << minute << ":" << sec << endl; } //Define output time function
private:
	int minute;
	int sec;
};
Time Time::operator++() //Define operator overloaded member functions
{
	if (++sec >= 60)
	{
		sec -= 60; //Enter 1 minute after 60 seconds
		++minute;
	}
		return *this; //Returns the current object value
}
int main()
{
	Time time1(34, 0);
	for (int i = 0; i < 61; i++)
	{
		++time1;
		time1.display();
	}
	return 0;
}

Operation results:

  • The "+ +" and "–" operators can be used in two ways: the pre self increment operator and the post self increment operator. Their functions are different. In view of this feature, C + + stipulates that adding an int type parameter to the overloaded function of the self increment (self decrement) operator is the post self increment (self decrement) operator function

Example: add the overload of the post increment operator on the basis of the previous program

#include<iostream>
using namespace std;
class Time
{
public:
	Time() { minute = 0; sec = 0; } //Default constructor 
	Time(int m,int s):minute(m),sec(s){} //Overloading Constructors 
	Time operator++(); //Declaration of overloaded function of prefix autoincrement operator "+ +"
	Time operator++(int); //Overloaded function of post increment operator "+ +" declared
	void display() { cout << minute << ":" << sec << endl; } //Define output time function
private:
	int minute;
	int sec;
};
Time Time::operator++() //Define the overloaded function of the leading autoincrement operator "+ +"
{
	if (++sec >= 60)
	{
		sec -= 60; //Enter 1 minute after 60 seconds
		++minute;
	}
		return *this; //Returns the current object value after self addition
}
Time Time::operator++(int)
{
	Time temp(*this);
	sec++;
	if (sec >= 60)
	{
		sec -= 60; //Enter 1 minute after 60 seconds
		++minute;
	}
	return temp; //The object before self addition is returned
}
int main()
{
	Time time1(34, 59), time2;
	cout << "time1:  ";
	time1.display();
	++time1;
	cout << "++time1:";
	time1.display();
	time2 = time1++; //Assign the value of the object before self addition to time2
	cout << "time1++:";
	time1.display();
	cout << "time2:  ";
	time2.display(); //Outputs the value of the time2 object
	return 0;
}

Program analysis:

  • The pre increment operator "+ +" is self adding first and returns the modified object itself; the post increment operator "+ +" returns the object before self adding and then adds the objects.
    It can be seen that when overloading the post auto increment operator, an int type parameter is added. This parameter is only added to distinguish it from the overloaded function of the pre auto increment operator. In addition, it has no effect. It is not necessary to use this parameter when defining the function. Therefore, you can save the parameter name and just write int in parentheses. The compilation system will automatically call this function when encountering the overloaded post increment operator.

Operation results:

Topics: C++