A detailed explanation of C++ operator overloading

First, the basic concept of operator overloading

 Operator overloading is to redefine an existing operator and give it another function to adapt to different data types (operator overloading cannot change the original meaning. It cannot change the meaning of the underlying type)

Operator overloading is just a syntactic convenience, i.e. it's just another way of calling a function

 In C++ it is possible to define a new operator that handles classes. This definition is much like a normal function definition, except that the function name consists of the keyword operator followed by the operator that follows it, and that's it, it's like any other A function is also a function, which is called when the compiler encounters the appropriate pattern

Syntax: Defining an overloaded operator is like defining a function, except that the name of the function is operator@ where @ represents the overloaded operator, and the number of parameters in the function's parameters depends on two factors

Second, the plus operator overloading

2.1 Write as a global function

If the overloaded function is written as global, the left side of the binary operator is the first parameter, and the right side is the second parameter

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

private:
	int id;
	int age;
};

// global overload
Maker operator+(Maker &p1, Maker &p2)
{
	Maker t(p1.getId() + p2.getId(), p1.getAge() + p2.getAge());
	return t;
}

void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// The compiler sees the addition of two objects, then the compiler will look for a function called operator+
	Maker m3 = m + n;// operator overloading

	cout << m3.getId() << m3.getAge() << endl;
}

int main()
{

	test01();
	return EXIT_SUCCESS;
}

2.2 Write it as a member function

If the overloaded function is written as a member function, then the left side of the binary operator is this, and the right side is the first parameter

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// Written as a member function, then only one parameter is required, and this parameter is the right side of the plus sign
	Maker operator+(Maker& m)
	{
		Maker t(this->id + m.id,this->age + m.age);
		return t;
	}

private:
	int id;
	int age;
};

 global overload
//Maker operator+(Maker &p1, Maker &p2)
//{
//	Maker t(p1.getId() + p2.getId(), p1.getAge() + p2.getAge());
//	return t;
//}

void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// The compiler sees the addition of two objects, then the compiler will look for a function called operator+
	Maker m3 = m + n;// operator overloading

	cout << m3.getId() << m3.getAge() << endl;
}

int main()
{

	test01();
	return EXIT_SUCCESS;
}

2.3 Adding objects of different classes

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// Written as a member function, then only one parameter is required, and this parameter is the right side of the plus sign
	Maker operator+(Maker& m)
	{
		Maker t(this->id + m.id,this->age + m.age);
		return t;
	}

private:
	int id;
	int age;
};


class Student
{
public:
	Student()
	{
		this->mid = 0;
	}

	Student(int id)
	{
		this->mid = id;
	}


	int getID()
	{
		return this->mid;
	}
private:
	int mid;
};

 global overload
//Maker operator+(Maker &p1, Maker &p2)
//{
//	Maker t(p1.getId() + p2.getId(), p1.getAge() + p2.getAge());
//	return t;
//}

void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// The compiler sees the addition of two objects, then the compiler will look for a function called operator+
	Maker m3 = m + n;// operator overloading

	cout << m3.getId() << m3.getAge() << endl;
}

// Adding objects of different classes Global overloading
Maker operator+(Maker &m, Student &s)
{
	Maker t(m.getId() + s.getID(),20);
	return t;
}

void test02()
{
	Maker m(1, 9);
	Student n(2);
	Maker s = m + n;
	cout << s.getId() << s.getAge() << endl;
}

int main()
{
	test02();
	return EXIT_SUCCESS;
}

 Almost all operators in C can be overloaded, but the use of operator overloading is still quite limited, especially operators that are currently meaningless in C cannot be used, operator precedence cannot be changed, and operator parameters cannot be changed.

Third, the minus operator overloading

3.1 Form of member functions

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// Written as a member function, then only one parameter is required, and this parameter is the right side of the plus sign
	Maker operator-(Maker& m)
	{
		Maker t(this->id - m.id,this->age - m.age);
		return t;
	}

private:
	int id;
	int age;
};


void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// The compiler sees the addition of two objects, then the compiler will look for a function called operator+
	Maker m3 = m - n;// operator overloading

	cout << m3.getId() << m3.getAge() << endl;
}

int main()
{
	test01();
	return EXIT_SUCCESS;
}

global overload

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// Written as a member function, then only one parameter is required, and this parameter is the right side of the plus sign
	Maker operator-(Maker& m)
	{
		Maker t(this->id - m.id,this->age - m.age);
		return t;
	}

private:
	int id;
	int age;
};


int operator-(Maker& m, int b)
{
	return m.getId() - b;
}


void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// The compiler sees the addition of two objects, then the compiler will look for a function called operator+
	int m3 = m - 5;// operator overloading

	cout << m3 << endl;
}

int main()
{
	test01();
	return EXIT_SUCCESS;
}

Fourth, the left shift operator overloading

4.1 Global functions

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// Written as a member function, then only one parameter is required, and this parameter is the right side of the plus sign
	Maker operator-(Maker& m)
	{
		Maker t(this->id - m.id,this->age - m.age);
		return t;
	}

private:
	int id;
	int age;
};

// If it is to be used with endl then an ostream object must be returned
opstream& operator<<(ostream& out, Maker& m)
{
	cout << m.getId() << " " << m.getAge() << endl;
    return out;
}


void test01()
{
	Maker m(1,2);
	cout << m;// Overloading the left shift operator
}


int main()
{
	test01();
	return EXIT_SUCCESS;
}

Notice:

  • cout is the object, << is the left shift operator
  • Overloading the left shift operator is to print the object directly
  • Formal and actual parameters are an object
  • Cannot change code in library classes
  • Make copy constructor private in oatream
  • If you want to use endl together, you must return an ostream object

Five, right shift operator overloading

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {
	friend void operator>>(istream& in, Maker& m);
public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// Written as a member function, then only one parameter is required, and this parameter is the right side of the plus sign
	Maker operator-(Maker& m)
	{
		Maker t(this->id - m.id,this->age - m.age);
		return t;
	}

private:
	int id;
	int age;
};

// Declare a global function as a friend function with access to private members
void operator>>(istream& in, Maker& m)
{
	in >> m.id;
	in >> m.age;
}

void test01()
{
	Maker m(1,2);
	cin >> m;
	cout << m.getAge() << " " << m.getId() << endl;
}



int main()
{
	test01();
	return EXIT_SUCCESS;
}

6. Assignment operator overloading

6.1 The default assignment operator overloaded function performs a simple assignment operation

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker()
	{
		this->id = 0;
		this->age = 0;

	}
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}


private:
	int id;
	int age;
};


void test01()
{
	Maker m(1,2);
	Maker m1;

	m1 = m;// The default assignment operator overloaded function performs a simple assignment operation
	cout << m1.getId() << m1.getAge() << endl;
}

int main()
{
	test01();
	return EXIT_SUCCESS;
}

6.2 When the class has a member pointer, then apply for heap space in the constructor and release the heap space in the destructor

When the class has a member pointer, and then apply for heap space in the constructor and release the heap space in the destructor, the same space will be released twice, and then the memory leaks, so it is necessary to rewrite the assignment operator overloaded function.

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Student {


public:
	Student(const char* name)
	{
		pName = new char[strlen(name) + 1];// Request heap space
		strcpy(pName,name);// copy content
	}

	// Override assignment operator overloaded function
	Student& operator=(const Student& stu)
	{
		// Not sure whether the space pointed to by this->pName can hold the data in stu, so first release the space pointed to by this->pNmae
		if (this->pName != NULL)
		{
			delete this->pName;
			this->pName = NULL;
		}

		// The size of the application space is determined by stu
		this->pName = new char[strlen(stu.pName) + 1];
		strcpy(this->pName,stu.pName);

		// return the object itself
		return *this;
	}

	~Student()
	{
		if (pName != NULL)
		{
			delete[] pName;
			pName = NULL;
		}
	}

	void printName()
	{
		cout << pName << endl;
	}


private:
	char* pName;
};

void test01()
{
	Student s1("Sun Wukong");
	Student s2("hhhh");

	s1.printName();
	s2.printName();

	s1 = s2;// The assignment operator needs to be overloaded, otherwise a memory leak will occur

	s1.printName();
	s2.printName();


}

int main()
{
	test01();
	return EXIT_SUCCESS;
}

Seven, relational operators

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker()
	{
		this->id = 0;
		this->age = 0;

	}
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	// Relational operator overloading
	bool operator==(Maker& m)
	{
		if (this->id == m.getId() && this->age == m.getAge())
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}


private:
	int id;
	int age;
};


void test01()
{
	Maker m(1,2);
	Maker m1(2,3);

	if (m1 == m)
	{
		cout << "same" << endl;
	}
	else
	{
		cout << "Are not the same" << endl;
	}
}



int main()
{
	test01();
	return EXIT_SUCCESS;
}

Eight, pre-add operator and post-add operator

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

	friend ostream& operator<<(ostream& out, Maker& m);

public:

	// default constructor
	Maker()
	{
		this->a = 0;
	}

	Maker(int a)
	{
		this->a = a;
	}

	// reload prepend
	Maker& operator++()
	{
		// Need to return a reference, otherwise it will return a new Maker object
		++this->a;
		return *this;
	}

	// Reload post plus plus
	Maker operator++(int)
	{	
		// post add add first return then add
		Maker tmp(*this);// The value a in *this is equal to 2 and the copy constructor is called 
		++this->a;// add after
		return tmp;
	}

	int getA()
	{
		return this->a;
	}

private:
	int a;

};

ostream& operator<<(ostream& out, Maker& m)
{
	//cout << m.getA() << endl;
	cout << m.a<< endl;// Declare a friend function to access private member variables

	return out;
}

void test01()
{
	Maker m1(1);
	cout << m1 << endl;
	cout << ++m1 << endl;

	cout << m1++ << endl;// Returns the temp object local object
	cout << m1 << endl;
}



int main()
{
	test01();
	return EXIT_SUCCESS;
}

9. Array subscript overloading

main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include<string>
#include"MyArray.h";

void printMyArray(MyArray& arr)
{
	for (int i = 0; i < arr.Size(); i++)
	{
		cout << arr.Get(i) << " ";
	}
	cout << endl;
}

void test01()
{
	MyArray arr;

	for (int i = 0; i < 20; i++)
	{

		arr[i] = i + 10;
	}

	MyArray arr2;
	arr2 = arr;

	for (int i = 0; i < 20; i++)
	{

		cout<<arr2[i] << endl;
	} 
}


int main()
{

	test01();

	return EXIT_SUCCESS;
}

MyArray.h

#pragma once
class MyArray
{
public:
	MyArray();// No-argument constructor declaration
	MyArray(const MyArray& arr);// copy constructor declaration
	MyArray(int capacity, int val = 0);// Argument constructor

	// Override assignment operator overloaded function
	MyArray& operator=(const MyArray& m);

	// overloaded array subscript
	int& operator[](int index);


	~MyArray();// destructor

	//head plug
	void PushFront(int val);

	//tail plug
	void PushBack(int val);

	// header removal
	void PopFront();

	// tail deletion
	void PopBack();

	// Get the number of elements in an array
	int Size();

	// get array capacity
	int Capacity();

	// Insert element at specified position
	void Insert(int pos, int val);

	// Get the value at the specified location
	int& Get(int pos);

	// Modify the value at the specified location
	void Set(int pos, int val);

private:
	int* pArray;// Point to heap space, store data = new int[this->mCapacity]; Create heap array
	int mSize;// number of elements
	int mCapacity;// capacity
};

MyArray.cpp

#include "MyArray.h"
#include<iostream>
using namespace std;

MyArray::MyArray()
{
	this->mCapacity = 20;
	this->mSize = 0;

	// Create heap array
	this->pArray = new int[this->mCapacity];

	// All array elements are initialized to 0
	for (int i = 0; i < this->mCapacity; i++)
	{
		this->pArray[i] = 0;
	}
}

MyArray::~MyArray()
{
	if (this->pArray == NULL)
	{
		delete[] this->pArray;// free array space
		this->pArray = NULL;
	}
}

// Overriding assignment operator overloading
MyArray& MyArray::operator=(const MyArray& m)
{
	// First free up the original space
	if (this->pArray != NULL)
	{
		delete[] this->pArray;
		this->pArray = NULL;
	}

	this->mCapacity = m.mCapacity;
	this->mSize = m.mSize;

	// application space  
	this->pArray = new int[m.mCapacity];

	// copy data
	for (int i = 0; i < this->mCapacity; i++)
	{
		this->pArray[i] = m.pArray[i];
	}

	return *this;
}


// overloaded array subscript
int& MyArray::operator[](int index)
{
	// Add when assigning
	this->mSize++;
	return this->pArray[index];
}



// Implementation of copy constructor
MyArray::MyArray(const MyArray& arr)
{
	this->mCapacity = arr.mCapacity;
	this->mSize = arr.mSize;

	// application space
	this->pArray = new int[arr.mCapacity];

	// copy data
	for (int i = 0; i < this->mSize; i++)
	{
		this->pArray[i] = arr.pArray[i];
	}
}

// Implementations cannot have default parameters
MyArray::MyArray(int capacity, int val)
{
	this->mCapacity = capacity;
	this->mSize = capacity;
	this->pArray = new int[capacity];
	for (int i = 0; i < this->mSize; i++)
	{
		this->pArray[i] = val;
	}
}

//head plug
void MyArray::PushFront(int val)
{
	if (this->mSize == this->mCapacity)
	{
		return;// array is full
	}

	// If not full, move the element backward from the last element of the array
	for (int i = this->mSize - 1; i >= 0; i--)
	{
		this->pArray[i + 1] = this->pArray[i];
	}

	// vacate the first position
	this->pArray[0] = val;

	// Array length plus one
	this->mSize++;
}

//tail plug
void MyArray::PushBack(int val)
{
	if (this->mSize == this->mCapacity)
	{
		return;// array element full
	}

	this->pArray[this->mSize] = val;
	this->mSize++;// Array length plus one
}

// header removal
void MyArray::PopFront()
{
	if (this->mSize == 0)
	{
		return;// array element is 0
	}

	// The latter number is moved forward to cover the first element
	for (int i = 0; i < this->mSize; i++)
	{
		this->pArray[i] = this->pArray[i + 1];
	}

	this->mSize--;
}

// tail deletion
void MyArray::PopBack()
{
	// This can only be deleted logically
	if (this->mSize == 0)
	{
		return;
	}
	this->mSize--;
}

// Get the number of elements in an array
int MyArray::Size()
{
	return this->mSize;
}

// get array capacity
int MyArray::Capacity()
{
	return this->mCapacity;
}


// Insert element at specified position
void MyArray::Insert(int pos, int val)
{
	// First determine whether the capacity is full
	if (this->mSize == this->mCapacity)
	{
		return;
	}

	// If the position is wrong, insert the tail
	if (pos < 0 || pos > this->mSize - 1)
	{
		pos = this->mSize;
	}

	for (int i = this->mSize - 1; i >= pos; i--)
	{
		this->pArray[i + 1] = this->pArray[i];
	}

	this->pArray[pos] = val;
	this->mSize++;
}

// Get the value at the specified location
int& MyArray::Get(int pos)
{
	return this->pArray[pos];
}

// Modify the value at the specified location
void MyArray::Set(int pos, int val) {

	if (pos < 0 || pos > this->mCapacity - 1)
	{
		return;
	}

	this->pArray[pos] = val;
}

Tags: C++ Algorithm leetcode programming language

Posted by rdhatt on Fri, 07 Oct 2022 11:27:09 +0300