Programozás II gyakorlat 8. Operátor túlterhelés
Kezdő feladat Írjunk egy Vector osztályt, amely n db double értéket tárol. A konstruktor kapja meg az elemek számát. Írj egy set(int idx, double v) függvényt, amely az idx. elem értékét v-re állítja, valamint egy get(int idx)-et, amely visszaadja az idx. elem értékét! Ne feledkezz meg az elemek felszabadításáról sem! 2
Megoldás: main.cpp #include <iostream> #include "vector.h" using namespace std; int main() { Vector a(5); int i; for (i = 0; i < 5; i++) { a.set(i, i * 4.0); for (i = 0; i < 5; i++) { cout << a.get(i) << " "; cout << endl; 3
Megoldás: vector.h class Vector { double * elemek; int mennyi; public: Vector(int mennyi); ~Vector(); void set(int idx, double v); double get(int idx) const; ; 4
Megoldás: vector.cpp #include "vector.h" Vector::Vector(int mennyi) { this->mennyi = mennyi; elemek = new double[mennyi]; Vector::~Vector() { delete [] elemek; elemek = 0; void Vector::set(int idx, double v) { elemek[idx] = v; double Vector::get(int idx) const { return elemek[idx]; 5
Probléma Szeretnénk elérni, hogy a Vector osztályra is használhassuk a +, -, *, /, =, +=, [] stb. operátorokat: Vector a(5), b(5); a[4] = 3; b[2] = a[1] * 3; a += b; 6
Megoldás 1.: Találjuk ki, hogy mit jelentsenek a különböző operátorok a Vector osztályra alkalmazva 2.: Magyarázzuk el a C++-nak, hogy hogy kell értelmeznie az operátorokat Operátor túlterhelés 7
Operátor túlterhelés Terheljük túl a += operátort úgy, hogy az alábbi kifejezés hatása Vector a(5), b(5); a += b; az legyen, hogy a vektor minden i. eleméhez hozzáadjuk b i. elemét! 8
Operátor túlterhelés class Vector { double * elemek; int mennyi; public: Vector(int mennyi); ~Vector(); void set(int idx, double v); double get(int idx) const; ; void operator+=(const Vector & v) { int i; for (i = 0; i < mennyi; i++) elemek[i] += v.elemek[i]; 9
Operátor túlterhelés void operator+=(const Vector & v) { int i; for (i = 0; i < mennyi; i++) elemek[i] += v.elemek[i]; Vector a(5), b(5); a += b; 10
Operátor túlterhelés void operator+=(const Vector & v) { int i; for (i = 0; i < mennyi; i++) elemek[i] += v.elemek[i]; Vector a(5), b(5); a += b; 11
Operátor túlterhelés int a1, a2, a3; a1 += a2 += a3; Ennek mintájára: Vector a(5), b(5), c(5); a += b += c; 12
Operátor túlterhelés int a1, a2, a3; a1 += a2 += a3; Működik? Vector a(5), b(5), c(5); a += b += c; 13
Operátor túlterhelés void operator+=(const Vector & v) { int i; for (i = 0; i < mennyi; i++) elemek[i] += v.elemek[i]; Vector a(5), b(5), c(5); a += b += c; 14
Operátor túlterhelés void operator+=(const Vector & v) { int i; for (i = 0; i < mennyi; i++) elemek[i] += v.elemek[i]; Vector a(5), b(5), c(5); Kiértékelődik az operator+= által a += b += c; 15
Operátor túlterhelés void operator+=(const Vector & v) { int i; for (i = 0; i < mennyi; i++) elemek[i] += v.elemek[i]; Vector a(5), b(5), c(5); Az operator+= visszatérési értéke helyettesítődik be (ha lenne ) a += void 16
Operátor túlterhelés Vector & operator+=(const Vector & v) { int i; for (i = 0; i < mennyi; i++) elemek[i] += v.elemek[i]; return *this; Vector a(5), b(5), c(5); Kiértékelődik az operator+= által a += b += c 17
Operátor túlterhelés Vector & operator+=(const Vector & v) { int i; for (i = 0; i < mennyi; i++) elemek[i] += v.elemek[i]; return *this; Vector a(5), b(5), c(5); Behelyettesítődik b referenciájával! a += b 18
Operátor túlterhelés Vector & operator+=(const Vector & v) { int i; for (i = 0; i < mennyi; i++) elemek[i] += v.elemek[i]; return *this; Vector a(5), b(5), c(5); Újra meghívódik az operator+= függvény! a += b 19
= operátor Vector a(5), b(5); Vector c = a; // másoló konstruktor Ehhez viszont a = operátort kell túlterhelnünk: b = a; 20
= operátor Mi hiányzik? class Vector { double * elemek; int mennyi; public: [ ] Vector & operator = (const Vector & v) { mennyi = v.mennyi; elemek = new double[mennyi]; int i; for (i = 0; i < mennyi; i++) elemek[i] = v.elemek[i]; return *this; ; 21
class Vector { double * elemek; int mennyi; public: [ ] ; = operátor Fel kell szabadítani a már meglévő tömböt! (Ez nem konstruktor, itt már létezik az objektum!) Vector & operator = (const Vector & v) { delete [] elemek; mennyi = v.mennyi; elemek = new double[mennyi]; int i; for (i = 0; i < mennyi; i++) elemek[i] = v.elemek[i]; return *this; 22
= operátor Mi történik ekkor? Vector a; a = a; class Vector { double * elemek; int mennyi; public: [ ] Vector & operator = (const Vector & v) { delete [] elemek; mennyi = v.mennyi; elemek = new double[mennyi]; int i; for (i = 0; i < mennyi; i++) elemek[i] = v.elemek[i]; return *this; ; 23
= operátor Az értékadó operátort létező objektumra hívjuk meg, esetünkben a Vector típusú objektumnak már létezik egy belső dinamikus tömbje. Hogy elkerüljük a memóriaszivárgást, ezt előbb fel kell szabadítani, hiszen a helyére újat hozunk létre. Az önmagának való értékadás hatására azonban felszabadul a tömb, amiből másolunk, ezt ne engedjük meg! 24
A helyes = operátor class Vector { double * elemek; int mennyi; public: [ ] ; Vector & operator = (const Vector & v) { if (&v == this) { return *this; delete [] elemek; mennyi = v.mennyi; elemek = new double[mennyi]; int i; for (i = 0; i < mennyi; i++) elemek[i] = v.elemek[i]; return *this; Önmagának való értékadás esetén kilépünk. Töröljük a régi tartalmat. Végül másolunk. 25
Destruktor, másoló konstruktor, = operátor A destruktor feladata: Felszabadítás Másoló konstruktor feladata: másolás = operátor feladata: Felszabadítjuk a meglévő adatokat, majd másolunk Az ismétlődő feladatok: felszabadítás és másolás Ötlet: Írjunk egy private clear függvényt a felszabadításra, és egy private copy függvényt a másolásra 26
Destruktor, másoló konstruktor, = operátor class Vector { double * elemek; int mennyi; void clear() { delete [] elemek; void copy(const Vector & v) { mennyi = v.mennyi; elemek = new double[mennyi]; int i; for (i = 0; i < mennyi; i++) elemek[i] = v.elemek[i]; 27
Destruktor, másoló konstruktor, = operátor public: [ ] Vector(const Vector & v) { copy(v); ~Vector() { clear(); Vector & operator = (const Vector & v) { if (&v == this) { return *this; clear(); copy(v); return *this; ; 28
Ha egy Ososztaly nevű osztályból származtattunk public: [ ] Vector(const Vector & v): Ososztály(v) { copy(v); ; ~Vector() { clear(); Vector & operator = (const Vector & v) { Ne felejtsük el meghívni a if (&v == this) { return *this; clear(); Ososztaly::operator=(v); copy(v); return *this; szülő másoló konstruktorát és a szülő = operátorát! 29
Operátor túlterhelés Írjunk operátort, amely segítségével jobbról megszorozhatjuk a vektort, a kifejezés egy új vektort ad eredményül! Vector a(5), b(5); a = b * 4; 30
Operátor túlterhelés class Vector { double * elemek; int mennyi; public: [ ] Vector operator * (double lambda) { Vector res = *this; int i = mennyi; while (i--) res.elemek[i] = elemek[i]*lambda; return res; ; 31
Operátor túlterhelés A már meglevő operátorok alapján működik-e a következő kód: Vector a(5), b(5); a = 4 * b; 32
Operátor túlterhelés A már meglevő operátorok alapján működik-e a következő kód: Vector a(5), b(5); a = 4 * b; Nem, mert a jelenlegi * operátor csak a jobbról való szorzásra működik! 33
Operátor túlterhelés Probléma: Olyan * operátor kell, amely bal oldala double, míg jobb oldala Vector & Két paraméteres operátor függvényre van szükségünk Ez már nem lehet tagja az osztálynak! 34
Operátor túlterhelés Az új operátort az osztályon kívül kell definiálni! Vector operator * (double lambda, const Vector & v) { Vector res = v; int i = v.mennyi; while (i--) res.elemek[i] = v.elemek[i]*lambda; return res; Mi ezzel a probléma? 35
Operátor túlterhelés A függvény nem éri el a private adattagokat! Engedjük meg kivételesen neki, hogy hozzáférhessen! Class Vector { double * elemek; int mennyi; public: [ ] friend Vector operator * ( double lambda, const Vector & v); ; 36
Operátor túlterhelés Prefix operátor: Vector & operator++ () { int i = mennyi; while (i--) elemek[i]++; return *this; Vector a(5); ++a; 37
Operátor túlterhelés Prefix operátor: Vector & operator++ () { int i = mennyi; while (i--) elemek[i]++; return *this; Vector a(5); ++a; Postfix operátor: Vector & operator++ (int) { int i = mennyi; Vector b(5); while (i--) b++; elemek[i]++; return *this; 38
Érdekesebb operátorok Használjuk tömbként az osztályunkat! class Vector { double * elemek; int mennyi; public: [ ] ; double & operator[](int index) { Vector a(5); a[0] = 32; a[3] = 10; return elemek[index]; 39
Érdekesebb operátorok Írjuk felül a << operátort úgy, hogy ki tudjuk íratni a vektorunkat! class Vector { double * elemek; int mennyi; public: [ ] friend ostream & operator<<(ostream & os, const Vector & v) { for (int i = 0; i < v.mennyi; i++) os<<v.elemek[i]<<" "; return os; ; Vector a(5), b(5); cout << a << endl << b << endl; 40
A túlterhelhető operátorok + - * / % ^ & -> ~! = < > += [] -= *= %= ^= &= = () << >> && =!= <= >= >>= <<= ++ - ->*, new new[] delete delete[] 41
Operátor túlterhelés Új operátort nem hozhatunk létre Az új operátoroknak tartalmazniuk kell újonnan definiált típusokat (vagy azok tagfüggvényeinek kell lenniük) Precedencia nem változtatható Asszociativitás nem változtatható 42
Feladat Add hozzá a következő operátorokat a Vector osztályhoz: -, / : Páronként kivonják / elosztják az elemeket, az eredmény egy új vektor. * : Ha két vektort szorzunk össze, akkor a Skaláris szorzatot kapjuk 43
Feladat Add hozzá a következő operátorokat a Vector osztályhoz: << Ha a jobb oldal egész szám, a bal oldal Vector, akkor a jobb oldalon látható értékkel tolja el az elemeket annyival kisebb indexű helyre. >> Ugyanaz, mint az előző, csak másik irányba végezzük az eltolást. 44