Files
PPC/Přetěžování operátorů.md

184 lines
5.5 KiB
Markdown

- *operator overloading*
- prakticky definování operací s operátory (např. +, -, /, etc.) pro objekty
- [oficiální dokumentace](https://isocpp.org/wiki/faq/operator-overloading/1000)
- [geeks for geeks](https://www.geeksforgeeks.org/cpp/operator-overloading-cpp/)
- [krátké YT video](https://www.youtube.com/watch?v=9tHu4mWtrnM&pp=ygUYY3BwIG9wZXJhdG9yIG92ZXJsb2FkaW5n) které jsem trochu vykradl
Když si vytvoříme třídu, chceme někdy mít jednoduchou práci při pracování s více jejími objekty. Pokud mám třeba třídu `ComplexNumber`" a chci sečíst 2 její objekty, musel bych normálně vytvořit vlastní funkci, která mi to umožní. Přetěžování operátorů mi ale zjednoduší práci. **Místo sepsání funkce pro součet si nadefinuji, že pokud sečtu 2 objekty pomocí operátoru `+`, tak se sečtou.**
Příklad:
```cpp
class Number
{
public:
int n;
Number(int set_n) {n = set_n;}
};
int main()
{
Number a(5);
Number b(10);
Number c = a + b; //TOTO BY SE NÁM HODILO, ALE VYHODÍ TO ERROR
return 0;
}
```
V kódu se snažím sečíst dva různé objekty, ale nejde to, protože součet dvou objektů typu `Number` není definován (stejně jako není definován součet 2 objektů typu chyba, nebo čehokoli jiného).
**Dostanu error ve stylu:** `Invalid operands to binary expression ('Number' a...)`
**Já to ale můžu spravit tím, že si operaci pro součet sám definuju...**
```cpp
#include <iostream>
using namespace std;
class Number
{
public:
int n;
Number(int set_n) {n = set_n;}
Number operator+(const Number &numA) //reference na objekt napravo od +
{
return Number(this->n + numA.n)
}
};
int main()
{
Number a(5);
Number b(10);
Number c = a + b; //TED FUNGUJE
std::cout << c.n << std::endl;
return 0;
}
```
Takže co se děje...
Tady je příklad binárního operátoru `+`. V podstatě to je ekvivalent zavolání funkce z objektu `a`, která by se mohla jmenovat `vrat_muj_soucet_s_necim_jak_objekt` (hrozné jméně, já vím). **V podstatě `+` je nyní funkce objektu nalevo a to co je napravo od `+` je argument této funkce.**
Je dobrý ale vědět, že operátory se nemusí přetěžovat jenom přes funkci objektu/třídy. Přetěžovat se dá i pomocí **nečlenské funkce** (normální funkce, která není součástí žádné třídy).
Příklad:
```cpp
Number operator+(const Vec& a, const Vec& b)
{
return Vec{a.x + b.x};
}
```
Toto funguje, i když to není v třídě `Number`. Pomocí nečlenské funkce lze mít součty 2 různých objektů, kde nalevo není objekt třídy, např: `5 + a`.
# Typy operátorů a jejich přetěžování
Existuje více typů operátorů valná většina z nich se dá přetěžovat. Přetěžovat ale nelze operátory:
- `.`
- `::`
- `?:`
- `sizeof`
- `typeid`
## Binární operátory
- operáty s více stranami
- `např +, -, /, %` etc.
Ve třídě:
```cpp
class Vec
{
public:
int x;
Vec operator+(const Vec& other)
const {
return Vec{x + other.x};
}
};
```
`a + b` je prakticky `a.operator+(b)`
Mimo funkci:
``` cpp
Vec operator+(const Vec& a, const Vec& b)
{
return Vec{a.x + b.x};
}
```
`a + b` je prakticky `operator+(a, b)`
## Unární operátory (prefixové)
- mají pouze jeden člen
- operátory prefixové se píšou před členem
- př: `-a, ++a`
Ve třídě:
```cpp
class Vec
{
public:
int x;
Vec operator-()
const {
return Vec{-x};
}
};
```
`-a` je prakticky funkce `a.operator-()`
`const` v tomto případě znamená, že se nezmění objekt `this`. Ono to v tomto případě ani nedává smysl, jelikož chceme vrátit `-a`, ale nechceme změnit `a` na `-a`... Tho, ten syntax je matoucí.
**Přesněji** `const` před `{}` znamená, že metoda **nesmí měnit objekt** na kterém byla zavolaná.
Nesmím pak měnit proměnné objektu a volat jeho funkce, co nejsou stejně `const`. **Umožní mi to ale volat funkci i na const objektu**.
## Unární operátory (postfixové)
- pouze jeden člen
- píší se za členem
- mají dummy parametr -> to je trik jak odlišit `++a` od `a++`
- např: `a++`
```cpp
Vec operator++(int)
{
Vec old = *this;
x++; //kde x je promenna Vec
return old;
}
```
`a++` je prakticky `a.operator++(0)`
`this` je ukazatel a já nechci kopírovat ukazatel, ale to na co ukazuje, proto:
`Vec old = *this;`
**Proč se to sakra píše jako:** `operator++(int)` **s int?** (`a.operator++(0)` -> dělá kompilátor). Protože by jinak nešlo rozlišit `++a` a `a++`. Proto je konvence, že `a++` je `operator++(int)` a `++a` je `operator++()`.
## Ostatní
Operátor přístupu:
```cpp
int& operator[](size_t i)
{
return data[i];
}
```
`a[i]` -> `a.operator[](i)`
`int&` protože jsem šetrný a vracím referenci na prvek -> něco co ukazuje na jeho hodnotu a ne jeho
**Funktor**:
```cpp
class Functor
{
public:
int operator()(int x) {
return x * 2;
}
};
```
`a(i)` -> `f.operator()(i)`
Funktory jsou důležité! Funktory jsou objekty, které se chovají jako funkce a hádám (teď to nedokážu říct), že o nich uslyšíme ještě dost.
Jejich hlavní smysl je **být něco mezi objektem a funkcí**. Funkce nemá žádnou paměť, což znamená, že vždy dává stejný výsledek při stejném vstupu (i mean ne nutně, jako když použiju náhodná čísla, tak ne... buuuhuuu, ale chápeme se...).
Funktor mi umožňuje udělat objekt, který "předstírá, že je funkce". Pamatuje si svoje operace a může ukládat do vnitřních proměnných při každém zavolání/upravě, etc...
Dále jde přetěžovat třeby `->`. Operátorů je opravdu hodně...