Contents
  1. I. Addition Operator Overloading
  2. II. Left-Shift Operator Overloading
  3. III. Increment Operator Overloading
  4. IV. Assignment Operator Overloading
  5. V. Relational Operator Overloading
  6. VI. Function-Call Operator Overloading

Operator overloading redefines existing operators to give them another function, allowing them to work with different data types.

I. Addition Operator Overloading

Purpose: Implement addition for two custom data types.

1.1 Overload the + operator with a member function

例:
class Person{
    Person operator+(Person &p){
		Person temp;
		temp.m_A = this->m_A + p.m_A;
		temp.m_B = this->m_B + p.m_B;
		return temp;
	}
    int m_A;
    int m_B;
};

How to call it:

  1. Underlying call: Person p3 = p1.operator+(p2);
  2. Simplified call: Person p3 = p1 + p2;

1.2 Overload the + operator with a global function

Person operator+(Person &p1, Person &p2){
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}
Person operator+(Person &p1, int num){
	Person temp;
	temp.m_A = p1.m_A + num;
	temp.m_B = p1.m_B + num;
	return temp;
}

How to call it:

  1. Underlying call: Person p3 = operator+(p1, p2);
  2. Simplified call: Person p3 = p1 + p2;

Operator overloading can itself involve function overloading:

Person p3 = p1 + p2;
Person p4 = p1 + 100;

1.3 Notes:

  • Operators in expressions involving built-in data types cannot be changed.
  • Do not overuse operator overloading.

II. Left-Shift Operator Overloading

Overload the << left-shift operator with a global function

ostream& operator<<(ostream &cout,Person &p){
	cout << "p.m_A = " << p.m_A << endl;
	cout << "p.m_B = " << p.m_B << endl;
	return cout;
}
  • Select cout, right-click, and choose Go to Declaration. You can then see that cout belongs to the ostream class.
  • Returning cout enables method chaining, allowing additional << operators to be appended indefinitely.
  • A member-function overload is not used because it could only implement p.operator<<(cout), which is p<<cout and does not match the expected behavior.

How to call it:

cout << p << endl;

III. Increment Operator Overloading

class MyInteger{
public:
	MyInteger(){
		m_Num = 0;
	}
	//重载前置++运算符
	MyInteger& operator++(){
		++m_Num;
		return *this;
	}
	//重载后置++运算符	此处的int代表占位参数,可以用于区分前置和后置递增
	MyInteger operator++(int){
		MyInteger temp = *this;
		m_Num++;
		return temp;
	}

private:
	int m_Num;
};

Prefix ++:

  • The return value is a reference so that ++(++a) can be implemented, ensuring that each increment operates on the same data.

Postfix ++:

  • Because postfix ++ must return the current value before incrementing it, first save the current value, increment it, and then return the saved value.
  • The return value is passed by value because temp is a local object, and a local object cannot be returned by reference.

IV. Assignment Operator Overloading

The C++ compiler adds at least four functions to a class:

  1. A default constructor (no parameters and an empty function body)
  2. A default destructor (no parameters and an empty function body)
  3. A default copy constructor that copies property values
  4. An assignment operator, operator=, that copies property values

If a class has a property that points to the heap, assignment can also cause shallow-copy and deep-copy issues.

Solution: Use a deep copy to solve the problems caused by shallow copying.

#include<iostream>
using namespace std;

class Person{
public:
	Person(int age){
		m_Age = new int(age);
	}

	~Person(){
		if(m_Age != NULL){
			delete m_Age;
			m_Age = NULL;
		}
	}

	//重载赋值运算符
	Person& operator=(Person &p){
		//先判断是否有属性在堆区,如果有,先释放干净,再深拷贝
		if(m_Age != NULL){
			delete m_Age;
			m_Age = NULL;
		}
		//进行深拷贝
		m_Age = new int(*p.m_Age);
		return *this;
	}

	int *m_Age;
};

void test01(){
	Person p1(18);
	Person p2(20);
	Person p3(24);
	p3 = p2 = p1;

	cout << "p1.m_Age = " << *p1.m_Age << endl;
	cout << "p2.m_Age = " << *p2.m_Age << endl;
	cout << "p3.m_Age = " << *p3.m_Age << endl;
}

int main(){

	test01();

	system("pause");
	return 0;
}

Notes:

  • The logic of overloading = is as follows: for p2=p1, first release the property in p2 (because with a shallow copy, m_Age in p2 and p1 points to the same memory), and then allocate new memory for a deep copy.
  • The return value is Person& to allow chained assignment.

V. Relational Operator Overloading

Purpose: Overloading relational operators allows two objects of custom types to be compared. (For example, == and !=)

//例:重载==运算符
bool operator==(Person &p){
    if(this->m_Name == p.m_Name && this->m_Age == p.m_Age){
        return true;
    }
    else{
        return false;
    }
}

VI. Function-Call Operator Overloading

  • The function-call operator is ().
  • Because an overloaded function-call operator is used much like a function call, it is called a function object.
  • Function objects have no fixed implementation and are highly flexible.
//例:重载()实现自定义打印
class MyPrint{
public:
	void operator()(string test){
		cout << test << endl;
	}
};
---------
//调用方法
	MyPrint myprint;
	myprint("hello world");

Overloading () closely resembles a function call. For example, the functionality above can also be implemented with a function.

void myPrint(string test){
	cout << test << endl;
}
---------
myPrint("hello world")

Reference: A Meticulously Crafted Work by Heima Programmer | C++ Tutorial: Start at 0 and Advance Through 1, Making Programming Easier Link: https://www.bilibili.com/video/BV1et411b73Z