Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 1/31 Přetěžování operátorů Ing. Josef Vogel, CSc Katedra softwarového inženýrství Katedra teoretické informatiky, Fakulta informačních technologii, ČVUT v Praze Karel Müller, Josef Vogel, 2011 Programování a algoritmizace 2, BI-PA2, 2011, Přednáška 4 BI-PA2 Evropský sociální fond Praha & EU: Investujeme do vaší budoucnosti
Přetěžování operátorů třída Zlomek s operacemi pomocí funkcí třída Zlomek s operacemi pomocí metod třída Zlomek s operátory přetíženými pomocí funkcí třída Zlomek s operátory přetíženými pomocí metod třída Zlomek s operátory přetíženými pomocí friend-funkcí třída Zlomek s přetíženým operátorem << konstruktor uživatelské konverze přehled přetěžování operátorů Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 2/31
p4\zlomek1\zlomek.h Třída Zlomek po prvé Instance třídy Zlomek je objekt obsahující dvě celočíselné položky: čitatel a jmenovatel Instanční metody jsou zřejmé z následující deklarace třídy. Operace sčítání a odčítání realizujeme pomocí funkcí definovaných mimo třídu: class Zlomek { int cit, jmen; void zkratit(); public: Zlomek(int c = 0, int j = 1); int citatel() const {return cit; int jmenovatel() const {return jmen; void vypis() const; ; Zlomek soucet(zlomek, Zlomek); Zlomek rozdil(zlomek, Zlomek); bool rovno(zlomek, Zlomek); Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 3/31
Operace se zlomky Zkrácení zlomku čitatele i jmenovatele vydělíme největším společným dělitelem čitatele a jmenovatele Součet zlomků zlomky převedeme na společného jmenovatele a pak sečteme Rozdíl zlomků zlomky převedeme na společného jmenovatele a pak odečteme Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 4/31
Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 5/31 Definice konstruktoru a metod: p4\zlomek1\zlomek.cpp Třída Zlomek po prvé Zlomek::Zlomek(int c, int j) { cit = c; if (j>0) jmen = j; else if (j<0) { jmen = -j; cit = -cit; else throw "jmenovatel je 0"; zkratit(); vyvolání výjimky int nsd(int x, int y) { int zbytek = x%y; while (zbytek!=0) { x = y; y = zbytek; zbytek = x%y; return y; void Zlomek::zkratit() { int d = nsd(abs(cit), jmen); if (d==1) return; cit /= d; jmen /= d; void Zlomek::vypis() const { cout << '(' << cit << '/' << jmen << ')';
p4\zlomek1\zlomek.cpp Třída Zlomek po prvé Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 6/31 Definice netřídních funkcí Zlomek soucet(zlomek x, Zlomek y) { int spoljm = x.jmenovatel() * y.jmenovatel(); Zlomek res(x.citatel()*y.jmenovatel() + y.citatel()*x.jmenovatel(), spoljm); return res; Zlomek rozdil(zlomek x, Zlomek y) { int spoljm = x.jmenovatel() * y.jmenovatel(); Zlomek res(x.citatel()*y.jmenovatel() y.citatel()*x.jmenovatel(), spoljm); return res; bool rovno(zlomek x, Zlomek y) { return x.citatel()*y.jmenovatel() == x.jmenovatel()*y.citatel();
p4\zlomek1\main.cpp Třída Zlomek po prvé Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 7/31 Použití: #include "zlomek.h" int main() { Zlomek a(3,5), b(1), c; a.vypis(); cout << ' '; b.vypis(); cout << endl; c = soucet(a, b); c.vypis(); cout << endl; c = rozdil(a, b); c.vypis(); cout << endl; cout << rovno(a,b) << endl; system("pause"); return 0;
p4\zlomek2\zlomek.h Třída Zlomek po druhé Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 8/31 Binární operace se zlomky můžeme realizovat pomocí metod (metody třídy Zlomek mohou používat privátní položky) class Zlomek { int cit, jmen; void zkratit(); public: Zlomek(int c = 0, int j = 1); int citatel() const {return cit; int jmenovatel() const {return jmen; void vypis() const; Zlomek soucet(zlomek) const; Zlomek rozdil(zlomek) const; bool rovno(zlomek) const; ;
p4\zlomek2\zlomek.cpp Třída Zlomek po druhé Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 9/31 V metodě realizující binární operaci se zlomky bude první operand reprezentován objektem, na který se metoda aplikuje, a druhý operand bude dán parametrem metody Zlomek Zlomek::soucet(Zlomek y) const { int spoljm = jmen * y.jmen; Zlomek res(cit*y.jmen + y.cit*jmen, spoljm); return res; Zlomek Zlomek::rozdil(Zlomek y) const { int spoljm = jmen * y.jmen; Zlomek res(cit*y.jmen - y.cit*jmen, spoljm); return res; bool Zlomek::rovno(Zlomek y) const { return cit*y.jmen == jmen*y.cit;
p4\zlomek2\main.cpp Třída Zlomek po druhé Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 10/31 V metodě realizující binární operaci se zlomky bude první operand reprezentován objektem, na který se metoda aplikuje, a druhý operand bude dán parametrem metody int main() { Zlomek a(3,5), b(1), c; a.vypis(); cout << ' '; b.vypis(); cout << endl; c = a.soucet(b); c.vypis(); cout << endl; c = a.rozdil(b); c.vypis(); cout << endl; cout << a.rovno(b) << endl; system("pause"); return 0;
p4\zlomek3\main.cpp Třída Zlomek po třetí Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 11/31 Nelíbilo by se vám, kdybychom pro součet zlomků mohli použít operátor + a pro rozdíl zlomků operátor -? Pak by hlavní funkce mohla mít tento kód: int main() { Zlomek a(3,5), b(1), c; a.vypis(); cout << ' '; b.vypis(); cout << endl; c = a + b; c.vypis(); cout << endl; c = a - b; c.vypis(); cout << endl; cout << (a == b) << endl; system("pause"); return 0; Toho lze dosáhnout přetížením operátorů
p4\zlomek3\zlomek.h Třída Zlomek po třetí Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 12/31 Deklarace třídy Zlomek a funkcí přetěžujících operátory: class Zlomek { int cit, jmen; void zkratit(); public: Zlomek(int c = 0, int j = 1); int citatel() const {return cit; int jmenovatel() const {return jmen; void vypis() const; ; Zlomek operator+(zlomek, Zlomek); Zlomek operator-(zlomek, Zlomek); bool operator==(zlomek, Zlomek);
Třída Zlomek po třetí Přetížení binárních operátorů pomocí funkcí způsobí, že c = a + b; se provede jako c = operator+(a, b); c = a - b; se provede jako c = operator-(a, b); a == b se vyhodnotí jako operator==(a, b); Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 13/31
p4\zlomek3\zlomek.cpp Třída Zlomek po třetí Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 14/31 Definice funkcí přetěžujících operátory: Zlomek operator+(zlomek x, Zlomek y) { int spoljm = x.jmenovatel() * y.jmenovatel(); Zlomek res(x.citatel()*y.jmenovatel() + y.citatel()*x.jmenovatel(), spoljm); return res; Zlomek operator-(zlomek x, Zlomek y) { int spoljm = x.jmenovatel() * y.jmenovatel(); Zlomek res(x.citatel()*y.jmenovatel() y.citatel()*x.jmenovatel(), spoljm); return res; bool operator==(zlomek x, Zlomek y) { return x.citatel()*y.jmenovatel() == x.jmenovatel()*y.citatel();
p4\zlomek4\zlomek.h Třída Zlomek po čtvrté Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 15/31 Operátor lze přetížit též metodou: class Zlomek { int cit, jmen; void zkratit(); public: Zlomek(int c = 0, int j = 1); int citatel() const {return cit; int jmenovatel() const {return jmen; void vypis() const; Zlomek operator+(zlomek) const; Zlomek operator-(zlomek) const; bool operator==(zlomek) const; ;
Třída Zlomek po čtvrté Přetížení binárních operátorů pomocí metod způsobí, že c = a + b; se provede jako c = a.operator+(b); c = a - b; se provede jako c = a.operator-(b); a == b se vyhodnotí jako a.operator==(b); Poznámka: operátor nemůže být současně přetížen funkcí i metodou Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 16/31
p4\zlomek4\zlomek.cpp Třída Zlomek po čtvrté Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 17/31 Operátor lze přetížit též metodou: Zlomek Zlomek::operator+(Zlomek y) const { int spoljm = jmen * y.jmen; Zlomek res(cit*y.jmen + y.cit*jmen, spoljm); return res; Zlomek Zlomek::operator-(Zlomek y) const { int spoljm = jmen * y.jmen; Zlomek res(cit*y.jmen - y.cit*jmen, spoljm); return res; bool Zlomek::operator==(Zlomek y) const { return cit*y.jmen == jmen*y.cit;
p4\zlomek5\zlomek.h Třída Zlomek po páté Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 18/31 Operátor lze přetížit pomocí friend funkce, která má stejný přístup k private položkám, jako instanční metody třídy: class Zlomek { int cit, jmen; void zkratit(); public: Zlomek(int c = 0, int j = 1); int citatel() const {return cit; int jmenovatel() const {return jmen; void vypis() const; friend Zlomek operator+(zlomek, Zlomek); friend Zlomek operator-(zlomek, Zlomek); friend bool operator==(zlomek, Zlomek); ;
p4\zlomek5\zlomek.cpp Třída Zlomek po páté Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 19/31 Definice friend-funkcí vypadají stejně, jako definice jiných funkcí (můžeme v nich však používat private položky) Zlomek operator+(zlomek x, Zlomek y) { int spoljm = x.jmen * y.jmen; Zlomek res(x.cit*y.jmen + y.cit*x.jmen, spoljm); return res; Zlomek operator-(zlomek x, Zlomek y) { int spoljm = x.jmen * y.jmen; Zlomek res(x.cit*y.jmen - y.cit*x.jmen, spoljm); return res; bool operator==(zlomek x, Zlomek y) { return x.cit*y.jmen == x.jmen*y.cit;
p4\zlomek6\main.cpp Třída Zlomek po šesté Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 20/31 A co výstupní konverze do standardního proudu cout? Lze dosáhnout toho, aby výpis zlomku byl předepsán operátorem <<, jak je uvedeno v následující funkci main? int main() { Zlomek a(3,5), b(1), c; cout << a << ' ' << b << endl; c = a + b; cout << c << endl; c = a - b; cout << c << endl; cout << (a == b) << endl; system("pause"); return 0;
p4\zlomek6\zlomek.h, zlomek.cpp Třída Zlomek po šesté Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 21/31 Výstupní konverzi zlomků pomocí operátoru << umožní přetížení tohoto operátoru (pomocí funkce) pro pravý operand typu Zlomek: class Zlomek { int cit, jmen; void zkratit(); public: Zlomek(int c = 0, int j = 1); int citatel() const {return cit; int jmenovatel() const {return jmen; friend Zlomek operator+(zlomek, Zlomek); friend Zlomek operator-(zlomek, Zlomek); friend bool operator==(zlomek, Zlomek); friend ostream& operator<<(ostream&, Zlomek); ; ostream& operator<<(ostream& os, Zlomek x) { os << '{' << x.cit << '/' << x.jmen << ')'; return os;
p4\zlomek6\main2.cpp Třída Zlomek po šesté a půl Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 22/31 Podívejme se ještě na funkci main2.cpp: číslo typu int se přiřazuje do proměnné typu Zlomek, sčítá se zlomek s číslem typu int, číslo typu int se zlomkem: int main() { Zlomek a(3,5), b; int i = 1; b = 2; cout << b << endl; b = a + i; cout << b << endl; b = 2 + a; cout << b << endl; system("pause"); return 0; Kdo za to může? Zlomek = int Zlomek = Zlomek + int Zlomek = int + Zlomek
Konstruktor uživatelské konverze Konstruktorem uživatelské konverze typu T1 na T je každý konstruktor třídy T, který lze volat s jediným parametrem typu T1 Je-li ve třídě takový konstruktor, pak všude tam, kde se očekává objekt typu T, může být uveden výraz typu T1. Konverze se provede tak, že se vytvoří dočasný objekt typu T a ten se inicializuje konstruktorem uživatelské konverze Konstruktor Zlomek(int c = 0, int j = 1); je konstruktorem uživatelské konverze. Proto: b = 2; se provede jako b = Zlomek(2); b = a + 2; se provede jako b = a + Zlomek(2); b = 2 + a; se provede jako b = Zlomek(2) + a; Uživatelskou konverzi lze zakázat tím, že před deklaraci konstruktoru se uvede explicit: explicit Zlomek(int c = 0, int j = 1); Vyzkoušejte Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 23/31
p4\zlomek7\zlomek.h,zlomek.cpp Přehled přetěžování binárních operátorů Binární operátory @ (* / % + - << >> < <= > >= ==!= & ^ && += -=...) zápis operace: signatura: přetížení metodou třídy T1: ekviv. volání metody: přetížení funkcí: ekviv. volání funkce: @x @T -> Tv Tv operator@() x.operator@() Tv operator@(t) operator@(x) Poznámka: přetížením operátoru např. + není přetížen operátor +=, ten musíme přetížit zvlášť, např. takto: friend Zlomek& operator+=(zlomek&, Zlomek); Zlomek& operator+=(zlomek& x, Zlomek y) { int spoljm = x.jmen*y.jmen; x.cit = x.cit*y.jmen+y.cit*x.jmen; x.jmen = spoljm; x.zkratit(); return x; Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 24/31
p4\zlomek7\zlomek.h,zlomek.cpp Přehled přetěžování unárních operátorů Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 25/31 a) Unární prefixové operátory @ (! ~ + - ++ -- & *) zápis operace: signatura: přetížení metodou třídy T: ekviv. volání metody: přetížení funkcí: ekviv. volání funkce: @x @T -> Tv Tv operator@() x.operator@() Tv operator@(t) operator@(x) Příklad: Zlomek operator++(); Zlomek Zlomek::operator ++() { cit += jmen; zkratit(); return *this;
p4\zlomek7\zlomek.h,zlomek.cpp Přehled přetěžování unárních operátorů b) Unární postfixové operátory @ (++ --) zápis operace: signatura: přetížení metodou třídy T: ekviv. volání metody: přetížení funkcí: ekviv. volání funkce: x@ @T -> Tv Tv operator@(int) x.operator@(0) Tv operator@(t,int) operator@(x,0) Příklad: Zlomek operator++(int); Zlomek Zlomek::operator ++(int) { Zlomek vysl = *this; cit += jmen; zkratit(); return vysl; Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 26/31
p4\zlomek7\main.cpp Třída Zlomek po sedmé Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 27/31 Použití: int main() { Zlomek a(2,5); cout << a << endl; // vypise (2/5) cout << ++a << endl; // vypise (7/5) cout << a++ << endl; // vypise (7/5) cout << a << endl; // vypise (12/5) Zlomek x(3,4), y(1,8); x += y; cout << x << endl; // vypise (7/8) return 0;
Přetížení operátoru = Operátor přiřazení (=) zápis operace: signatura: přetížení metodou třídy T: ekviv. volání metody: přetížení funkcí: ekviv. volání funkce: x = y T = T1 -> T T& operator=(const T1&) x.operator=(y) - - Poznámky: Typ argumentu T1 je obvykle stejný s T Typ výsledku může být též const T& nebo void. Jaký je v tom případě důsledek? Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 28/31
Přetížení operátoru [] Operátor [] zápis operace: signatura: přetížení metodou třídy T: ekviv. volání metody: přetížení funkcí: ekviv. volání funkce: x[y] T[T1] -> Tv Tv& operator[](t1) nebo const Tv& operator[](t1) cons x.operator[](y) - - První způsob přetížení je určen pro nekonstantní objekty a umožňuje použít výsledek jako l-value (tzn. na levé straně přiřazení) Druhý způsob přetížení je určen pro konstantní objekty, výsledek není modifikovatelný Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 29/31
Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 30/31 Přetížení operátoru -> Operátor -> (unární) zápis operace: signatura: přetížení metodou třídy T: ekviv. volání metody: přetížení funkcí: ekviv. volání funkce: x -> p T -> Tv* Tv* operator->() (x.operator->()) -> p - -
Karel Müller, Josef Vogel (ČVUT FIT) Přetěžování operátorů BI-PA2, 2011, Přednáška 4 31/31 Přetížení operátoru volání funkce Operátor () zápis operace: signatura: přetížení metodou třídy T: ekviv. volání metody: přetížení funkcí: ekviv. volání funkce: x(x1,x2,,xn) T(T1,T2,,Tn) -> Tv Tv operator()(t1,t2,,tn) x.operator()(x1,x2,,xn) - -