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

5.5 KiB

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:

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...

#include <iostream>

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:

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ě:

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:

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ě:

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++
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:

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:

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ě...