Kihagyás

4. gyakorlat

operátor kiterjesztés

Az eddig ismert alap típusokra léteznek operátorok. Egy egész típusú változó operátora lehet pl. összeadás (+), kivonás (-), értékadás (=), inkrementálás (++). Ezeknek a jól megszokott hatásuk van a változó értékére. Az általunk megvalósított osztályokra azonban alapból nem működnek ezek az operátorok. A C++ azonban lehetőséget kínál rá, hogy saját típusra is értelmezzük az operátorokat, csak meg kell mondanunk, hogy mi történjen pl. összeadás esetén. Ezt operátor kiterjesztésnek hívjuk.

Vegyük alapul, hogyan működik pl. az összeadás operátor az egész számokra. Mit is jelent, ha leírjuk az a+b kifejezést:

1
a+b => össze kell adni: a, b => összead(a,b) => +(a,b) => operator+(a,b)

Fontos észre venni, hogy az a és b értéke nem változik meg, és a kifejezés eredményének tárolására egy új ,,objektum'' jön létre (3+2 esetén 5, ami szintén egy egész), tehát van visszatérési értéke. Egész számok esetén ez egy egész szám lesz, tehát ha kiegészítjük a fentit a visszatérési értékkel, akkor a következő módon fog kinézni:

1
int operator+(a,b) => int operator+(int, int)

A saját osztályunkra nézve hasonlóan néz ki, csak meg kell határoznunk, hogy mit mivel szeretnénk összeadni és ennek mi lesz az eredménye. Ha vesszük a Kurzus osztályt és hozzáadunk egy Hallgato osztályt, akkor egy bővített kurzust kapunk, ami szintén egy Kurzus. Tehát a két paraméter: Kurzus, Hallgato (ami valójában konstans referencia lesz). A visszatérési érték típusa Kurzus.

1
Kurzus operator+(Kurzus, Hallgato)

Mivel az operátor viselkedése szorosan kötődik a Kurzushoz (így fogalmaztuk meg) OOP szerint az osztályon végzett műveletet rakjuk be az osztályba magába. Ezt a metódust megírhatjuk mint a Kurzus osztály tagfüggvénye. Ha tagfüggvény, akkor az első paraméter (Kurzus) adott is, hiszen ha meghívjuk egy objektum operator+ függvényét, az első paraméter maga az objektum, így a paraméterlistából azt el is hagyhatjuk.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#define KURZUS_LIMIT 10

class Kurzus {
  string nev, kod;
  unsigned felvette = 0;
  Hallgato hallgatok[KURZUS_LIMIT];
public:
  Kurzus(const string& nev, const string& kod) : nev(nev), kod(kod) {
  }

  Kurzus operator+(const Hallgato& h) const { // const, mert a Kurzus nem valtozik meg
    Kurzus res = *this;
    if ( res.felvette == KURZUS_LIMIT ) {
      cout << "A kurzus megtelt. Nem lehet felvenni." << endl;
    } else {
      res.hallgatok[res.felvette++] = h;
    }

    return res; //Kurzussal térünk vissza
  }
};

Ez hozzáadja a Kurzus-hoz a kapott hallgatót, ha még elfér. Hasonló szemantikája van, mint az egész számoknál.

A használata a következő:

1
2
3
4
5
6
7
8
int main() {

  Kurzus kurzus("Programozas II.", "IB302G-1");
  Hallgato hallgato("Kreatív Név", "KNAA.SZE");

  kurzus = kurzus + hallgato;
  // a levezetésünk szerint valójában: kurzus = kurzus.operator+(hallgato);
}

Az egészeknél ha egy változóhoz hozzá akarunk adni egy értéket az összeadást a következőképpen kell használnunk:

1
a = a + 5
Ennek rövidített változata:
1
a += 5
Ezt szintén megtehetjük a saját osztályunkra ha az operator+=-t definiáljuk felül.

 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
const unsigned KURZUS_LIMIT = 10;

class Kurzus {
  string nev, kod;
  unsigned felvette = 0;
  Hallgato hallgatok[KURZUS_LIMIT];
public:
  Kurzus(const string& nev, const string& kod) : nev(nev), kod(kod) {
  }

  Kurzus& operator+=(const Hallgato& h) { // NEM konstans, az aktuális objektum módosul
    if ( felvette == KURZUS_LIMIT ) {
      cout << "A kurzus megtelt. Nem lehet felvenni." << endl;
    } else {
      hallgatok[felvette++] = h;
    }

    return *this;
  }
};

int main() {

  Kurzus k("Programozas II", "IB302G-1");
  Hallgato h("Kreatív Név", "KNAA.SZE");

  k += h1;
}

Ez a megvalósítás meglehetősen hasonló, azonban fontos kiemelni néhány jelentős különbséget! Az első, hogy a visszatérési érték nem Kurzus, hanem Kurzus& és nem hozunk létre egy újabb Kurzus típusú objektumot. Ennek oka a következő:

Mikor az operator+-t használtuk, akkor az operandusok változatlanok maradtak, és létrejött egy új objektum, mely az összeget tárolta el és teljesen független a két operandustól. Ezt az alábbi ábra vizualizálja, melyen két Kurzus objektumot összeadva lérrejön egy harmadik Kurzus:

operator+

Ha ezt k1 = k1 + k2; formában akarjuk használni, akkor akkor az eredeti változó objektuma felülíródik az eredmény objektummal. Ezt a kövezkező két ábra szemlélteti.

operator+

operator+

Ilyen esetekben jobb az operator+= használata. Ennél már magában az operátorban benne van az értékadás, tehát az eredeti objektum módosul. Ezért is kell referenciával visszatérni, hiszen ugyan arról az objektumról beszélhetünk.

A hallgató hozzáadása is hasonlóan működik:

operator+

Fontos, hogy az eredeti Kurzus módosult, a Hallgato bekerült a kurzusra, de nem módosult. Ezért lehetett a paraméterben const &.

operator+

Mivel az operátorokat függvényeknek vettük, ezért a meghatározásoknál nemcsak az operátor neve, hanem a paraméter típusa is meghatározza. A Kurzus osztályban lehet két operator+=, az egyik Hallgato, a másik Kurzus paraméterrel. Az így kiegészített Kurzus osztály:

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#define KURZUS_LIMIT 10

class Kurzus {
  string nev, kod;
  unsigned felvette = 0;
  Hallgato hallgatok[KURZUS_LIMIT];
public:
  Kurzus(const string& nev, const string& kod) : nev(nev), kod(kod) {
  }

  Kurzus& operator+=(const Hallgato& h) {
    if ( felvette == KURZUS_LIMIT ) {
      cout << "A kurzus megtelt. Nem lehet felvenni." << endl;
    }
    else {
      hallgatok[felvette++] = h;
    }

    return *this;
  }

  Kurzus& operator+=(const Kurzus& k) {
    if ( k.felvette + this->felvette >= KURZUS_LIMIT ) {
      cout << "A két kurzus nem vonható össze. Nincs elég hely" << endl;
    }
    else {
      for ( unsigned i = 0; i < k.felvette; ++i ) {
        hallgatok[felvette++] = k.hallgatok[i];
      }
    }

    return *this;
  }
};

int main() {

  Kurzus k("Programozas II", "IB302G-1");
  Kurzus k2("Programozas II 2", "IB302G-13");

  Hallgato h1("Kreatív Név", "KNAA.SZE");
  Hallgato h2("Kreatívabb Név", "KBAA.SZE");

  k += h1;
  k2 += h2;
  k += k2;
}

A += operátor hasonlóan működik Kurzus-ra, mint Hallgató esetén:

operator+

Természetesen, a kurzushoz adott paraméter most sem módosul, így az lehet const &.

operator+

indexer operátor

Az indexer operátor különleges operátornak tekinthető. Szemantikáját általában tömbökhöz kötjük, elemek eléréséhez (de tetszőleges értelmezést adhatunk neki). Ehhez az operator[](int) metódust kell kiterjeszteni. Ha egy tömbben el akarunk érni egy elemet, azzal több dolgot is tehetünk. Egyszerűen lekérdezhetjük (módosítás nélkül):

1
cout << int_array[2];

Vagy az értéket módosíthatjuk:

1
int_array[2] = 55;

Mivel az első esetben csak az értékre vagyunk kíváncsiak, nem kell tudnunk módosítani azt. A második esetben már a visszaadott értéket szeretnénk módosítani, vagyis igazából az int_array objektumban lévő 2. indexen lévő elemet szeretnénk felülírni.

Azt már vettük, hogy ha egy függvény módosíthatja vagy sem az objektumot azt a const kulcsszóval jelezhetjük. Az első esetben nem módosíthatjuk tehát használjuk a const kulcsszót, a második esetben pedig nem.

Természetesen nem elég a metódust konstanssá tenni, hiszen ha a visszatérési értékben kiszivárog egy "kiskapu", akkor az objektumot elronthatjuk. Ennek kivédése érdekében a visszatérési értéket is máshogy kell megadnunk. Erről már szó volt a const-ról szóló részben. Az indexer operátorok kiterjesztése a Kurzus osztályra (annak éri meg, hiszen annak van tömbhöz köthető jellege):

 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
#define KURZUS_LIMIT 10

class Kurzus {
  string nev, kod;
  unsigned felvette = 0;
  Hallgato hallgatok[KURZUS_LIMIT];
public:
  Kurzus(const string& nev, const string& kod) : nev(nev), kod(kod) {
  }

  //eset 1
  Hallgato& operator[](unsigned i) {
    if (i < felvette)
      return hallgatok[i];
    // valodi hibakezeles szukseges, ami majd kesobb jon
    cout << "HIBA: nincs " << i << " darab hallgato a kurzushoz rendelve" << endl;
    return hallgatok[0]; // nem szep, de ...
  }

  //eset 2
  const Hallgato& operator[](unsigned i) const {
    if (i < felvette)
      return hallgatok[i];
    // valodi hibakezeles szukseges, ami majd kesobb jon
    cout << "HIBA (const): nincs " << i<< " darab hallgato a kurzushoz rendelve" << endl;
    return hallgatok[0]; // nem szep, de ...
  }
};

Az első esetben egy referenciát adunk vissza, tehát ahova visszaadjuk, ott elérik az adott elemet pl. felül is írhatják az értékét. Mivel ezzel módosulhat a Kurzus, ez a függvény nem is const. A második esetben szintén referenciát adunk vissza, azonban ez most const & tehát nem írható felül az értéke. Ez biztosítja, hogy akármit is teszünk, a Kurzus nem fog megváltozni, így ez a függvény lehet const.

++ / -- operátorok

A ++a, a++, --a és a-- kifejezések mindenki számára ismerősek. A különbség a prefix és a postfix inkrementálás (dekrementálás) közt annyi pl. intekre nézve, hogy a prefix verzióban egy kifejezés kiértékelésekor már a megnövelt (csökkentett) érték van használva, míg postfix esetben a kifejezés kiértékeléskor még a nem megnövelt (csökkentett) értéket használjuk fel.

Mivel ezek is operátorok, ezeket is felül lehet definiálni. Azonban a pre s post verziók közt leírásban nem sok különbséget találunk, így valahogyan meg kell különböztetni azokat. Az operator kiterjesztése során a post verzióhoz egy plusz (nem használt) paramétert adunk, csupán a megkülönböztetés miatt.

Működésük miatt a visszatérési értékük sem teljesen egyezik meg. Nézzünk egy példát rá!

 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
#include <iostream>
class Kurzus {
  unsigned letszam = 0;

 public:
  Kurzus operator++(int) {  // a nem hasznalt parameter mutatja, hogy post incrementalast hajtunk vegre
    std::cout << "Udv a Kurzus post ++ operatorabol" << std::endl;
    Kurzus tmp = *this; // Mivel a post verzio eseteben a regi allapotot szoktuk visszaadni,
    // arrol kell egy mentest keszitenunk.
    letszam++;
    return tmp; // mivel egy lokalis valtozot adunk vissza, ami megszunik a metodus vegen
    // nem adhatunk vissza pl. referenciat.
  }

  Kurzus& operator++() {
      std::cout << "Udv a Kurzus pre ++ operatorabol" << std::endl;
      ++letszam;
      return *this; // mivel a pre verzioban a valtoztatott erteket adjuk vissza,
      // nem kell masolatot kesziteni es visszaadhatjuk az objektumot magat, hiszen
      // a mar frissitett adat kell nekunk.
  }
};

int main() {
    Kurzus k;
    ++k;
    k++;
}

Hasznos a kettőt együtt megvalósítani s ekkor az egyiket a másik felhasználásával, hiszen ekkor hiba javításakor csak egy helyen kell módosítanunk.

 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
31
#include <iostream>
class Kurzus {
  unsigned letszam = 0;

 public:
  Kurzus operator++(int) {  // a nem hasznalt parameter mutatja, hogy post incrementalast hajtunk vegre
    Kurzus tmp = *this; // Mivel a post verzio eseteben a regi allapotot szoktuk visszaadni,
    // arrol kell egy mentest keszitenunk.
    /* Az alabbi resz ugyan az mint a pre verzioban. Hivjuk azt
    * Igaz, most csak egy sor, de ez akar tobb, osszetettebb kod is lehet.
    letszam++;
    */
    operator++();   // Ezzel a pre verziot meghivtuk, a noveles megtortent
    // az elozo ertek ugyan ugy lementesre kerult, a funkcionalitas megmaradt.
    return tmp; // mivel egy lokalis valtozot adunk vissza, ami megszunik a metodus vegen
    // nem adhatunk vissza pl. referenciat.
  }

  Kurzus& operator++() {
      ++letszam;
      return *this; // mivel a pre verzioban a valtoztatott erteket adjuk vissza,
      // nem kell masolatot kesziteni es visszaadhatjuk az objektumot magat, hiszen
      // a mar frissitett adat kell nekunk.
  }
};

int main() {
    Kurzus k;
    ++k;
    k++;
}

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

Ahogy azt előzőekben 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;
}

Órai gyakorló feladatok

  1. A múlt órán elkészült a Kurzus osztály. A hallgato-kurzus-1.cpp fájlban található az osztály megvalósítása, illetve van egy Hallgató osztály is.

    • Nézzük meg, hogy milyen paraméterátadások vannak (const string&)
    • Nézzük meg, hogy a metódus milyen módosítót kapott (const)
  2. Valósítsuk meg, hogy a kurzushoz hozzá lehessen adni hallgatót

    • A hozzáadáshoz a + operátort használjuk
    • Valósítsuk meg a += operátort is
    • A hibakezeléssel most nem kell foglalkozni, azaz ha a kurzus tele van, akkor csak írjuk ki a képernyőre, hogy nem sikerült felvenni a halgatót, és ne csináljunk semmit sem.
  3. Valósítsuk meg a kurzusok "egyesítését"

    • Össze lehetesen adni két kurzust, ahol a második kurzus hallgatóit hozzáadjuk az elsőhöz (a + operator alkalmazásával)
    • Valósítsuk meg Kurzusokra a += operátort is
    • A hibakezeléssel itt sem kell foglalkozni, azaz ha a második kurzus hallgatói nem férnek fel az elsőre, akkor csak adjunk hibaüzenetet
  4. Valósítsuk meg az index operátorát is a Kurzus osztálynak

    • Lehessen módosítani az adott elemet
    • const objektumra is működjön, és ilyenkor nem módosítható módon adja vissza az i-edik elemet
    • Valódi hibakezeléssel nem kell foglalkozni, elég kiírni a képernyőre, hogy nincs olyan indexű elem, és a 0. indexűt kell visszaadni (a 0. indexű biztosan létezik, legfeljebb "üres")

Megoldások: 2.-3. feladat, 4. feladat

Órai gyakorló feladatsor II

  1. Legyen egy dokumentum osztály

    • címmel (str)
    • tartalommal (str, default param: "")
    • szerzővel (str, default param: "anonym")
    • megnyitasokkal (unsigned, kezdő érték 0)
  2. 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)
  3. 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)
  4. Valósítsd meg a += operátort, mely egy stringet fűz a tartalomhoz
  5. 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)
  6. 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!
  7. 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

Megoldás

Otthoni gyakorló feladatok

  • Készíts egy Meme osztály. Két string típusú adattagja van, láthatóságuk legyen privát. Nevük: szoveg, kep. Legyen hozzá egy két paraméteres publikus konstruktor, amivel beállítjuk az adattagokat. Írj gettereket is!

    Részmegoldás
  • Készíts egy void print_meme() metódust, melynek paramétere egy ^^konstans^^ Meme referencia. Egy sorban kiírja a meme szövegét majd egy rákövetkező sorban a képet is. Hogyan kell módosítani az előbb megírt gettereket?

    Részmegoldás
  • Készíts egy MemeGyujtemeny osztályt. Ez maximum tíz Memét tud letárolni egy tömbben. Mivel kell kiegészíteni a Meme osztályt, hogy működjön a tömb létrehozás?

    Részmegoldás
  • Definiáld felül a & és a &= operátorokat a MemeGyujtemeny osztályban úgy, hogy a baloldali operandus egy MemeGyujtemeny legyen, a jobboldali pedig egy Meme. A MemeGyujtemeny tömbjében az első üres helyre kerüljön be az új meme. Ennek megfelelően módosítsuk úgy a MemeGyujtemeny osztályt, hogy le tudja tárolni, aktuálisan hány meme-t tárol!

    • MemeGyujtemeny & Meme művelet esetén ne legyen módosítva egyik operandus sem, a visszatérési érték pedig egy lemásolt, bővített MemeGyujtemeny objektum legyen.
    • MemeGyujtemeny &= Meme művelet esetén a bal oldali MemeGyujtemeny objektum módosuljon, ahhoz adjuk hozzá az új elemet. MemeGyujtemeny referencia legyen visszaadva.
    • Ha nincs már hely a gyűjteményben, ne legyen hozzáadás, csak történjen meg a megfelelő típusok visszadaása (módosítás nélküli másolat illetve módosítás nélküli bal oldali operandus)
    Részmegoldás
  • Definiáld felül az indexer [] operátort a MemeGyujtemeny osztályban úgy, hogy adja vissza az i-edik letárolt memét. Legyen konstans és nem konstans verziója is. Túlindexelés esetén legyen kiírva a "Hiba" szöveg a cout-ra és a 0-ás indexű meme legyen visszaadva.

    • Hozz létre az osztályokon kívül egy void print_gyujtemeny függvényt, amely konstans MemeGyujtemeny paramétert vár és egy for ciklusban kiíratja a print_meme segítségével az összes letárolt memét. Gettert csak az aktuális meme szám lekérdezéséhez írj!
    Részmegoldás
Teljes megoldás

Otthoni gyakorló feladatsor II

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

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

    Részmegoldás
  4. 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
  5. 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: 2022-09-28 21:54:57