1. Function overload
There is no way for the compiler to confirm which function is called by returning a value.
code:
#include<iostream> using namespace std; int func(int x) { return 2 * x; } double func(double x) { return x * x; } int func(int x, int y) { return x * y; } int main() { cout << func(2) << endl;//func(int) cout << func(2.3) << endl; // func(double) cout << func(2, 4) << endl; // func(int, int) return 0; }
Conflict form: parameters have default values
In this case, you can combine the two functions:
#include<iostream> using namespace std; //Two call forms //func(int) //func(int, int) int func(int x, int y = 10) { return x * y; } double func(double x) { return x * x; } int main() { cout << func(2) << endl;//func(int) cout << func(2.3) << endl; // func(double) cout << func(2, 4) << endl; // func(int, int) return 0; }
Function overload cannot be distinguished by return value:
2. Significance of heavy load
3. Friends
If a function outside the class wants to access members inside the class, it needs to be declared as a friend function of the class.
The form is as follows:
class A { private : int a; int b; public : A() : a(0), b(0) {} A(int a, int b): a(a), b(b) {} A(const A &obj): a(obj.a), b(obj.b) {} friend A add(const A &obj1, const A &obj2); }; A add(const A &obj1, const A &obj2) { A ret(0, 0); ret.a = obj1.a + obj2.a; ret.b = obj1.b + obj2.b; return ret; }
example:
#include<iostream> using namespace std; class Data { public: Data() {} Data(int x, int y) : x(x), y(y) { cout << "Data : " << this << endl; } friend ostream &operator<<(ostream &out, const Data &d); private: int x, y; }; ostream &operator<<(ostream &out, const Data &d) { //To access the private property of Data, you need to declare the function as friend in the Data class out << d.x << " " << d.y; return out; } int main() { Data d(10, 9); //Reload < < cout << d << endl; return 0; }
A friend function is essentially a function outside the class. Although it is still a function outside the class after being declared as a friend function, it can access the private properties inside the class.
4. Operator overloading (non member)

4.1 what symbols can be overloaded?

sizeof is also an operator and cannot be overloaded, but it is usually not considered an operator, but a function.
Note: new and delete are operators and malloc is a function.
4.2 external heavy load
#include<iostream> using namespace std; class Point { public: Point(); Point(int x, int y); private: friend Point operator+(const Point &a, const Point &b); friend ostream &operator<<(ostream &out, const Point &p); int x, y; }; //Delegate Construction: the Point(int, int) constructor is called for construction Point::Point() : Point(0, 0) {} Point::Point(int x, int y) : x(x), y(y) {} //Out of class overload + number //The first parameter represents the variable to the left of the + sign, and the second represents the variable to the right of the + sign Point operator+(const Point &a, const Point &b) { Point c(a.x + b.x, a.y + b.y); return c; } //Overload < < operator //The first parameter represents the variable on the left of < < operator, and the second represents the variable on the right of < < operator ostream &operator<<(ostream &out, const Point &p) { out << "(" << p.x << ", " << p.y << ")"; return out; } int main() { Point a(3, 4); Point b(7, 9); Point c = a + b; cout << a << endl; cout << b << endl; cout << c << endl; return 0; } //Output results: //(3, 4) //(7, 9) //(10, 13)
Q: what is a delegate construct?
Answer:
Point::Point() : x(0), y(0) {} Point::Point(int x, int y) : x(x), y(y) {}
The second line in the code assigns values to x and Y respectively, and the first line assigns values to both x and y as 0, so line 1 can delegate 2 lines to construct:
Point::Point() : Point(0, 0) {} Point::Point(int x, int y) : x(x), y(y) {}
4.3 in class heavy load
#include<iostream> using namespace std; class Point { public: Point(); Point(int x, int y); //Overloaded operator + in class, only one parameter needs to be passed in //Because the object pointed to by this pointer represents the parameter on the left of + and the passed in parameter a represents the parameter on the right of + Point operator+(const Point &a); private: friend Point operator+(const Point &a, const Point &b); friend ostream &operator<<(ostream &out, const Point &p); int x, y; }; Point::Point() : Point(0, 0) {} Point::Point(int x, int y) : x(x), y(y) {} //In class overloading+ Point Point::operator+(const Point &a) { cout << "inner operator+" << endl; Point c(x + a.x, y + a.y); return c; } //Out of class overload+ Point operator+(const Point &a, const Point &b) { cout << "outer operator+" << endl; Point c(a.x + b.x, a.y + b.y); return c; } ostream &operator<<(ostream &out, const Point &p) { out << "(" << p.x << ", " << p.y << ")"; return out; } int main() { Point a(3, 4); Point b(7, 9); Point c = a + b; cout << a << endl; cout << b << endl; cout << c << endl; return 0; }
There is no conflict between out of class overloads and in class overloads at the same time, because the priority of in class overloads is higher, and in class overloads will be called when they exist at the same time.
4.4 principles of overloading member functions
- Assignment (=), subscript ([]), call (()), member access (- >) operators must be overloaded as member functions
- Compound assignment operators are usually overloaded as member functions
- Operators that change the operation state of an object and operators whose types are closely related are generally overloaded as member functions, such as self increment
- Operators with symmetry may convert objects at either end, and are usually overloaded with non member functions, such as equality, relationship and bit operation
4.5 overload + = operator
#include<iostream> using namespace std; class Point { public: Point(); Point(int x, int y); //Overloaded operator + in class, only one parameter needs to be passed in //Because the object pointed to by this pointer represents the parameter on the left of + and the passed in parameter a represents the parameter on the right of + Point operator+(const Point &a); //Overload + = operator Point &operator+=(int); private: friend Point operator+(const Point &a, const Point &b); friend ostream &operator<<(ostream &out, const Point &p); int x, y; }; Point::Point() : Point(0, 0) {} Point::Point(int x, int y) : x(x), y(y) {} Point &Point::operator+=(int n) { x += n, y += n; return *this; } //In class overloading+ Point Point::operator+(const Point &a) { cout << "inner operator+" << endl; Point c(x + a.x, y + a.y); return c; } //Out of class overload+ Point operator+(const Point &a, const Point &b) { cout << "outer operator+" << endl; Point c(a.x + b.x, a.y + b.y); return c; } ostream &operator<<(ostream &out, const Point &p) { out << "(" << p.x << ", " << p.y << ")"; return out; } int main() { Point a(3, 4); Point b(7, 9); Point c = a + b; cout << a << endl; cout << b << endl; cout << c << endl; a += 2; //Give x and y of a + 2 each cout << a << endl; return 0; }
Note these two lines of code:
Point operator+(const Point &a); Point &operator+=(int);
Why does the overloaded + = operator return a reference? Does the overloaded + operator return a new value?
a += 2; it acts on a.
Logically speaking, this expression returns the value of a, so it returns a reference;
Functionally, if you add (a += 2) += 2 continuously, you must return a reference. Only the previous + = expression returns a reference, and the latter + = will continue to act on object a.
The return value of the operator is arbitrary and there is no requirement to return a reference.
4.6 overloading of [] operator
[] the overloaded object is called = = array object = =;
[] only supports = = in class overloading==
#include<iostream> using namespace std; class Point { public: Point(); Point(int x, int y); //Overloaded operator + in class, only one parameter needs to be passed in //Because the object pointed to by this pointer represents the parameter on the left of + and the passed in parameter a represents the parameter on the right of + Point operator+(const Point &a); //Overload + = operator Point &operator+=(int); int operator[](string s); private: friend Point operator+(const Point &a, const Point &b); friend ostream &operator<<(ostream &out, const Point &p); int x, y; }; Point::Point() : Point(0, 0) {} Point::Point(int x, int y) : x(x), y(y) {} int Point::operator[](string s) { if (s == "x") return x; if (s == "y") return y; return 0; } Point &Point::operator+=(int n) { x += n, y += n; return *this; } //In class overloading+ Point Point::operator+(const Point &a) { cout << "inner operator+" << endl; Point c(x + a.x, y + a.y); return c; } //Out of class overload+ Point operator+(const Point &a, const Point &b) { cout << "outer operator+" << endl; Point c(a.x + b.x, a.y + b.y); return c; } ostream &operator<<(ostream &out, const Point &p) { out << "(" << p.x << ", " << p.y << ")"; return out; } int main() { Point a(3, 4); Point b(7, 9); cout << a["x"] << endl; cout << a["y"] << endl; Point c = a + b; cout << a << endl; cout << b << endl; cout << c << endl; a += 2; //Give x and y of a + 2 each cout << a << endl; return 0; }
Because the Point class overloads the [] operator, all objects of the Point class support the [] operator, so you can add the [] operator after the a object.
The external representation of an object is like an array, but its essence is still an object. Therefore, the object generated by the class overloaded with the [] operator is called an array object, that is, the external representation is an array, but it is still an object in essence.
4.7 overloading of () operator
() the overloaded object is called a function object, also known as = = imitation function = =;
#include<iostream> using namespace std; class ADD { public : ADD(int c) : c(c) {} int operator()(int a, int b) { return a + b + c; } private : int c; }; int main() { ADD add(5); cout << add(6, 7) << endl; //Output 18 return 0; }
The add object behaves like a function, but it is essentially an object, so it is called a function object, also called a callable object, also known as an imitation function.
4.8 - > overloading of operators
->(indirect reference) overloaded objects are called pointer objects.
#include<iostream> using namespace std; class Point { public : Point(); Point(int x, int y); //Overloaded operator + in class, only one parameter needs to be passed in //Because the object pointed to by this pointer represents the parameter on the left of + and the passed in parameter a represents the parameter on the right of + Point operator+(const Point &a); //Overload + = operator Point &operator+=(int); //Overload [] operator int operator[](string s); int getX() { return x; } int getY() { return y; } private : friend Point operator+(const Point &a, const Point &b); friend ostream &operator<<(ostream &out, const Point &p); int x, y; }; class PPoint { public : PPoint(Point *p) : p(p) {} Point *operator->() { return p; } //->Operator overloading usually returns a pointer private: Point *p; }; Point::Point() : Point(0, 0) {} Point::Point(int x, int y) : x(x), y(y) {} int Point::operator[](string s) { if (s == "x") return x; if (s == "y") return y; return 0; } Point &Point::operator+=(int n) { x += n, y += n; return *this; } //In class overloading+ Point Point::operator+(const Point &a) { cout << "inner operator+" << endl; Point c(x + a.x, y + a.y); return c; } //Out of class overload+ Point operator+(const Point &a, const Point &b) { cout << "outer operator+" << endl; Point c(a.x + b.x, a.y + b.y); return c; } ostream &operator<<(ostream &out, const Point &p) { out << "(" << p.x << ", " << p.y << ")"; return out; } int main() { Point a(3, 4); Point b(7, 9); cout << a["x"] << endl; cout << a["y"] << endl; Point c = a + b; cout << a << endl; cout << b << endl; cout << c << endl; a += 2; //Give x and y of a + 2 each cout << a << endl; PPoint p = &a; //The conversion constructor was called cout << p->getX() << " " << p->getY() << endl; return 0; }
Object p behaves like a pointer, but is essentially an object, so it is called an object pointer.
4.9 implementation of intelligent pointer object
Intelligent pointer leading knowledge
The smart pointer shared_ptr is defined in the header file < memory >.
Code that caused a memory leak:
#include <iostream> #include <memory> using namespace std; class A { public : A() { cout << "default constructor" << endl; } ~A() { cout << "destructor" << endl; } }; int main() { A *p1 = new A(); p1 = nullptr; return 0; }
Because the object p1 has not been destroyed, the address of the object is not known anywhere in the system where the data is stored, that is, a storage area belongs to the current process, but the current process can never find where the storage area is.
Using ordinary pointers, there is only a construction process and no deconstruction process, so a memory leak occurs.
However, using smart pointers does not cause memory leaks:
#include <iostream> #include <memory> using namespace std; class A { public : A() { cout << "default constructor" << endl; } ~A() { cout << "destructor" << endl; } }; int main() { A *p1 = new A(); p1 = nullptr; //p2 is the smart pointer object shared_ptr<A> p2(new A()); p2 = nullptr; //Automatic deconstruction return 0; }
Operation results:
default constructor default constructor destructor
Intelligence of smart pointer: when there is no pointer to the current object, the smart pointer will destruct the related object.
Principle of smart pointer: there is a reference count inside, which will record the number of smart pointers pointing to the current object.
#include <iostream> #include <memory> using namespace std; class A { public : A() { cout << "default constructor" << endl; } ~A() { cout << "destructor" << endl; } }; int main() { A *p1 = new A(); p1 = nullptr; //p2 is the smart pointer object shared_ptr<A> p2(new A()); //Output reference count: how many pointers to the object pointed to by p2 point to it cout << p2.use_count() << endl; // 1, only p2 shared_ptr<A> p3 = p2; cout << p2.use_count() << endl; // 2, p2 and p3 p2 = nullptr; //Automatic deconstruction cout << p3.use_count() << endl; // 1, only p3 return 0; }
Operation results:
default constructor default constructor 1 2 1 destructor
The last reason to call destructor is that after the execution of the main function, the p3 object is automatically recovered. The number of smart pointers to the current object becomes 0, and the reference count becomes 0, when the object is destructed by the intelligent pointer.
The smart pointer behaves like a pointer because it overloads the - > operator and the * operator:
#include <iostream> #include <memory> using namespace std; class A { public : A() { cout << "default constructor" << endl; } int x, y; ~A() { cout << "destructor" << endl; } }; int main() { A *p1 = new A(); p1 = nullptr; //p2 is the smart pointer object shared_ptr<A> p2(new A()); //Output reference count: how many pointers to the object pointed to by p2 point to it cout << p2.use_count() << endl; // 1 shared_ptr<A> p3 = p2; p2->x = 123; p2->y = 456; (*p2).x = 456; cout << p2.use_count() << endl; // 2 p2 = nullptr; //Automatic deconstruction cout << p3.use_count() << endl; // 1 return 0; }
Implement smart pointer object
#include <iostream> #include <memory> using namespace std; namespace haizei { class A { public : A() { cout << "default constructor" << endl; } int x, y; ~A() { cout << "destructor" << endl; } }; class shared_ptr { public : shared_ptr(); shared_ptr(A *); shared_ptr(const shared_ptr &); int use_count(); A *operator->(); A &operator*(); //The reason why the assignment operator returns the reference of the current object is to support the operation of concatenation shared_ptr &operator=(const shared_ptr &); ~shared_ptr(); private : int *cnt; //Reference count A *obj; }; shared_ptr::shared_ptr() : obj(nullptr), cnt(nullptr) {} shared_ptr::shared_ptr(A *obj) : obj(obj), cnt(new int(1)) {} shared_ptr::shared_ptr(const shared_ptr &p) : obj(p.obj), cnt(p.cnt) { *p.cnt += 1; } int shared_ptr::use_count() { return cnt ? *cnt : 0; } A *shared_ptr::operator->() { return obj; } A &shared_ptr::operator*() { return *obj; } shared_ptr::~shared_ptr() { if (cnt != nullptr) { *cnt -= 1; if (*cnt == 0) { delete obj; delete cnt; } obj = nullptr; cnt = nullptr; } } shared_ptr &shared_ptr::operator=(const shared_ptr &p) { //Determine whether to point to the same object if (this->obj != p.obj) { if (this->cnt != nullptr) { *(this->cnt) -= 1; if (*(this->cnt) == 0) { delete this->obj; delete this->cnt; } } this->obj = p.obj; this->cnt = p.cnt; if (this->cnt != nullptr) { *(this->cnt) += 1; } } return *this; } } // end of haizei int main() { haizei::A *p1 = new haizei::A(); p1 = nullptr; //p2 is the smart pointer object haizei::shared_ptr p2(new haizei::A()); cout << p2.use_count() << endl; // 1 haizei::shared_ptr p3 = p2; p2->x = 123; p2->y = 456; (*p2).x = 456; cout << p2.use_count() << endl; // 2 p2 = nullptr; //Automatic deconstruction cout << p3.use_count() << endl; // 1 p2 = p3; cout << p2.use_count() << endl; // 2 return 0; }
The implementation of the operator = function is garbage, so it is changed as follows:
#include <iostream> #include <memory> using namespace std; namespace haizei { class A { public : A() { cout << "default constructor" << endl; } int x, y; ~A() { cout << "destructor" << endl; } }; class shared_ptr { public : shared_ptr(); shared_ptr(A *); shared_ptr(const shared_ptr &); int use_count(); A *operator->(); A &operator*(); //The reason why the assignment operator returns the reference of the current object is to support the operation of concatenation shared_ptr &operator=(const shared_ptr &); ~shared_ptr(); private : void decrease_by_one(); void increase_by_one(); int *cnt; //Reference count A *obj; }; shared_ptr::shared_ptr() : obj(nullptr), cnt(nullptr) {} shared_ptr::shared_ptr(A *obj) : obj(obj), cnt(new int(1)) {} shared_ptr::shared_ptr(const shared_ptr &p) : obj(p.obj), cnt(p.cnt) { increase_by_one(); } int shared_ptr::use_count() { return cnt ? *cnt : 0; } A *shared_ptr::operator->() { return obj; } A &shared_ptr::operator*() { return *obj; } void shared_ptr::decrease_by_one() { if (this->cnt != nullptr) { *(this->cnt) -= 1; if (*(this->cnt) == 0) { delete this->obj; delete this->cnt; } } return ; } void shared_ptr::increase_by_one() { if (cnt != nullptr) { *cnt += 1; //cnt[0] += 1; } return ; } shared_ptr::~shared_ptr() { this->decrease_by_one(); this->obj = nullptr; this->cnt = nullptr; } shared_ptr &shared_ptr::operator=(const shared_ptr &p) { //Determine whether to point to the same object if (this->obj != p.obj) { decrease_by_one(); //Reference count - 1 //Copy Object obj = p.obj; cnt = p.cnt; increase_by_one(); //Count new references + 1 } return *this; } } // end of haizei int main() { haizei::A *p1 = new haizei::A(); p1 = nullptr; //p2 is the smart pointer object haizei::shared_ptr p2(new haizei::A()); cout << p2.use_count() << endl; // 1 haizei::shared_ptr p3 = p2; p2->x = 123; p2->y = 456; (*p2).x = 456; cout << p2.use_count() << endl; // 2 p2 = nullptr; //Automatic deconstruction cout << p3.use_count() << endl; // 1 p2 = p3; cout << p2.use_count() << endl; // 2 return 0; }
Operation results:
default constructor default constructor 1 2 1 2 destructor
So far, the implementation of a simple smart pointer has been completed.
4.10 calling object
Sort the array:
#include <iostream> #include <vector> #include <algorithm> using namespace std; ostream &operator<<(ostream &out, const vector<int> &a) { for (auto x : a) { out << x << " "; } return out; } int main() { vector<int> arr; int n; cin >> n; while (n--) { int a; cin >> a, arr.push_back(a); } sort(arr.begin(), arr.end()); cout << arr << endl; return 0; }
The sort function sorts from small to large by default. If you want to sort from large to small, you usually add a user-defined comparison function:
#include <iostream> #include <vector> #include <algorithm> using namespace std; ostream &operator<<(ostream &out, const vector<int> &a) { for (auto x : a) { out << x << " "; } return out; } bool cmp1(int a, int b) { return a > b; //When a > b, a is in front of B, that is, from large to small } int main() { vector<int> arr; int n; cin >> n; while (n--) { int a; cin >> a, arr.push_back(a); } sort(arr.begin(), arr.end(), cmp1); cout << arr << endl; return 0; }
After learning the imitation function, you can also use the imitation function to implement the user-defined comparison rules:
#include <iostream> #include <vector> #include <algorithm> using namespace std; ostream &operator<<(ostream &out, const vector<int> &a) { for (auto x : a) { out << x << " "; } return out; } bool cmp1(int a, int b) { return a > b; //When a > b, a is in front of B, that is, from large to small } class CMP { public : bool operator()(int a, int b) { return a > b; } }; int main() { vector<int> arr; int n; cin >> n; while (n--) { int a; cin >> a, arr.push_back(a); } //sort(arr.begin(), arr.end(), cmp1); sort(arr.begin(), arr.end(), CMP());//The CMP() constructor constructs an anonymous object. The third position should be the function name, and the function name of the imitation function is the object name cout << arr << endl; return 0; }
The function of a functor is determined by the overloaded () operator.
The above can also be rewritten as follows:
CMP cmp2; sort(arr.begin(), arr.end(), cmp2);
Imitation functions are more powerful than ordinary functions: different comparison rules are used according to different z values
#include <iostream> #include <vector> #include <algorithm> using namespace std; ostream &operator<<(ostream &out, const vector<int> &a) { for (auto x : a) { out << x << " "; } return out; } bool cmp1(int a, int b) { return a > b; //When a > b, a is in front of B, that is, from large to small } class CMP { public : CMP(int z = 0) : z(z) {} //z = 0 less, z = 1 greater bool operator()(int a, int b) { return (a < b) ^ !!(z); } int z; }; int main() { vector<int> arr; int n; cin >> n; while (n--) { int a; cin >> a, arr.push_back(a); } sort(arr.begin(), arr.end(), CMP());//CMP() is an anonymous object. The third place should be the function name, and the function name of the imitation function is the object name cout << arr << endl; return 0; }
sort(arr.begin(), arr.end(), CMP()); The default is to sort from small to large; sort(arr.begin(), arr.end(), CMP(1)); It's from big to small.
4.11 implement your own sort function
#include <iostream> #include <vector> #include <algorithm> using namespace std; ostream &operator<<(ostream &out, const vector<int> &a) { for (auto x : a) { out << x << " "; } return out; } bool cmp1(int a, int b) { return a > b; //When a > b, a is in front of B, that is, from large to small } namespace haizei { class CMP { public : CMP(int z = 0) : z(z) {} //z = 0 less, z = 1 greater bool operator()(int a, int b) { return (a < b) ^ !!(z); } int z; }; //cmp is a function pointer object, which is essentially an object and behaves like a function pointer. The return value is bool and the incoming parameters are two ints void sort(int *arr, int l, int r, function<bool(int, int)> cmp = CMP()) { //Quick sort if (l >= r) return ; int x = l, y = r, z = arr[(l + r) >> 1]; do { while (cmp(arr[x], z)) ++x; while (cmp(z, arr[y])) --y; if (x <= y) { swap(arr[x], arr[y]); ++x, --y; } } while (x <= y); sort(arr, l, y, cmp); sort(arr, x, r, cmp); return ; } }; //end of haizei int main() { vector<int> arr; int n; cin >> n; while (n--) { int a; cin >> a, arr.push_back(a); } //sort(arr.begin(), arr.end(), cmp1); sort(arr.begin(), arr.end(), haizei::CMP());//CMP() is an anonymous object. The third place should be the function name, and the function name of the imitation function is the object name cout << arr << endl; // ====Use your own sort function int arr2[5] = {6, 8, 4, 5, 1}; haizei::sort(arr2, 0, 4); //The default is from small to large for (int i = 0; i < 5; i++) { cout << arr2[i] << " "; } cout << endl; //From large to small: using custom functions haizei::sort(arr2, 0, 4, cmp1); for (int i = 0; i < 5; i++) { cout << arr2[i] << " "; } cout << endl; haizei::sort(arr2, 0, 4); //The default is from small to large for (int i = 0; i < 5; i++) { cout << arr2[i] << " "; } cout << endl; //From large to small row: using affine functions haizei::sort(arr2, 0, 4, haizei::CMP(1)); for (int i = 0; i < 5; i++) { cout << arr2[i] << " "; } cout << endl; return 0; }
Operation results:
5 #input 1 4 2 0 9 #input 0 1 2 4 9 #output 1 4 5 6 8 #output 8 6 5 4 1 #output 1 4 5 6 8 #output 8 6 5 4 1 #output
The reason why the system sort function can pass in callable objects is that the template class function is used.