C++ operator overloading as member function

Operator overloading is essentially function overloading. When overloaded as a member function, he can freely access the data members of this class. In actual use, an overloaded operator is always accessed through an object of that class.

If it is a binary operator, the left operand is the data of the object itself, pointed out by the this pointer, and the right operand needs to be passed through the parameter table of the operator overload function; if it is a unary operator, the operand is indicated by the this pointer of the object If given, no arguments are required.

1. Overloading of binocular operators

For the binocular operator B, if you want to overload it as a member function of a class so that it can realize expressions a B c , where a1 is an object of class A, you should overload B as a member function of class A, and this function has only one form Parameter, the formal parameter type is the type to which c belongs. After overloading, the expression a B c is equivalent to a call a.operator B(c).

2. Overloading of unary operators

For the pre-unary operator U, such as "-" (minus sign), etc., if it is to be overloaded as a member function of a class to realize an expression, U awhere a is an object of class A, then U should be overloaded as class A The member function of the function has no formal parameters. After overloading, the expression U ais equivalent to calling. a.operator U()
For the post-operators "++" and "-", if they are overloaded as member functions of the class, they are used to realize the expression a++or a--, where a is class A object, then the operator should be overloaded as a member function of class A, and the function should have an integer (int) parameter. After overloading, expression sum a++is a--equivalent to call a.operator++(0)sum a.operator--(0). The int type parameter here does not play any role in the operation, it is only used to distinguish post ++, – and pre ++, –.

(1) The realization principle of addition, subtraction, multiplication and division of two objects

[Example 1] The implementation body of adding two objects, that is, the principle of adding two objects. It is to add the data members of the two objects in turn. The same goes for subtraction.

class A
{
    
    
public:
	A(int i = 0, int j = 0, int k = 0) :m_i(i), m_j(j), m_k(k) {
    
    }
	int GetI()const
	{
    
    
		return m_i;
	}
	int GetJ()const
	{
    
    
		return m_j;
	}
	void Add(A& s)
	{
    
    
		cout <<"("<< this->m_i + s.m_i << "," << this->m_j + s.m_j << "," << this->m_k + s.m_k<<")"<<endl;
		
	}

private:
	int m_i;
	int m_j;
	int m_k;
};
void main()
{
    
    
	A a(2, 5, 7);
	A b(5, 7, 9);
	a.Add(b); //a+b
}

Running result:
insert image description here
[Example 2] The implementation body of multiplying two objects, that is, the principle of multiplying two objects. It is to multiply the data members of the two objects in turn. Divide by the same token.

class A
{
    
    
public:
	A(int i = 0, int j = 0, int k = 0) :m_i(i), m_j(j), m_k(k) {
    
    }
	int GetI()const
	{
    
    
		return m_i;
	}
	int GetJ()const
	{
    
    
		return m_j;
	}
	void fn(A& s)
	{
    
    
		cout << "(" << this->m_i * s.m_i << "," << this->m_j * s.m_j << "," << this->m_k * s.m_k << ")" << endl;
	}
private:
	int m_i;
	int m_j;
	int m_k;
};
void main()
{
    
    
	A a(2, 5, 7);
	A b(5, 7, 9);
	a.fn(b); //a * b

}

Running results:
insert image description here
In the above two examples, before executing the Add function a.Add(b);or the fn function a.fn(b); , we don’t know what these two functions do before the execution results come out, which leads to poor readability of the program. good. Although in the above two examples, the function we need to achieve is realized through the function call, but this way of writing is not as good as writing it directly as an operator, which can intuitively express what the function we want to realize is. So that leads to operator overloading.

Reasons for operator overloading:
①I want the current object to be operated like a basic data type.
② It is more readable to write the operation of the object as an operator than as a function call.

(2) The operator "+" is overloaded as a member function

[Example 3] Overload the operator "+" as a member function

class A
{
    
    
public:
	A(int i = 0) :m_i(i) {
    
    }
	A operator+(A& s)
	{
    
    
		cout << "调用函数operator +()" << endl;
		return this->m_i + s.m_i;//把A类中的m_i和B类中的m_i相加,this指针指向当前对象a中的数据成员m_i的地址
		//此处返回值为int型,是因为构造函数可以进行类型转换
	}
	void print()
	{
    
    
		cout << m_i << endl;
	}
private:
	int m_i;
};
int main()
{
    
    
	A a(5);
	A b(6);

	//a + b;//相当于a.operator+(b)  //+(a.b)
	(a + b).print();/*遇到+,先看类中有没有重载加法运算符,如果重载了,去调用这个+的重载函数,
	                      把第一个操作数a当作this指针传给这个重载函数,把右操作数b当作实参传给+的重载函数的形参*/

	A c;
	c = a.operator+(b);
	c.print();
	
	return 0;
}

Running results:
insert image description here
Result analysis:
In the program, when encountering "+", first check whether the "+" here is an overloaded addition operator. If it is overloaded, call the overloaded function of this "+". In the above program, Encountered in the main function a+b, the "+" here is the overloading of the addition operator, so call A operator+(A& s)the overloaded function, pass the first operand a as the this pointer to the overloaded function, and use the right operand b as the real parameter passed to this overloaded function A& s. In A operator+(A& s)the function body of the overloaded function, return the value of adding m_i in class A object a and m_i in class B object b to the formal parameter of the constructor of class A, and then pass the initialization list to the data member m_i of class A to perform The assignment is the result of adding the value of object m_i in class A and the value of m_i of class B object. Then use the added object to call the print function to output the result of the addition.

Because the essence of operator overloading is function overloading, it can be a+b;written that a.operator+(b);these two ways of writing are exactly the same.

[Example 4] Complex addition and subtraction operations are overloaded as member functions

class Complex//复数Complex类的定义
{
    
    
public://外部接口
	Complex(double r = 0.0, double i = 0.0) :real(r), imag(i){
    
    }//构造函数
	Complex operator+(const Complex& c2)const;//运算符+重载成员函数
	Complex operator-(const Complex& c2)const;//运算符-重载成员函数
	void display() const;//输出复数
private://私有成员
	double real;//复数实部
	double imag;//复数虚部
};

Complex Complex::operator+(const Complex& c2)const//重载运算符+函数实现
{
    
    
	return Complex(real + c2.real, imag + c2.imag);//创建一个临时的无名对象作为返回值
}

Complex Complex::operator-(const Complex& c2)const//重载运算符-函数实现
{
    
    
	return Complex(real - c2.real, imag - c2.imag);//创建一个临时的无名对象作为返回值
}

void Complex::display() const
{
    
    
	cout << "(" << real << "," << imag << ")" << endl;
}


int main()
{
    
    
	Complex c1(5, 4);//定义复数类对象

	Complex c2(2, 10);//定义复数类对象

	Complex c3;//定义复数类对象

	cout << "c1=";
	c1.display();

	cout << "c2=";
	c2.display();

	c3 = c1 + c2;//使用重载运算符+完成复数加法
	cout << "c3=c1+c2=";
	c3.display();

	Complex c4;//定义复数类对象
	c4=c1.operator-(c2);//调用重运算符-的存在函数完成复数减法
	cout << "c4=c1-c2=";
	c4.display();


	return 0;
}

Running results:
insert image description here
Analysis:
In the above example, the addition and subtraction operators of complex numbers are overloaded as member functions of the complex number class. It can be seen that, except for the keyword operator used in function declaration and implementation, operator overloading of member functions is the same as ordinary class functions. Member functions are no different. When in use, function calls can be completed directly through operators and operands. At this time, the original functions of the operators "+" and "-" remain unchanged, and operations on basic data types such as integers and floating-point numbers still follow the rules predefined by C++, and new addition and subtraction operations for complex numbers are added. function. The "+" operator, acting on different objects will lead to completely different operation behaviors, and has a wider range of polymorphic features.

In the overloaded "+" and "-" member functions in the above example, an unnamed temporary object is created as the return value. For example, the temporary return Complex(real + c2.real, imag + c2.imag);object syntax at this time means: call the Complex constructor to create a temporary object and return it.

[Example 5] Proper transformation of the original operator "+"

class A
{
    
    
public:
	A(int i = 0) :m_i(i) {
    
    }
	A& operator+(A& b)
	{
    
    
		m_i = m_i + b.m_i; //this = &a this->m_i = this->m_i + b.m_i  a修改了
		return *this;
	}
	void print()
	{
    
    
		cout << m_i << endl;
	}
private:
	int m_i;
};

void main()
{
    
    
	A a(5), b(8);
	A c(2;

	a + b = c;//表达式要放在=的左边,必须有确定的内存单元才可以
	a.print();
}

Running results:
insert image description here
Analysis:

In the above code, in the main program, you want to assign the value of c to a+b, that is, use a+b as an lvalue, but because a+b has no definite memory space, a+b cannot be used as an lvalue , that is, you cannot assign c to an indeterminate memory space. So we need to modify the operator "+" overloaded member function, change the return type of this operator overloaded function to a reference type, and modify the function body content as follows:

	A& operator+(A& b)
	{
    
    
		m_i = m_i + b.m_i;
		return *this;
	}

After the function return type is changed to a reference type, the function body m_i = m_i + b.m_i;is equivalent this->m_i = this->m_i + b.m_i;. The this pointer points to the object a, which stores the address of a, that is, this = &a, which is equivalent to assigning the value of m_i of object a plus m_i of object b to m_i of a, that is to say, this statement actually assigns the value of object The value of the data member in a is modified, and the value of a+b is stored in the memory unit of a. At this time, a+b has a certain memory space, that is, the memory space of a. So a+b can be used as an lvalue, assigning the value of c to a+b, and assigning c to a+b is equivalent to putting the value of c into the memory unit of a+b, that is, putting c into the memory of a unit. The reason why the output a.print();result is the value of object c is that a+b=c;the value of a+b is first assigned to a, and then the value of c is assigned to a, and the value returned in the overloaded function return *this;is the final value of a that has been modified. That is, the value of c.

In general, do not write the overloaded function of the addition operator "+", because it violates the rules of the operator.

(3) Default assignment operator overloading function

[Example 6] Default assignment operator overloaded function

class A
{
    
    
public:
	A(int i = 0) :m_i(i) {
    
    }
	
	void print()
	{
    
    
		cout << m_i << endl;
	}
private:
	int m_i;
};
int main()
{
    
    
	A a(5);
	A b(6);

	(a = b).print();
	
	return 0;
}

Running result:
insert image description here
Result analysis:
We can see from the program that the current operator "=" is not overloaded, but the result is correct, indicating that a default assignment operator overload function is given in the class. The default assignment operator overload function is to assign the value of the data member of the current right object to the data member of the left object in sequence. The default assignment overloaded function here is shallow assignment. If you write this default assignment operator overload function, it is like this:

A& operator(const A& s)
{
    
    
	if(this==&s)//判断自赋值
		return *this;
	m_i=s.m_i;
	return *this;
	
}

So it (a = b).print();is equivalent a.operator=(b)to passing a as the this pointer to the overloaded function, and passing b as the actual parameter to the formal parameter of the overloaded function. The return type of the default assignment operator overload function above is a reference type, because if the value type is used to return, a copy constructor will be generated, which is troublesome, and the expression composed of the current operator can be placed on the left of =, it Has a corresponding memory space, so use a reference as the return type of the default assignment operator overloaded function. Therefore, the last return *this;return is the value of the data member m_i of the current object a.

(4) The unary operator "++" is overloaded as a member function.

[Example 7] The unary operator "++" is overloaded as a member function.

class A
{
    
    
public:
	A(int i = 0) :m_i(i) {
    
    }

	void print()
	{
    
    
		cout << m_i << endl;
	}

	A operator++(int)  //为了实现++的重载,语法规定,在后++的时候,参数里面写int,没有实际的意义,不用传参
	{
    
    
		cout << "调用函数operator++" << endl;
		return m_i++;
	}
	A& operator++()
	{
    
    
		cout << "调用函数++operator" << endl;
		m_i = m_i + 1;
		return *this;
	}
private:
	int m_i;
};

void main()
{
    
    
	A a(6);
	A b(3);
	A c(20);

	//后置++
	cout << "(a++)表达式的值:"<<endl;
	(a++).print();//相当于a.++()  //a++表达式的值是a没加以前的值6  a的值是a加过1之后的值7,最终返回的是表达式的值
	//(a++)这个表达式在执行过程中有两个结果
	cout << "a的值:" << endl;
	a.print();


	//前置++	
	cout << "(++b)表达式的值:" << endl;
	(++b).print(); //b.++()  ++b表达式的值是b加过1之后的值,b的值也是b加过1之后的值
	//因为++b表达式的值和b的值是相同的,所以没有必要给++b这个表达式开辟空间,直接从b的内存单元中将这个值取出来,所以返回类型可以不用值类型,而是使用引用的返回类型
	cout << "b的值:" << endl;
	b.print();

}

Running result:
insert image description here
Result analysis:
① Grammatically stipulates that when post ++, write int in the parameter, which has no practical meaning and does not need to be passed as a parameter. Its purpose is to form function overload with pre ++.
②Postposition ++: The value of (a++) expression is the value before a is not added, and the value of a becomes the value after a+1, so the postposition ++ expression has two different values, but finally returns is the value of the expression, so the return type of the post ++ operator overloaded as a member function is a value type.
③Prefix ++: The value of the (++b) expression is the same as the value of b, which is the value after b+1, so there is only one value of the prefix ++ expression, because the value of the ++b expression It is the same as the value of b, so there is no need to open up space for the expression ++b, and the value is directly taken out from the memory unit of b, so the return type can use the reference return type instead of the value type.

[Example 8] An example using the clock class. The operands of the unary operators pre ++ and post ++ are objects of the clock class, and these operators are overloaded as member functions of the clock class. For the pre-unary operator, the overloaded function has no formal parameters, and for the post-unary operator, the overloaded function has an int type parameter.

class Clock
{
    
    
public:
	Clock(int hour, int minute, int second);
	void showTime()const;
	Clock& operator++();
	Clock operator++(int);
private:
	int h, m, s;
};

Clock::Clock(int hour, int minute, int second)
{
    
    
	if (hour >= 0 && minute >= 0 && second >= 0 && hour < 24 && minute < 60 && second < 60)
	{
    
    
		h = hour;
		m = minute;
		s = second;
	}
	else
	{
    
    
		cout << "时间不存在" << endl;
	}
}
void Clock::showTime()const
{
    
    
	cout << h << ":" << m << ":" << s << endl;
}

Clock& Clock:: operator++()
{
    
    
	s++;
	if (s >= 60)
	{
    
    
		
		s -= 60;
		m++;
		if (m >= 60)
		{
    
    
			m -= 60;
			h = (h + 1) % 24;
		}
	}
	return *this;
}

Clock Clock::operator++(int)
{
    
    
	Clock old = *this;
	
	return *this;
}

int main()
{
    
    
	Clock c1(24, 59, 1);

	Clock c2(10,59,59);

	cout << "输出c2的时间:";
	c2.showTime();

	cout << "输出c2++后的时间";
	(c2++).showTime();

	cout << "输出++c2后的时间";
	(++c2).showTime();

	return 0;
}

operation result:
insert image description here

Guess you like

Origin blog.csdn.net/NuYoaH502329/article/details/132154004