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; }