catalogue
I What is operator overloading
II How operators are overloaded
2. Class member function overloading
III Special operator overloading
2. Self increasing and self decreasing operator overloading and text overloading
(1) Self increasing and self decreasing
(3) Implicit conversion of class
I What is operator overloading
Operator overloading is to give operators the function of operating user-defined type data. It is essentially a function call.
Composition of operator overloaded function:
Function return value: determined by the value completed after operation. Function name: operator plus the operator you want to overload. Parameters: determined by the operands of the operator and the overloading method.
II How operators are overloaded
1. Friend overload
The number of function parameters is the operand of the operator. Let's take the implementation of "+" as an example:
class Complex { public: Complex(int a,int b):a(a),b(b){} void print() { cout << a << " " << b << endl; } friend Complex operator+(Complex one, Complex two) { return Complex(one.a + two.a, one.b + two.b); } protected: int a; int b; }; int main() { Complex a(1, 1); Complex b(2, 0); Complex c = a + b; c.print(); }
Note that "c = a + b" we see is an implicit call of operator overloading, and the explicit call is as follows:
Complex c = operator+(a, b);
In fact, it is no different from ordinary functions.
2. Class member function overloading
The operator overloading is realized by the way of class member function, and the number of parameters is equal to the operator operand - 1. Because the class member function is essentially something in the class and needs to be called with an object, there is naturally one less parameter. Let's take the implementation of ">" as an example:
bool operator>(Complex object) { if (this->a > object.a) return true; else if (this->a == object.a && this->b > object.b) return true; else return false; }
In this way, you can compare objects.
III Special operator overloading
1. Stream operator
cin type: object of istream class.
cout type: object of ostream class.
Stream operators: < <, > >.
friend void operator>>(istream& in, A& a) { in >> a.name >> a.age; } friend void operator<<(ostream& out, A& a) { out << a.name << '\t' << a.age << endl; }
According to the above description, we can implement the input and output of class A defined by us, but this implementation is limited. Because the returned type is void, we can't do continuous input and output. How can we optimize this?
class A { public: A(string name = "", int age = 10) :name(name), age(age) {} friend istream& operator>>(istream& in, A& a) { in >> a.name >> a.age; cout << endl; return in; } friend ostream& operator<<(ostream& out, A& a) { out << a.name << '\t' << a.age << endl; return out; } protected: string name; int age; }; int main() { A a("Blue ", 18); A b("Little green", 19); A c; A d; cin >> c >> d; cout << a << b << c << d; }
As shown in the figure, when we modify the return value to the reference types of istream and ostream, we can realize continuous input and output.
2. Self increasing and self decreasing operator overloading and text overloading
(1) Self increasing and self decreasing
The overload of self increasing and self decreasing operator needs to solve the problem of pre and post, and a useless parameter needs to be added to indicate that the current operator is a post operation.
A operator++(int) { return A(name, age++); } A operator++() { return A(name, ++age); }
Let's write some test cases:
class A { public: A(string name = "", int age = 10) :name(name), age(age) {} friend ostream& operator<<(ostream& out, A& a) { out << a.name << '\t' << a.age << endl; return out; } A operator++(int) { return A(name, age++); } A operator++() { return A(name, ++age); } protected: string name; int age; }; int main() { A a("Blue ", 18); A object = a++; cout << object << a; object = ++a; cout << object << a; }
It can be seen that we have realized the self increment and self subtraction of user-defined types.
(2) Text overload
Fixed return value type:
unsigned long long
Fixed function name:
operator""
Fixed parameter type:
unsigned long long
Generally, text overloading can be used when multiple unit conversions are required. An example of conversion time is given below:
unsigned long long operator"" _h(unsigned long long num) { return num * 60 * 60; } unsigned long long operator"" _min(unsigned long long num) { return num * 60; } unsigned long long operator"" _s(unsigned long long num) { return num; } int main() { int second = 1_h + 10_min + 20_s; cout << second << "s"; }
(3) Implicit conversion of class
When defining a new object, there is no way to assign it directly to a basic data type -- > 1 Overloaded operator 2 Implicit conversion
class A { public: A(string name ,int age):name(name),age(age){} //Implicit conversion of objects of class operator int() { return age; } protected: string name; int age; }; int main() { A a("a", 18); int age= a; cout << age<< endl; return 0; }
IV Operator overload case
1. Handwriting dynamic array
Step 1: design basic elements
class vector { public: protected: int size; //Number of current elements int* base; //storage space int capacity; //capacity };
Step 2: write constructor and destructor
vector(int capacity) :capacity(capacity) { if (capacity > 0) { base = new int[capacity]; this->capacity = capacity; this->size = 0; } } ~vector() { if (base) { delete[] base; base = nullptr; } }
Step 3: write two magic oil functions
bool empty() const { return size == 0; } int getsize()const { return size; }
Step 4: implement subscript access
int& operator[](int index) { if (index >= 0 && index < capacity) { return base[index]; } }
Step 5: then realize dynamic array growth
void push_back(int data) { if (size == capacity) { int* newbase = new int[capacity * 2]; this->capacity *= capacity; for (int i = 0; i < size; i++) { newbase[i] = base[i]; } delete[] base; base = newbase; } base[size++] = data; }
Finally, write some debugging code and try it!
int main() { vector a(3); for (int i = 0; i < 5; i++) { a.push_back(i); cout << a[i] << " "; } cout << endl << a.getsize(); }
As you can see, the result is the same as we want!
2. Packing
It's relatively simple here. Paste the code directly, and the comments have been added to the code.
class Int { public: Int(int num) :num(num) {} int& data() { return num; //get data } string tostr() { return to_string(num); //Convert int to string type } //Arithmetic operator overloading Int operator+(const Int& value) //+Method of overloading member functions in object addition classes { return Int(this->num + value.num); } //Friend overloading: operand = = number of parameters of overloaded function friend Int operator-(const Int& one, const Int& two) { return Int(one.num - two.num); } Int operator+=(const int& value) //Original value + = value of type int { return Int(this->num + value); } Int operator+=(const Int& value) //The original value + = an object forms an overload | the parameter types are inconsistent { return Int(this->num + value.num); } Int operator++(int) { return Int(this->num++); } Int operator++() { return Int(++this->num); } //Bitwise operator overloading Int operator&(const Int& value) { return Int(this->num & value.num); } bool operator!() { return !this->num; } //minus sign Int operator-() { return Int(-this->num); } //Stream overload friend ostream& operator<<(ostream& out, const Int& object) { out << object.num << endl; return out; } friend istream& operator>>(istream& in, Int& object) { in >> object.num; return in; } //Address character overload int* operator&() { return &this->num; //You don't need to return an object, just return an address of type int * } //Relational operator overloading bool operator>(const Int& object) { return this->num > object.num; } protected: int num; //Data or int type };
3. Other heavy loads
Reload ():
class Function { typedef void(*PF)(); public: Function(PF pf) :pf(pf){} void operator()() { pf(); } protected: PF pf; }; void print() { cout << "Reload successful"; } int main() { Function f(print); f(); }
Overload - >:
class A { public: A(string name,int age):name(name),age(age){} void print() { cout << name << " " << age << endl; } protected: string name; int age; }; class Auto_ptr { public: Auto_ptr(int* ptr):ptr(ptr){} Auto_ptr(A* pa):pa(pa){} int& operator*() { return *ptr; } A* operator->() { return pa; } ~Auto_ptr() { if (ptr) { delete ptr; ptr = nullptr; } if (pa) { delete pa; pa = nullptr; } } protected: int* ptr; A* pa; }; int main() { Auto_ptr ptr(new int(18)); cout << *ptr << endl; Auto_ptr ptra(new A("Blue ", 20)); ptra->print(); }
Well, that's all for today's study!