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
#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
= operátor
Fel kell szabadítani a már meglévő tömböt! (Ez nem konstruktor, itt már létezik az objektum!)
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; } };
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 { Önmagának való double * elemek; int mennyi; public: kilépünk. […] Vector & operator = (const Vector & v) { if (&v == this) { return *this; Töröljük a régi } delete [] elemek; mennyi = v.mennyi; elemek = new double[mennyi]; int i; for (i = 0; i < mennyi; i++) elemek[i] = v.elemek[i]; return *this; } Végül másolunk. 25 };
értékadás esetén
tartalmat.
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: Ne felejtsük el meghívni a … […] Vector(const Vector & v): Ososztály(v) { copy(v); } ~Vector() { clear(); konstruktorát… } Vector & operator = (const Vector & v) { if (&v == this) { … és a szülő = return *this; } clear(); Ososztaly::operator=(v); copy(v); return *this; } 29 };
… szülő másoló
operátorát!
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; while (i--) elemek[i]++; return *this; }
Vector b(5); b++;
38
Érdekesebb operátorok Használjuk tömbként az osztályunkat! class Vector { double * elemek; int mennyi; public: […] double & operator[](int index) { return elemek[index]; } }; Vector a(5); a[0] = 32; a[3] = 10; 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<
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