📜 ⬆️ ⬇️

Overloading all 49 statements in C ++

Good day, dear readers of Habr!

When I just started my way of learning C ++, I had a lot of questions to which, at times, I could not quickly find the answers. Such an issue as operator overload was no exception. Now, when I figured out this topic, I want to help others dot the i .

In this publication, I will tell you about the various subtleties of operator overloading, why this overload is generally needed, about operator types (unary / binary), operator overloading with a friend (friendly function), as well as about the types of values ​​accepted and returned by overloads.

UPD.: For those who prefer the video format there is a video : https://youtu.be/Qn6mu9l6Xj8
')

What is overloading for?


Suppose that you create your own class or structure, let it describe a vector in 3-dimensional space:

struct Vector3 { int x, y, z; Vector3() {} Vector3(int x, int y, int z) : x(x), y(y), z(z) {} }; 

Now, you create 3 objects of this structure:

 Vector3 v1(10, 10, 10), v2(20, 20, 25), v3; //... 

And you want to align the object v2 with the object v1, write:

 v1 = v2; 

Everything works, but the vector example is very much simplified, maybe you have a structure in which you need not blindly copy all the values ​​from one object to another (as it happens by default), but produce some manipulations with them. For example, do not copy the last variable z. How does the program know about it? She needs clear commands that she will execute.

Therefore, we need to overload the assignment operator (=).

Understanding Operator Overloading


To do this, add an overload to our structure:

 Vector3 operator = (Vector3 v1) { return Vector3(this->x = v1.x, this->y = v1.y, this->z = 0); } 

Now, in the code above, we indicated that during assignment, it is necessary to copy the variables x and y , and z to zero.

UPD .: Thanks for the Eivind hint. Did not take into account in the article that the assignment operators should return the reference to * this, and not the value. This is the de facto standard expected behavior.

That is, the following code would be more correct:
 Vector3 operator = (Vector3 v1) { this->x = v1.x, this->y = v1.y, this->z = 0; return *this; } 


But such an overload is far from perfect, let's imagine that our structure contains not 3 variables of type int, but many objects of other classes, in this case this variant of overload will work rather slowly.


Differences between unary and binary operators


Unary operators are those operators where only one object is involved, to which all changes are applied.

 Vector3 operator + (const Vector3 &v1); //   Vector3 operator - (const Vector3 &v1); //   //  : //++, --, !, ~, [], *, &, (), (type), new, delete 

Binary operators - work with 2 objects

 Vector3 operator + (const Vector3 &v1, const Vector3 &v2); // -    ! Vector3 operator - (const Vector3 &v1, const Vector3 &v2); // -    ! //  : //*, /, %, ==, !=, >, <, >=, <=, &&, ||, &, |, ^, <<, >>, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, ->, ->*, (,), "," 

Overload in body and body class


We can declare and implement an overload directly in the body of the class or structure itself. I think that how to do it is already clear. Let's consider a variant in which the declaration of an overload occurs in the class body, and its implementation is already outside the class.

 struct Vector3 { //, , ... //  ,       = Vector3 operator = (Vector3 &v1); }; //      //   "Vector3::",    ,       //  Vector3 -     Vector3 Vector3::operator = (Vector3 &v1); { return Vector3(this->x = v1.x, this->y = v1.y, this->z = 0); } 

Why in friend operator overloading (friend)?


Friendly functions are those functions that have access to private methods of a class or structure.

Suppose that in our Vector3 structure, such members as x, y, z are private, then we cannot access them outside the structure body. This is where the friendly functions help.
The only change we need to make is to add the fried keyword before overload declaration:

 struct Vector3 { friend Vector3 operator = (Vector3 &v1); }; //     

When can you not do without friendly functions in operator overloading?


1) When we implement an interface (.h file) in which only method declarations are placed, and the implementation is placed in a hidden .dll file

2) When the operation is performed on objects of different classes. Example:

 struct Vector2 { // Vector2  Vector3 Vector2 operator + (Vector3 v3) {/*...*/} } // Vector2    Vector2  Vector3 vec2 = vec2 + vec3; //Ok vec2 = vec3 + vec2; // 

The error will occur for the following reason; in the Vector2 structure, we overloaded the + operator, which takes the Vector3 type as the value on the right, so the first option works. But in the second case, it is necessary to write the overload already for the Vector3 structure, and not 2. In order not to go into the implementation of the Vector3 class, we can write the following friendly function:

 struct Vector2 { // Vector2  Vector3 Vector2 operator + (Vector3 v3) {/*...*/} //   ,         Vector3 friend Vector2 operator + (Vector3 v3, Vector2 v2) {/*...*/} } vec2 = vec2 + vec3; //Ok vec2 = vec3 + vec2; //Ok 



Examples of different operator overloads with some explanations.


Example of overload for binary +, -, *, /,%

 Vector3 operator + (const Vector3 &v1, const Vector3 &v2) { return Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); } 

Overload example for postfix increment and decrement forms ( var ++, var-- )

 Vector3 Vector3::operator ++ (int) { return Vector3(this->x++, this->y++, this->z++); } 

Overload example for prefix increment and decrement forms ( ++ var, --var )

 Vector3 Vector3::operator ++ () { return Vector3(++this->x, ++this->y, ++this->z); } 

Overloading arithmetic operations with objects of other classes

 Vector3 operator * (const Vector3 &v1, const int i) { return Vector3(v1.x * i, v1.y * i, v1.z * i); } 

Unary Plus Overload (+)

 //  ,    Vector3 operator + (const Vector3 &v) { return v; } 

Unary Minus Overload (-)

 //   -1 Vector3 operator - (const Vector3 &v) { return Vector3(vx * -1, vy * -1, vz * -1); } 

Example of overloading of compound assignment operations + =, - =, * =, / =,% =

 Vector3 operator += (Vector3 &v1, const Vector3 &v2) { return Vector3(v1.x = v1.x + v2.x, v1.y = v1.y + v2.y, v1.z = v1.z + v2.z); } 

A good example of overloading comparison operators ==,! =,>, <,> =, <=

 const bool operator < (const Vector3 &v1, const Vector3 &v2) { double vTemp1(sqrt(pow(v1.x, 2) + pow(v1.y, 2) + pow(v1.z, 2))); double vTemp2(sqrt(pow(v2.x, 2) + pow(v2.y, 2) + pow(v2.z, 2))); return vTemp1 < vTemp2; } const bool operator == (const Vector3 &v1, const Vector3 &v2) { if ((v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z)) return true; return false; } // !=     const bool operator != (const Vector3 &v1, const Vector3 &v2) { return !(v1 == v2); } 

Example of overloading cast operations (type)

 //    -  true Vector3::operator bool() const { if (*this != Vector3(0, 0, 0)) return true; return false; } //    int -     Vector3::operator int() const { return int(this->x + this->y + this->z); } 

An example of overloading logical operators!, &&, ||

 // ,        bool const bool operator ! (Vector3 &v1) { return !(bool)v1; } const bool operator && (Vector3 &v1, Vector3 &v2) { return (bool)v1 && (bool)v2; } 

Example of overloading bitwise operators ~, &, |, ^, <<, >>

 //   (   -1,   ) const Vector3 operator ~ (Vector3 &v1) { return Vector3(~(v1.x), ~(v1.y), ~(v1.z)); } const Vector3 operator & (const Vector3 &v1, const Vector3 &v2) { return Vector3(v1.x & v2.x, v1.y & v2.y, v1.z & v2.z); } //   (xor) const Vector3 operator ^ (const Vector3 &v1, const Vector3 &v2) { return Vector3(v1.x ^ v2.x, v1.y ^ v2.y, v1.z ^ v2.z); } //     ostream& operator << (ostream &s, const Vector3 &v) { s << '(' << vx << ", " << vy << ", " << vz << ')'; return s; } //     (  ) istream& operator >> (istream &s, Vector3 &v) { std::cout << " Vector3.\nX:"; std::cin >> vx; std::cout << "\nY:"; std::cin >> vy; std::cout << "\nZ:"; std::cin >> vz; std::cout << endl; return s; } 

Example of overloading bitwise compound assignment & =, | =, ^ =, << =, >> =

 Vector3 operator ^= (Vector3 &v1, Vector3 &v2) { v1(Vector3(v1.x = v1.x ^ v2.x, v1.y = v1.y ^ v2.y, v1.z = v1.z ^ v2.z)); return v1; } //   ostream& operator <<= (ostream &s, Vector3 &v) { s.clear(); s << '(' << vx << ", " << vy << ", " << vz << ')'; return s; } 

An example of overloading operators working with pointers and class members [], (), *, &, ->, -> *
I see no reason to overload (*, &, ->, -> *), so there will be no examples below.

 //  !   []    ,     //    () int Vector3::operator [] (int n) { try { if (n < 3) { if (n == 0) return this->x; if (n == 1) return this->y; if (n == 2) return this->z; } else throw ":     "; } catch (char *str) { cerr << str << endl; } return NULL; } //       Vector3 Vector3::operator () (Vector3 &v1, Vector3 &v2) { return Vector3(v1 & v2); } 

How to overload new and delete ? Examples:

 //   1  void* Vector3::operator new(size_t v) { void *ptr = malloc(v); if (ptr == NULL) throw std::bad_alloc(); return ptr; } //     void* Vector3::operator new[](size_t v) { void *ptr = malloc(sizeof(Vector3) * v); if (ptr == NULL) throw std::bad_alloc(); return ptr; } void Vector3::operator delete(void* v) { free(v); } void Vector3::operator delete[](void* v) { free(v); } 

Overloading new and delete is a separate and rather large topic that I will not discuss in this publication.

Operator overload comma,

Attention! Do not confuse the operator with a comma with an enum mark! (Vector3 var1, var2;)

 const Vector3 operator , (Vector3 &v1, Vector3 &v2) { return Vector3(v1 * v2); } v1 = (Vector3(10, 10, 10), Vector3(20, 25, 30)); // : (200, 250, 300) 

Sources


1) https://ru.wikipedia.org/wiki/Operators in C and C ++
2) R. Laforé Object Oriented Programming in C ++
3) Thank you all for the comments on the publication and for pointing out the shortcomings!

Source: https://habr.com/ru/post/308890/


All Articles