Kihagyás

5. gyakorlat

Ismétlés

  • const method
  • const referencia
  • operátor felüldefiniálás

További operátor felüldefiniálás

Ahogy azt előző órán láttuk, az operátor felüldefiniálás csak egy függvény / metódus megvalósítását jelenti. Ha egy osztálynak metódusként írunk meg egy operátort, akkor tudjuk, hogy az operátor első paraméterét elhagyhatjuk, hiszen az maga az objektum lesz. Természetesen, ha unáris (vagyis csak egy paramétert váró) operátorról van szó, akkor nem kell megadni semmit, hiszen csak az objektumra van szükség. Ilyen operátort pl. az operator++, operator--, operator! stb.

operator!

A felkijáltó jel használata gyakran a negálást jelenti. Ez egy paramétert vár, magát az objektumt. A Kurzus példánál maradva nézhetjük, hogy bizonyos kurzusok jóváhagyásosak. Ezt egy bool értékkel le tudjuk tárolni, azonban ha meg akarjuk változtatni, akkor akár egy negálással is megtehetjük. Erre nézzünk példát!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

class Kurzus {
    // ...
    bool jovahagyasos = false;

  public:
    Kurzus() = default;

    bool is_jovahagyasos() const { return jovahagyasos; }

    /**
    * A szamunkra fontos metodus / operator.
    * A metodus megcsereli a kurzus jovahagyasos jelleget.
    * Az uj jelleggel ter vissza bool formaban.
    */
    bool operator!() { // FOntos, hogy nem kell parameter, hiszen az egyetlen parameter az objektum lesz
        jovahagyasos = !jovahagyasos;
        return jovahagyasos;
    }
};

using namespace std;

int main() {
    Kurzus k;
    cout << "A kurzus jovahagyasos? " << k.is_jovahagyasos() << endl;
    !k;
    cout << "A kurzus jovahagyasos? " << k.is_jovahagyasos() << endl;
}

Konverziós operátorok

Sokszor konvertáljuk az adatokat és előfordulhat, hogy ezt egy általunk készített osztályra is meg kell tennünk. Ekkor írhatunk egy eljárást, ami mindig megcsinálja a konverziót, de kényelmesebb, ha meg tudjuk mondani, hogyan kell egy-egy adott konverziót megtenni.

Ha a Kurzus objektumunkat intté szeretnénk konvertálni, pl. a létszámmal legyen egyenlő a konverzió eredménye, akkor ezt megtehetjük egy konverziós operátorral.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <iostream>
using namespace std;
class Kurzus {
    unsigned letszam = 0;
  public:
    Kurzus(unsigned letszam) : letszam(letszam) {}

    operator unsigned() const { return letszam; }
};

int main() {
    Kurzus k(8);
    cout << (unsigned)k << endl;
}

A fenti példán láthatjuk, hogy a kiíratásban unsigned típusra konvertáltuk a kurzusunkat. Ezt a Kurzusban definiált konverziós operátorral tettük meg. Fontos, hogy konverziós operátor esetében nem kell kiírni a visszatérési típust! Nem kell, hiszen a konverziós operátor neve egyértelűen meghatározza a visszatérési típust. Amire szeretnénk konvertálni, az lesz az eredmény. Eltérő típus esetében fordítási hibát kapunk.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;
class Kurzus {
    unsigned letszam = 0;
  public:
    Kurzus(unsigned letszam) : letszam(letszam) {}

    operator unsigned() const { return letszam; }
};

void foo(unsigned a) {
    cout << "Foo a: " << a << endl;
}

int main() {
    Kurzus k(8);
    foo(k);
}

Ebben a példában láthatjuk, hogy a konverziót nem kell explicit módon kiírnunk, automatikusan megtörténik. Ez azonban problémához is vezethet, hiszen lehet, hogy nem akartunk konvertálni, vagy éppen másik típusra akartuk volna. Erre megoldást az jelentene, ha a konverzió csakis az első módszerrel történhetne meg, vagyis csak akkor, ha kiírjuk (explicit módon). Ennek a megoldása, ha a konverziót ellátjuk az explicit taggal. Ekkor az automatikus konverziót letiltjuk, kötelezően ki kell írni a kívánt konverziót.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;
class Kurzus {
    unsigned letszam = 0;
  public:
    Kurzus(unsigned letszam) : letszam(letszam) {}

    explicit operator unsigned() const { return letszam; }
};

void foo(unsigned a) {
    cout << "Foo a: " << a << endl;
}

int main() {
    Kurzus k(8);
    cout << (unsigned)k << endl;
    // foo(k); forditasi hiba, hiszen itt implicit (nem kiirt) konverzio tortenik
    // es mivel megkoveteltuk a kiirast, ezt hibat okoz.
}

A konverzió másik iránya

Eddig azt néztük meg, hogyan lehet egy már létező objektumból konvertálással egy értéket előállítani. A másik irány, hogy egy értékből az objektumot állítjuk elő. Erre példát a stringeknél láthattunk:

1
2
3
4
5
6
7
8
#include <iostream>
#include <string>
using namespace std;
int main() {
    string str = "Alma";
    // ebben az esetben az "Alma" const char* tipusrol konvertalunk std::string tipusra
    cout << str << endl;
}

Ezt a saját osztályunkra is megtehetjük. Mivel érték alapján kell létrehoznunk objektumot a konstruktorhoz kapcsolódó részeket kell vizsgálnunk.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
class Kurzus {
    unsigned letszam = 0;
  public:
    Kurzus() = default;
    Kurzus(unsigned letszam) : letszam(letszam) {}
    unsigned get_letszam() const { return letszam;}
};

using namespace std;

int main() {
    Kurzus k(5u);  // Fontos, hogy az unsigned jelzes nelkul, mar az int literal is konvertalodik
    Kurzus k2;
    Kurzus k3 = (unsigned)5;    // unsigned literal 5-s ertekkel Lehetne 5u is!

    cout << k.get_letszam() << endl
    << k2.get_letszam() << endl
    << k3.get_letszam() << endl;
}

Látszik, hogy az unsigned értékkel tudjuk a k3 objektumot inicializálni, hiszen létezik hozzá konstruktor. Terészetesen előfordulhat, hogy ezt az autmatikus konverziót (konstruktor hívást) szeretnénk elkerülni. Ekkor a konstruktornak is megadhatjuk, hogy explicit lehessen csak meghívni.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
class Kurzus {
    unsigned letszam = 0;
  public:
    Kurzus() = default;
    explicit Kurzus(unsigned letszam) : letszam(letszam) {}
    unsigned get_letszam() const { return letszam;}
};

using namespace std;

int main() {
    Kurzus k(5u);
    Kurzus k2;
    // Kurzus k3 = (unsigned)5;    // az explicit hivas miatt hibat kapunk
    Kurzus k4(5u);
    cout << k.get_letszam() << endl
    << k2.get_letszam() << endl
    // << k3.get_letszam() << endl
    << k4.get_letszam() << endl;
}

Feladatok

  1. Órai feladatok

    • Legyen egy dokumentum osztály

      • címmel (str)
      • tartalommal (str, default param: "")
      • szerzővel (str, default param: "anonym")
      • megnyitasokkal (unsigned, kezdő érték 0)
    • Készíts hozzá gettert a tanult módon

      • minden getter növelje a megnyitasokat eggyel. (Kivéve az azt lekérdező) (itt ugye muszáj, hogy mutable legyen)
    • Valósítsd meg a pre és post incremental operátort

      • növelje a megnyitasok számát eggyel (Mintha csak touch lenne vagy ilyesmi)
    • Valósítsd meg a += operátort, mely egy stringet fűz a tartalomhoz

    • Valósítsd meg a + operátort, mely egy stringet fűz a tartalomhoz, de azt csak egy saved_as című dokumentumba teszi bele. (save as.. funkcionalitás)

    • A Dokumentum konvertálható legyen unsigned típusra, mely a tartalom hosszát adja eredményül.

      • a konverzió ne történjen meg implicit módon!
    • A Dokumentum negálása (operator!) megvalósítása (unsigned visszatérési típussal)

      • üres dokumentum esetén Lorem ipsum.. szöveget írja bele
      • bármilyen tartalom esetében törölje azt.
      • visszatérési értékben az új tartalom hossza szerepeljen
  2. Gyakorló feladatsor

    • Készíts egy Zene osztályt. Adattagjai: hossz (egész szám, másodpercben értve), nev. A nev default értéke legyen: "a-moll". Legyen az osztálynak egy print() metódusa, amely kiíratja cout-ra az értékeit. Legyen egy két paraméteres konstruktora, amely beállítja a két adattagot és legyen egy egy paraméteres is, amiben csak a hosszt lehet állítani (a nev pedig a deafult értékét kapja). Ne lehessen int-et impliciten Zene osztályra konvertálni.

      Részmegoldás
    • Legyen megvalósítva a Zene+int operátor, ami az előző Zene alapján készít egy új, megnövelt hosszúságú zenét. Az eredeti hosszhoz a paraméterben kapott értéket kell hozzáadni.

    • Legyen megvalósítva az int+Zene operátor, ami ugyanazt csinálja, mint a Zene+int operátor.

      Részmegoldás
    • Legyen megvalósítva a Zene+=int operátor, mely a paraméterben kapott értékkel növeli a zene hosszát. Az aktuális Zene objektum legyen módosítva, ennek megfelelően Zene referencia legyen a visszatérési érték.

      Részmegoldás
    • Legyen megvalósítva a Zene osztályra prefix és a postfix ++ operátor is, ami növeli 1-gyel a zene hosszát.

      Részmegoldás
    Teljes megoldás

Utolsó frissítés: 2020-10-07 08:01:19