Útmutató a 11. laborhoz

Felkészülés a laborra

  • Ismételje át a többszörös öröklésről és a perzisztenciáról tanultakat (9. előadás anyaga)!
  • Oldja meg a Clonable Hetero feladatot!

A laborgyakorlat az ún. rombusz vagy diamond öröklés problémáját, valamint egy összetettebb perzisztencia megvalósítást mutat be konkrét példákon keresztül.

A laborfoglalkozás végén az irodistak.h, a pirodistak.h és az elkeszult.h fájlt kell feltöltenie a Jporta rendszerbe (11. önellenőrző feladat), hogy megkapja a labor elvégzéséért járó pontot! Legalább az első négy tesztesetet (ELEKESZULT >= 4) hibátlanul meg kell oldania! A feladatok feltöltésére a laborgyakorlatot követő szombat 06:00-ig van lehetősége.

Feladatok megoldásokkal

Az ún. rombusz v. diamond öröklés problémájának elemzése (~30')

  1. Töltse le az előkészített fájlokat https://git.ik.bme.hu/Prog2/labor_peldak/lab_11, majd nyissa meg a diamond alkatalógusban található projektet! Röviden elemezze a tesztprogramot (diamond_test.cpp)! A palkalmazott.h és a perz_test.cpp állományok elemzését és megértését hagyhatja későbbre!) Vegye észre:
    • Az elkeszult állományban definiált ELKESZULT makró értékétől függően egyre több teszteset hajtódik végre.
    • ELKESZULT >=2 értékek tesztesetei a perz_test.cpp állományban vannak megvalósítva.
    • Egyes tesztesetek csak a képernyőre írnak, de van olyan is, ami gtest_lite ellenőrző funkcióit használja és a kiírásokat úgy ellenőrzi, hogy a standard output helyett egy std::stringstream típusú adatfolyamba irányítja az írást, majd az adatfolyam std::string "arcát" felhasználva hasonlít az elvárt kimenethez.
    • ELKESZULT <= 1 esetben az Alkalmazott alaposztály (alkalmazott.h) konstruktora minden lefutáskor egy nagy 'A' betűt ír ki, a destruktor pedig kiírja, hogy "megszunik".

    Állítsa az ELKESZULT makró értékét 0-ra, majd fordítson és futtasson! A mintaprogram futtatásával meggyőződhet arról, hogy az Alkalmazott osztály adattagjai megduplázódtak a HatIdCsV ill. HatIdCsVezH objektumokban. Különösen feltűnő ez a fizetéseknél, amit szándékosan eltérő értékkel inicializáltunk:

    AAAAAAA
    Lusta Dick fizetese: 100
    HatIdeju: Grabovszki fizetese: 300
    CsopVez: Mr. Gatto  fizetese: 500
    HatIdCsv:
       CsopVez: Mr. Tejfel fizetese: 1200
       HatIdeju: Mr. Tejfel fizetese: 6000
    HatIdCsVezH: HatIdCsv:
       CsopVez: Safranek fizetese: 1400
       HatIdeju: Safranek fizetese: 7000
    Megszunik: Lusta Dick fizetese: 100
    Megszunik: Grabovszki fizetese: 300
    Megszunik: Mr. Gatto  fizetese: 500
    Megszunik: Mr. Tejfel fizetese: 6000
    Megszunik: Mr. Tejfel fizetese: 1200
    Megszunik: Safranek fizetese: 7000
    Megszunik: Safranek fizetese: 1400
    
    • Figyelje meg, hogy az első sorban 7 db A betű jelent meg, ami azt jelenti, hogy 7-szer hívódott meg az Alkalmazott konstruktora, holott 5 db objektumot (Alkalmazott, HatIdeju, CsopVez, HatIdCsV, és HatIdCsVezH) hoztunk létre a tesztprogramban.
    • Magyarázza meg, hogy miért kell a típuskonverzió a main függvény azon soránál, ahol a HatIdCsV és a HatIdCsVezH típusú objektumokat akarunk a tárolóba tenni! Akkor is kell, ha virtuális öröklést használunk?
    • Figyelje meg, hogy az Alkalmazott destruktorából hívott kiir() sosem a származtatott osztály kiir() függvénye! Miért?
  2. Az üresen hagyott helyek segítenek!
  3. Elődadáson is látta, hogy a többszöröződéseket az okozza, hogy az Alkalmazott alaposztály adattagjai két öröklési útvonalon is beépülnek a HatIdCsV osztályba. Ezzel szemben virtuális öröklés során az adattagok pointereken keresztül épülnek be, így azok az öröklési láncon összeérve azonos adattagra mutatnak.
    Módosítsa az irodistak.h fájlt úgy, hogy a HatIdeju és a CsopVez osztályok virtuális örökléssel jöjjenek létre az Alkalmazott osztályból! Ne felejtse el az Alkalmazott alaposztály megfelelő konstruktorát meghívni az öröklési lánc végén! Ha elfelejti, akkor a default konstruktor hívódik!

    Állítsa az elkeszult.h fájlban definiált ELKESZULT makró értékét 1-re, majd fordítson és futtasson! Figyelje meg, hogy mi változott! Ha mindent jól módosított, akkor az alábbi kimenetet kell kapnia:
    AAAAA
    Lusta Dick fizetese: 100
    HatIdeju: Grabovszki fizetese: 300
    CsopVez: Mr. Gatto  fizetese: 500
    HatIdCsv:
       CsopVez: Mr. Tejfel fizetese: 600
       HatIdeju: Mr. Tejfel fizetese: 600
    HatIdCsVezH: HatIdCsv:
       CsopVez: Safranek fizetese: 700
       HatIdeju: Safranek fizetese: 700
    Megszunik: Lusta Dick fizetese: 100
    Megszunik: Grabovszki fizetese: 300
    Megszunik: Mr. Gatto  fizetese: 500
    Megszunik: Mr. Tejfel fizetese: 600
    Megszunik: Safranek fizetese: 700
    
  4. Elemezze az Iroda osztály megvalósítását az iroda.h fájlban! A tarolo_tipus-t azért vezettük be, hogy a tároló könnyen lecserélhető legyen bármilyen standard tárolóra. Figyelje meg, hogy az iterátor használatával nem korlátozódott indexelhető tárolókra a megoldás!

    Cserélje le az std::vector-t először std::list-re, majd std::deque-ra! Ne felejtse el a megfelelő header fájlt include-olni! Fordítsa le, majd futtassa az alkalmazást!

  5. Perzisztens viselkedés megvalósítása (~45')

  6. Egészítse ki a fenti feladatot úgy, hogy az irodisták állapota elmenthető és visszaállítható legyen! A 9. előadáson látottak alapján több lehetősége is van:
    • Többszörös öröklést alkalmazva hozza létre a perzisztens változatot. Ekkor az eredeti osztályt nem kell módosítani.
    • Módosítja az osztályokat a perzisztens viselkedés kialakítása érdekében.

    Az első megoldás tisztább, és ez a javasolt megoldás. Esetünkben külön figyelmet igényel a virtuális alaposztály konstruktorainak hívása, de ez nem egy megterhelő feladat. (Az öröklési láncok végén a virt. alaposztály megfelelő konstruktora hívódjon meg.)
    Alkalmazott osztály perzisztens változatát előre elkészítettük az első megoldásnak megfelelően a palkalmazott.h fájlban. Elemezze a megoldást, majd állítsa ELKESZULT makró értékét 2-re az elkeszult.h fájlban! Fordítson, futtasson!

  7. Készítse el a pirodistak.h fájlban a többi irodai alkalmazott perzisztens osztályát! Lépésenként haladva teszteljen a ELKESZULT makró segítségével (CsopVez = 3, HatIdeju = 4, Hat IdejuCsv = 5, HatIdejuCsVez = 6)!
  8. IMSC feladat: Az Iroda osztály egy heterogén tároló. A "benne levő" objektumok perzisztensek. Hogyan lehetne az egész tárolót perzisztens viselkedésűvé tenni? A kiírással nincs gond, de a visszatöltés?
    Kiíráskor ki kell írni valamilyen azonosítót, ami alapján visszaolvasáskor létre lehet hozni a megfelelő osztályt. OK, de hogyan lehet ezt könnyen bővíthetően megoldani? Az nem lenne túl szép, ha a tároló kódjába előre be kellene kódolni, hogy milyen objektumokat tárol!

  9. IMSC feladat: A fenti ötletek, valamint a korábban elkészített Clonable Hetero feladat alapján készítsen perzisztens viselkedésű, másolható generikus heterogén tárolót!
  10. Extra:
    A what alkatalógusban található programok rejtett programozási hibákra próbálják felhívni a figyelmet.
    • A secret nevű program az előadáson is mutatott Buffer Overflow hibát demonstrálja.
    • A hello_world nevű program az úgynevezett Use After Free hiba, viszonylag könnyen elkövethető változatát mutatja be.
    • A badguy nevű program szintén a Use After Free hibát demonstrálja kicsit komplikáltabb helyzetben.

    A témához kapcsolódó érdekes olvasmány: https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/

Jó munkát!

Szeberényi Imre

Utolsó frissítés: 2021-05-02 23.12