C++ Tutorial: Overloading Operators I (2023)

C++i Overload Operator Tutorial - 2020

C++ Tutorial: Overloading Operators I (1)

C++ Tutorial: Overloading Operators I (2)



look for no site bogotobogo.com:

functions


default arguments

ANDpredetermined argumentis a value that will be used automatically if we omit the corresponding current argument when calling a function.

How do we set a default value? We must use the function prototype because the compiler looks at the prototype to check how many arguments a function takes.

For example:

int f(const char *s, int n = 2013)

We want the function to return a new int, so its type isAnd t. We wantnortehave a default value of2013, then we assign this value tonorte. if we go outnorteonly has the value2013, but if we pass an argument, the new value replaces the old one.2013

When we use a function with a list of arguments, we must add patterns from right to left. In other words, we can't provide a default value for a specific argument unless we also provide default values ​​for all the arguments to the right of it:

float f1(int l, int m = 2, int n = 3); // OK float f2(int l, int m = 2, int n); // Not all is well

function overload

function overloadallows us to use multiple functions sharing the same name. Typically, we use function overloading to design a family of functions that do the same thing while using different argument lists.

The key to function overloading is the argument list of a function (function signature). If two functions take the same number and types of arguments in the same order, they have the same signature. C++ allows us to define two multiple functions with the same name, as long as the functions have different signatures. The signature may differ:

  1. not number of arguments
  2. or in the type of arguments
  3. the both

For example, we define several versions ofF()with the following prototypes:

vacío f(const char *s, int n); // (a)void f(doble d, int n); // (b)void f(largo l, int n); // (c)vacío f(int m, int n); // (d)void f(const char *s); //(mi)

Some signatures that look different but actually both have the same signature:

double square (double d); double square(double &d;);

But we have to look at it from the compiler's perspective. To call them we use:

square(z);

It's inzthe argument matches bothdoble dIt's indoble &d;, so the compiler has no way of knowing which function to use. Therefore, to avoid such ambiguity, when checking function signatures, the compiler considers a reference to a type and the type itself as the same signature.

Note that in the function matching process, the compiler discriminates betweenconstantminot constantvariables:

int f(character *s); // overloaded int f(const char *s); // overloaded

Also note that the signature, not the return type, allows for function overloading. For example, the following two have the same signature and cannot be overloaded.

float f(int m, int *n) // Not overloaded double f(int m, int *n) // Not overloaded

Operator Overload

Here is some very simple code that shows the gist of the operator overloads: '+', '++ (post)', and '++ (pre)'. If you don't understand what's going on in the code, don't worry, by the end of this chapter you'll know how it works.

(Video) C++ Operator Overloading beginner to advanced (in-depth explanation)

class A{public:A(){}explicit A(int n):data(n) {}int data;A& operator+(A&);A operator++(int);A& operator++();};// + overloadA& A ::operator+(A& obj){A tmp = *this;tmp.data = this->data + obj.data;return tmp;} // post-increment (x++) overload // return the original value, then increment the value // copy required // returns a locally created object. // Note that it does not return a reference as it is a temporary object. A A::operator++(int){A tmp = *this;this->data = (this->data)++;return tmp;}/ / pre-increment overload (++x) // return the value of t incremented // no copy required A& A::operator++(){this->data = (this->data)++;return *this; }int main(){ A obj1(10);A obj2(20);A obj3 = obj1 + obj2; // obj3.data = 10 + 20 = 30A obj4 = obj1++; // obj4.data = 10, obj1.data = 11 A obj5 = ++obj2; // obj5.data = 21, obj2.data = 21return 0;}

Operator overloading extends the concept of operator overloading so that we can assign new meanings to C++ operators. This allows us to extend the operator overloading to user-defined types. This allows us to use the "+" to add two objects. The compiler determines which addition definition to use depending on the number and type of operands. Overloaded operators can often make code look more natural. In other words, operator overloading can be very useful for making our class look and behave more like built-in types.

To overload an operator, we use a special function,operator role. For example, when we overload "+":

operator+(argument_list)

Suppose, for example, that we have amy complexclass for which we define aoperator+()member function to overload the+operator to add a complex number to another complex number. so yesc1, c2, c3are all objects ofmy complexclass, we can write this:

c3 = c1 + c2;

The compiler, recognizing the operands as belonging to themy complexclass, replace the operator with the corresponding operator function:

c3 = c1.operator+(c2);

The function then uses thec1object that invokes the method, and thec2The object is passed as an argument to calculate the sum and returns it. Note that we use the assignment operator=which should also be above the head.

Operators that cannot be overloaded

The following operators cannot be overloaded:

  1. .member selection
  2. .*member selection with pointer to member
  3. ?:conditional
  4. ::scope resolution
  5. #stringization operator
  6. ##merger operator
  7. size ofobject size information
  8. type IDobject type information

Overload '='

In this section we will learn how to overload the assignment operator (=) between two objects of the Complex class.

Let's see the following code:

class MyComplex{private:double real, image;public:MyComplex(){real = 0; imag = 0;}MyComplex(double r, double i) {real = r;imag = i;}double getReal() const {return real;}double getImag() const {return imag;}MeuComplexo & operator=(const MeuComplexo &);};MiComplejo & MiComplejo::operator=(const MiComplejo& c) {real = c.real;imag = c.imag;return *this;}
#include <iostream>int main(){usando namespace std;MeuComplexo c1(5,10);MeuComplexo c2(50,100);cout << "c1= " << c1.getReal() << "+" << c1 .getImag() << "i" << endl;cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;c2 = c1;cout << "atribuir c1 a c2:" << endl; cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;}

We get the output as expected:

c1= 5+10ic2= 50+100iaatribuir c1 a c2:c2= 5+10i

Overload '+'

In this section we will learn how to overload the addition (+) operator between two objects of the Complex class.

Let's look at the following code that has an additional '+' overload function:

class MyComplex{private:double real, image;public:MyComplex(){real = 0; imag = 0;}MyComplex(double r, double i) {real = r;imag = i;}double getReal() const {return real;}double getImag() const {return imag;}MyComplex & operator=(const MyComplex &);MyComplex & operator+(const MyComplex& );};MeuComplexo & MeuComplexo::operator=(const MeuComplex& c) {real = c.real;imag = c.imag;return *this;}MiComplejo & MiComplejo::operator+(const MiComplejo& c) {real += c.real;imag += c.imag;return *this;}#include <iostream>int main(){usando namespace std;MeuComplexo c1(5,10);MeuComplexo c2(50,100);cout << "c1= " << c1.getReal() << "+" << c1 .getImag() << "i" << endl;cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;c2 = c1;cout << "atribuir c1 a c2:" << endl; cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;cout << endl;MeuComplexo c3(10,100);MeuComplexo c4(20,200 );cout << "c3= " << c3.getReal() << "+" << c3.getImag() << "i" << endl;cout << "c4= " << c4.getReal( ) << "+" << c4.getImag() << "i" << endl;MeuComplexo c5 = c3 + c4;cout << "adicionando c3 e c4" << endl;cout << "c3= " < < c3.getReal() << "+" << c3.getImag() << "i" << endl;cout << "c4= " << c4.getReal() << "+" << c4. getImag() << "i" << endl;cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;}

Note that when we use '+' for the object of type MyComplex,

c5 = c3 + c4;

we're actually calling a function something like this.

c5 = c3.operator+(c4)

The output of the above code is:

c1= 5+10ic2= 50+100iaatribuir c1 a c2:c2= 5+10ic3= 10+100ic4= 20+200ia somando c3 e c4c3= 30+300ic4= 20+200ic5= 30+300i

We got the correct result at least forc5. But the value ofc3Has been changed.

What happened?

Let's look at the '+' code overload.

MiComplejo & MiComplejo::operator+(const MiComplejo& c) {real += c.real;imag += c.imag;return *this;}

It turned out that the operation inside the function that returned the reference toc3object that has been modified.

So let's rewrite the overload function.

const MyComplex operator+(const MyComplex & );const MyComplex MyComplex::operator+(const MyComplex& c) {MyComplex temp;temp.real = this->real + c.real;temp.imag = this->imag + c.imag; temperatura de retorno;}

Note that this does not returncomplex &, but instead returns aconstant complexclass variable. As you can see, the implementation is a bit different from the previous example. Here, we will not return*is. Instead, we are creating a temporary variable and assigning the results of the addition totemperature. This explains why the function returns const Complex and not Complex &. In other words, the function creates a newmy complexobjecttemperaturewhich represents the sum of the other twomy complexobjects. Returning the object creates a copy of the object that the calling function can use. If the return type weremy complex and, however, the reference would be thetemperatureobject. more ortemperaturethe object is avariable localand it is destroyed when the function terminates, so the reference would be a reference to a non-existent object. using amy complexreturn type, however, means that the program constructs acopy ofofmy complexobject before destroying it, and the calling function gets the copy.

why do we come backconstant? veareturning object.

(Video) C++ Programming Tutorial 93 - Operator Overloading == and +

So let's get the correct answer.

c1= 5+10ic2= 50+100iassignifica c1 a c2:c2= 5+10ic3= 10+100ic4= 20+200iasomando c3 e c4c3= 10+100ic4= 20+200ic5= 30+300i

Overload '+' using the friend function

We get the same result if we use a friend function that is global and not a member of ourmy complexclass.

Since this function will need access to theprivatemembers ofmy complex, we will have to declare it as aamigooccupation.

friend const Operator MyComplex+(const MyComplex&, const MyComplex&);

The prototype has two implications:

  1. despiteoperator+()role is not a member role, it has the same access rights as a member role.
  2. despiteoperator+()function is declared in the class declaration, it is not a member function. Therefore, it is not called using the association operator.

Our revised code is:

class MyComplex{private:double real, image;public:MyComplex(){real = 0; imag = 0;}MyComplex(double r, double i) {real = r;imag = i;}double getReal() const {return real;}double getImag() const {return imag;}MyComplex & operator=(const MyComplex &);friend const Operator MyComplex+(const MyComplex&, const MyComplex&);};MyComplex & MyComplex::operator=(const MyComplex& c) {real = c.real;imag = c.imag;return *this;}/* This is not a member function of class MyComplex */operador const MiComplejo+(const MiComplejo& c1, const MiComplejo& c2) {MiComplejo temp;temp.real = c1.real + c2.real;temp.imag = c1.imag + c2.imag;return temp;}#include <iostream>int main(){usando namespace std;MeuComplexo c1(5,10);MeuComplexo c2(50,100);cout << "c1= " << c1.getReal() << "+" << c1 .getImag() << "i" << endl;cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;c2 = c1;cout << "atribuir c1 a c2:" << endl; cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;cout << endl;MeuComplexo c3(10,100);MeuComplexo c4(20,200 );cout << "c3= " << c3.getReal() << "+" << c3.getImag() << "i" << endl;cout << "c4= " << c4.getReal( ) << "+" << c4.getImag() << "i" << endl;MeuComplexo c5 = c3 + c4;cout << "adicionando c3 e c4" << endl;cout << "c3= " < < c3.getReal() << "+" << c3.getImag() << "i" << endl;cout << "c4= " << c4.getReal() << "+" << c4. getImag() << "i" << endl;cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;}

Note that in the example above we used one of two forms of overloadingoperator+().

What we used was the non-member version:

Operator MyComplex+(const MyComplex& c1, const MyComplex& c2);

But there is another way which is the member version:

Operator MyComplex+(const MyComplex& c1);

For the member function version, one is implicitly passed through theispointer and the second is passed explicitly as a function argument. For the friends version, which is the non-member version, both are passed as arguments.

Any of these two prototypes corresponds to the expressc1+c2, Wherec1mic2are the typemy complexobjects. That is, the compiler can convert the instruction

c3 = c1 + c2;

to any of the following:

c3 = operator+(c1,c2); // non-member functionc3 = c1.operator+(c2); // member function

Overloading of the ostream(<<) operator and the istream(>>) operator

The output streams use the<<operator for standard types. We can also overload<<operator for our own classes.

actually the<<is the left shift bit manipulation operator. more orour enemyThe class overloads the operator, turning it into an output tool. Hecoutis aour enemyand that it's smart enough to recognize all the basic C++ types. That's because theour enemyclass declaration includes an overloadoperator <<()definition for each of the basic types.

The istream operator can be overloaded in the same way, except that the second parameter has noconstant.

#include <iostream>usando namespace std;class MyComplex{private:double real, imag;public:MyComplex(){real = 0; imag = 0;}MeuComplex(doble r, doble i) {real = r;imag = i;}doble getReal() const {return real;}doble getImag() const {return imag;}MeuComplex & operator=(const MyComplex &);const Operador MiComplejo+(const MiComplejo & );MiComplejo & operador++(void);Operador MiComplejo++(int);/*friend const Operador MiComplejo+(const MiComplejo&, const MiComplejo&); */amigo ostream& operator<<(ostream& os, const MyComplex& c);// note: no const for the second parameteramigo istream& operator>>(istream& is, MyComplex& c);};MeuComplex & MeuComplex::operator=(const MeuComplex& c) {real = c.real;imag = c.imag;return *this;}const MeuComplex MeuComplex::operator+(const MeuComplex& c) {MeuComplex temp;temp.real = this->real + c.real;temp.imag = this->imag + c.imag;return temp;}//pré-incrementoMyComplex & MyComplex::operator++() {real++;imag++;return *this;}/ /post-incrementMyComplex MyComplex::operator++(int) { MyComplex temp = *this; reales++; imagenm++; return temp;}/* Esta no es una función de miembro de la clase MyComplex *//*const MyComplex operator+(const MyComplex& c1, const MyComplex& c2) { MyComplex temp; temp.real = c1.real + c2.real; temp.imag = c1.imag + c2.imag; temperatura de retorno;}*/ostream& operator<<(ostream &os;, const MyComplex& c) {os << c.real << '+' << c.imag << 'i' << endl;return os;}istream& operator>>(istream &is;, MyComplex& c) {is >> c.real >> c.imag;return is;}int main(){MyComplex c1(5,10);cout << "c1 = " << c1.getReal() << "+" << c1.getImag() << "i" << endl;cout < < "Using overloaded stream(<<) " << endl;cout << "c1 = " << c1 << endl;MyComplex c2;cout << "Enter two numbers: " << endl;cin >> c2;cout << "Using overloaded istream(>>) " << endl; cout << "Input complex is = " << c2;returns 0;}

The output is:

c1 = 5+10iUsing overloaded stream(<<)c1 = 5+10iEnter two numbers: 111 222Using overloaded istream(>>)The input complex is = 111+222i

Notice that we just used:

cout << "c1 = " << c1 << endl;

Please note that when we do

cout << c1;

becomes the following function call:

operator<<(cout, c1);

Class Serialization - Operator Overloading (<<) and (>>)

When we want to serialize (save() and then load()) a class, we need to overload two operators << and >> because the operators know what to do with a class. Here's an example using Qt5:

#include <QCoreApplication>#include <QFile>#include <QString>#include <QDataStream>#include <QDebug>class Student{public: int ID; QString Nombre; // ostream, << sobrecargaamigo QDataStream &Student;::operator<<(QDataStream &out;, const Student &s;){ out << s.ID << s.Name; go out again; } // istream, >> overloadamigo QDataStream &Student;::operator>>(QDataStream &in;, Student &s;){ s = Student(); in >> s.ID >> s.Name; the return of the; }};void Save(){ Student s1; s1.ID = 1; s1.Name = "Ravel"; student s2; s2.ID = 2; s2.Name = "Schonberg"; Filename QString = "C:/Qt/Test/st.txt"; file QFile(filename); if(!file.open(QIODevice::WriteOnly)) { qDebug() << "Could not open" << filename; Returns; } QDataStream out(&file;); out.setVersion(QDataStream::Qt_5_1); output << s1 << s2; file.empty(); file.close();}void Load(){ Student s1; student s2; s2.ID; s2.Name; Filename QString = "C:/Qt/Test/st.txt"; file QFile(filename); if(!file.open(QIODevice::ReadOnly)) { qDebug() << "Could not open" << file name; Returns; } QDataStream in(&file;); en.setVersion(QDataStream::Qt_5_1); in >> s1 >> s2; file.close(); qDebug() << s1.Name << "The ID of "is" << s1.ID; qDebug() << s2.Name << "The ID of " is " << s2.ID;}int main( int argc, char *argv[]){ QCoreApplication a(argc, argv); save (); Load(); return a.exec();}

Salida:

The ID of "Ravel" is 1 The ID of "Schonberg" is 2

Overloading of the increment/decrement operator '++/--'

(Video) OPERATORS and OPERATOR OVERLOADING in C++

In this section, we'll learn how to overload the increment and decrement operators (++ and --). We will focus on the increment operators, since the decrement operators work in the same way. There are actually two types of increment operators:preincremento(++i)miposincremento(i++).

Consider the following:

  1. v++
    (post) increase; this is a postfix expression, and the value ofv++is the value ofvbefore the increase.
  2. ++v
    (pre) increase; this is a unary expression, and the value of++vis the value ofvafter the increment.

Here is the modified code with the overloaded increment functions.

class MyComplex{private:double real, image;public:MyComplex(){real = 0; imag = 0;}MyComplex(double r, double i) {real = r;imag = i;}double getReal() const {return real;}double getImag() const {return imag;}MyComplex & operator=(const MyComplex &); const Operator MyComplex+(const MyComplex & );MyComplex & operator++(void); MyComplex operator++(int);/*amigo const Operador MiComplejo+(const MiComplejo&, const MiComplejo&); */};MeuComplex & MeuComplexo::operator=(const MeuComplex& c) {real = c.real;imag = c.imag;return *this;}const MeuComplex MeuComplexo::operator+(const MeuComplex& c) {MeuComplex temp;temp .real = esto->real + c.real;temp.imag = esto->imag + c.imag;return temp;}//pré-incrementoMeuComplexo & MeuComplexo::operator++() {real++;imag++;return *this;}// post incrementMyComplex MyComplex::operator++(int) { MyComplex temp = *this; real++; image++; return temperature;}/* Esta no es una función de miembro de la clase MyComplex *//*const MyComplex operator+(const MyComplex& c1, const MyComplex& c2) { MyComplex temp; temp.real = c1.real + c2.real; temp.imag = c1.imag + c2.imag; return temp;}*/#include <iostream>int main(){usando namespace std;MeuComplexo c1(5,10);MeuComplexo c2(50,100);cout << "c1= " << c1.getReal() << "+" << c1.getImag() << "i" << endl;cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;c2 = c1;cout << "atribuir c1 a c2:" << endl; cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;cout << endl;MeuComplexo c3(10,100);MeuComplexo c4(20,200 );cout << "c3= " << c3.getReal() << "+" << c3.getImag() << "i" << endl;cout << "c4= " << c4.getReal( ) << "+" << c4.getImag() << "i" << endl;MeuComplexo c5 = c3 + c4;cout << "adicionando c3 e c4" << endl;cout << "c3= " < < c3.getReal() << "+" << c3.getImag() << "i" << endl;cout << "c4= " << c4.getReal() << "+" << c4. getImag() << "i" << endl;cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;cout << endl;++c5;cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;c5++;cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;}

The output is:

c1= 5+10ic2= 50+100iassignifica c1 a c2:c2= 5+10ic3= 10+100ic4= 20+200iasomando c3 e c4c3= 10+100ic4= 20+200ic5= 30+300ic5= 31+301ic5= 32+302i

There is a problem with the definition of the prefix and postfix operators: they each take the same number and type of parameters. The normal overload cannot distinguish whether the operator we are defining is the prefix or postfix version.

To solve this problem, thethey postThe operator function takes an additional parameter from theAnd t. When the postfix operator is used, the compiler provides0as an argument to this parameter. While our postfix function can use this additional parameter, it normally shouldn't. This parameter is not required for the work that a postfix operator normally does. Its sole purpose is to distinguish the suffix function definition from the prefix version.

Note that the back increment (v++) is returning a back value and requires localtemperaturevariable. That's why we prefer the prefix in STL iterators. Also, note that the post-increment (v++) case returns an object because it is created locally and cannot return a reference unlike the pre-increment (++v) case which returns a reference.Return a non-const object.


The increment(++) and decrement(--) operators are most commonly implemented for classes, such as iterators, that provide pointer behavior on elements of a sequence. In the following example, we define a class that points to an array and provides access to the elements of the array.

#include <iostream>clase SmartPtr{public:SmartPtr(int *b, int *e):beg(b), end(e), curr(b) {}SmartPtr operator++(int);SmartPtr& operator++();int* getCurrent() {return curr;}private:int *beg;int *end;int *curr;};SmartPtr& SmartPtr::operator++(){if(curr == end)throw "incremento após o final";++curr;return *this;}SmartPtr SmartPtr::operator++(int){SmartPtr ret(*this);++ *este;retorna ret;}int main( ) { using namespace std;int a[] = {1,2,3,4,5};try {SmartPtr *ptr = new SmartPtr(a,a+5);cout << *( ptr-> getCurrent()) << endl;(*ptr)++;cout << *(ptr->getCurrent()) << endl;}catch (const char* e) { cout << "exception: " << e << end;} returns 0; }

Let's look at the postfix version. It's a bit more complicated than the prefix operators. They must remember the current state of the object before incrementing the object. This operator defines a local SmartPtr, which is initialized as a copy of*is. In other words, the variablewithdrawnis a copy of the current state of this object.

Having saved a copy of the current state, the operator calls its own prefix operator to do the increment:

***this;

call himSmartPtrNameprefix increment operator on this object. This operator checks if the increment is safe and incrementsActualor throw an exception. If no exception was thrown, the postfix function completes by returning the copy stored inwithdrawn. So when you come back,the object itself has been incremented, but the return value reflects the original unincremented value.

SmartPtr& SmartPtr::operator++(){if(curr == end)throw "incremento após o final";++curr;return *this;}

How about "++++"

The implementation of "++"allow this:

aObj++++;

although it is not allowed for the built-inAnd t:

n++++; // Error

We can see it in the output of the following code:

#include <iostream>using namespace std;class A{public:explicit A(int d = 0): mValue(d) {}A operator++(int);friend ostream& operator<<(ostream &os;, const A& a); private:int mValue;}; // this returns a temporary stack variable, // so its return type is just A, not A&ANDA::operador++(int){A temp = *this;mValue++;returntemperature;}ostream& operator<<(ostream &os;, const A& a){os << a.mValue << endl;return os;}int main(){int n = 0;A aObj;cout << "n = " << n << " aObj = " << aObj;cout << "n++ = " << n++ << " aObj++ = " << aObj++;cout << "n = " << n << " aObj = " < < aObj;cout << "n++ = " << n++ << " aObj++ = " << aObj++;cout << "n = " << n << " aObj = " << aObj;cout << " aObj++++ = " << aObj++++;// cout << "n++++ = " << n++++ << " aObj++ = " << aObj++;cout << "n = " << n << " aObj = " << aObj;return 0;}

Salida:

n = 0 aObj = 0n++ = 0 aObj++ = 0n = 1 aObj = 1n++ = 1 aObj++ = 1n = 2 aObj = 2 aObj++++ = 2n = 2 aObj = 3

Whenever we overload an operator, we must adapt the operator rule to the built-in types. But the implementation of postfix incrementing does not respect the existing behavior of built-in types.

What went wrong?
When we do:

n++++; // applying postfix increment twice

is doing this:

norte.operator++(0).operator(0);

The second invocation ofoperator++is being applied to the object returned from the first. The integer is not allowed because its output would be confusing and contrary to the initial intent. The second invocation will end up incrementing the returned object, as a result, even if allowed, it will only increment once in the end, which is not the intention of ++++.

So we need to prohibit users from doing this by returningconstantoperator object++:

constantA A::operator++(int) {...}

Index Operator [] Overloading

To access an element of an array/vector, we need to make [] work. Let's start with the simplest example below:

#include <iostream>#include <string>usando namespace std;class Vector{int sz;double *elem;public:Vector(int s): sz(s), elem(new double[s]) { for (int i = 0; i < s; i++) elem[i] = 0;}~Vector() { delete[] elem; }int size() const { return sz; }vaziodefine(int n, doble val) {elem[n] = val;}dobleget(int n) {return elem[n];}};int main(){Vetor v(10);for (int i = 0; i < v.size() ; i++) {v.set(i, i *1.1);}for (int i = 0; i < v.size() ; i++) {cout << v.get(i) << " " << endl;}return 0;}

Here, we usedefine()miget(), which work, but are ugly. Also, we cannot use the index operator, []. So let's modify it a bit:

(Video) Buckys C++ Programming Tutorials - 50 - Operator Overloading

#include <iostream>#include <string>usando namespace std;class Vector{int sz;double *elem;public:Vector(int s): sz(s), elem(new double[s]) { for (int i = 0; i < s; i++) elem[i] = 0;}~Vector() { delete[] elem; }int size() const { return sz; }double operator[](int n) { return element[n]; }};int main(){ Vector v(10);duplo dval =v[3]; // OKv[4]=100; // error: '=': left operand must be l-valuereturn 0;}

In the above example,v[i]is interpreted asv.operator[](i). Returns the value ofi-thelement ofv. However thev[4]it's just avalor, not onevariableas indicated by the error.

If we modify the overload[] part, it improves a bit:

double *operador[](int n) { return &elem;[n]; }// devuelve un puntero ....int main(){Vector v(10);double dval = *v[3]; //OK*v[4] = 100; // OKcout << "*v[4] = " << *v[4] << endl; //OK devuelve 0;}

but we still needdereference (*)avget/set. So here is the final version that we can use []:

#include <iostream>#include <string>usando namespace std;class Vector{int sz;double *elem;public:Vector(int s): sz(s), elem(new double[s]) { for (int i = 0; i < s; i++) elem[i] = 0;}~Vector() { delete[] elem; }int size() const { return sz; }double &operator;[](int n) { return elem[n]; }// devuelve la reference};int main(){Vector v(10);double dval = v[3]; //OKv[4] = 100; // OKcout << "v[4] = " << v[4] << endl; //OK return 0;}

Finally,v[i]is interpreted asv.operator[](i)and returns areferencedoi-thelement ofv.

Function Call Operator() Overload

The function call operator can be overloaded for objects of class of type. the overloadedoperator()must be declared as a member function. It is called by applying an argument list to an object of type class.

In the following example, we call the algorithmto transform()to apply the operation defined byabs valueto each element ofvec.

#include <iostream>#include <vector>#include <algorithm>using the namespace std;class absValue{public:intoperator()(int value) { return value < 0 ? -val : val;}};int main(){int a[] = {-3,-2,-1, 0, 1, 2, 3};int size = size(a)/size(a[0) ]); vector<int> vec(a, a+size);for(int i = 0; i < size; i++) cout << vec[i] << " ";transform(vec.begin(), vec .end(), vec.begin(),valorabs());cout << "\ndespués de transformar()\n";for(int i = 0; i < tamaño; i++) cout << vec[i] << " ";return 0;}

Salida:

-3 -2 -1 0 1 2 3após transform()3 2 1 0 1 2 3

For more details on the transformation, visitStandard Template Library (STL) V - Function Objects: Predefined Function Objects.

Member operator vs non-member operator

We can define operators as members of our class or as functions that are not members. Some members must be defined as class members, but others can be defined anyway. As an example, let's look at the following code that implements the *= operator:

class Complex{ public:Complex(double r, double i);Complex *= (const Complex &c;);...};

But we can use the non-member operator:

class Complex{public:Complex(double r, double i);...};Complex &operator;*=(Complex &lhs;, const Complex &rhs;);

Operator overloading for a user-defined object type

In the example code below, we define a new object type,And t. We put the collection object into the list and array, then sort the collection. For the list element we uselist::sort()while we usestd::sort()for the class of vectors.

To sort an arbitrary object, we definitely need to overload<(less than). For vector container we need additional dataassignment operator =().

#include <algorithm>#include <iostream>#include <list>#include <vector>using namespace std;class Int{public: Int(int n = 0) : i(n) { }public: booloperator<(const Int& a) const { cout << "operator<" << endl; returns this->i < a.i; } in t&operator=(const Int &a;) { cout << "operator=" << endl; this->i = a.i; return *this; }private: int i;};int main(){ list<Int> l; l.push_back(Int(3)); l.push_back(Int(1)); // list::sort(), // needs to do custom operator<() cout << "list::sort()" << endl;l.sort(); vector<Int>v; v.push_back(Int(2)); v.push_back(Int()); // standard::sort(); // this needs operator=() as well as operator<() cout << endl << "std::sort()" << endl;std::sort(v.start(), v.end()); returns 0;}

Salida

list::sort()operator<operator<operator<std::sort()operator<operator<operator=operator=

Operators that must be declared as member methods

The following operators must be declared as member methods to ensure that they receive an lvalue as their first operand:

  1. =assignment
  2. []subscribed
  3. ->class member access
  4. ->*member selection pointer
  5. new/delete
  6. ()function call
  7. (T)conversion (C-style share)

Operators other than those listed above can be overloaded as members or non-members. But in general, the non-member overload is recommended. The reasons are as follows:

  1. Symmetry
    When a binary operator is defined as a method of a class, it must have an object as the lhs operand. For example, the * operator, we should be able to write it ascomplex*10but I do not like10*complexWhy10.operator*(complex)It does not make any sense. In other words,a*bmust be the same aslicensed in letters. Otherwise, it breaks the community that users expect from*operator. So in this case, we should use the overload of the non-member operator.
  2. low coupling
    Since a non-member method cannot access a private member, it tends to make the class less coupled.

Operator overload - pros and cons

This is fromGoogle C++ Style Guide.

Do not overload operators, except in rare special circumstances.

  1. advantages
    It can make the code look more intuitive because a class will behave the same as built-in types (such as int). Overloaded operators are funnier names for functions with less colorful names, likeIs equal to()oAdd(). For some template functions to work correctly, we may need to define operators.
  2. Contras
    While operator overloading can make your code more intuitive, it has several drawbacks:
    1. This can trick our intuition into thinking that expensive trades are cheap built-in trades.
    2. It's much harder to find call locations for overloaded carriers. looking forIs equal to()is much easier than searching for relevant invocations of==.
    3. Some operators also work with pointers, which makes it easy to introduce errors.Foo + 4can do one thing while& Foo; +4does something totally different. The compiler doesn't complain about any of them, so it's very difficult to debug.
    Overcharging also has surprising ramifications. For example, if a class overloads the unary operator&, cannot be declared with certainty.
  3. Decision
    In general, don't overload operators. The assignment operator ( operator= ), in particular, is insidious and should be avoided. We can define functions likeIs equal to()micopy from()if we need them. Also, avoid dangerous ones.unary operator&at all costs if there is any possibility of the class being declared early.
    However, there may be rare cases where we need to overload an operator to interoperate with standard C++ classes or models (such asoperator <<(ostream&, const T&)for registration). These are acceptable if fully justified, but we should try to avoid them whenever possible. In particular, do not overloadoperator ==ooperator<just so our class can be used as a key in an STL container; instead, we must create equality and comparison functor types when declaring the container.
    Some of the STL algorithms require us to overloadoperator ==, and we can in those cases as long as we document the reason.

More to come...

C++ Tutorial: Overloading Operators I (3)

(Video) C++ Programming Tutorial 92 - Intro To Operator Overloading

Videos

1. C++ Programming Tutorial 50 - Overloading Operators
(Sonar Systems)
2. Operator Overloading Introduction | C++ Tutorial
(Portfolio Courses)
3. Operator Overloading in C++ Programming | C++ Programming for Beginners
(Simple Snippets)
4. Operator Overloading
(javidx9)
5. C++ Tutorial 12 : Operator Overloading & File I/O
(Derek Banas)
6. C++ Programming Tutorial : Operator Overloading
(Jayanam)

References

Top Articles
Latest Posts
Article information

Author: Edwin Metz

Last Updated: 27/06/2023

Views: 5831

Rating: 4.8 / 5 (78 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: Edwin Metz

Birthday: 1997-04-16

Address: 51593 Leanne Light, Kuphalmouth, DE 50012-5183

Phone: +639107620957

Job: Corporate Banking Technician

Hobby: Reading, scrapbook, role-playing games, Fishing, Fishing, Scuba diving, Beekeeping

Introduction: My name is Edwin Metz, I am a fair, energetic, helpful, brave, outstanding, nice, helpful person who loves writing and wants to share my knowledge and understanding with you.