Útmutató a 7. laborhoz

Laborfeladatok megoldásokkal

A feladat az előadáson bemutatott alakzatos modellezési példa elemzése és kiegészítése. A laborfoglalkozás végén 5 db fájlt kell feltöltenie — egy részük már félig kész van — a Jporta rendszerbe (Alakzatlab, 7. önellenőrző feladat), hogy megkapja a labor elvégzéséért járó pontot! Legalább az első öt tesztesetet (ELEKESZULT >= 5) hibátlanul meg kell oldania! A feladatok feltöltésére a laborgyakorlatot követő szombat 06:00-ig van lehetősége.

Ahhoz, hogy a Jporta ellenőrizni tudja a stadard outputra (std::cout) történő kiírásokat kérjük, hogy minden esetben std::cout-ként hivatkozzon a szabványos kimenetre. Ne használjon továbbá std azonosítót (változónevet, függvénynevet, ...) a megoldásában!
  1. Töltse le a laborgyakorlathoz előkészített projektet: https://git.ik.bme.hu/Prog2/labor_peldak/lab_07!
  2. Elemezze, értse meg a programot, ami lényegében az 5. előadáson bemutatott síkidomokat modellező példa módosított változata. Kezdetben a modell csak szakaszt és kört tartalmaz, amit később bővíteni fogunk. Az új osztályok kipróbálását a főprogramban definiált ELKESZULT makróval lehet vezérelni. A megértést segíti a mellékelt osztálydiagram:

    Az előadás végén demózott SDL_alakzat példával ellentétben ez a program nem használ grafikát. Minden alakzathoz defináltunk egy globális inserter operátort (operator<<), ami kiírja az adott alakzat adatait egy adatfolyamra. A rajzol() tagfüggvények grafikus megjelenítés helyett ezt az insertert hívják.

    A enum nem osztály, de ez most lényegtelen.

    A Szin osztályt pedig lecseréltük egy enum-ra. Figyelje meg, hogy a csere nem jelentett az osztályokban semmilyen módosítást!

  3. Alapesetben (ELKESZULT==0) a program létrehoz egy szakaszt és egy kört, majd különböző módokon kiírja azok adatait ill. "felrajzolja" azokat. Futtassa a programot! Ezt az eredményt várta? Amennyiben nem, elemezze a kódot újra! Szükség esetén használja a fejlesztőeszköz debug funkcióját illetve kérjen segítséget a laborvezetőtől.
  4. Cserélje ki a

    virtual void rajzol() const = 0; kódsort a

    virtual void rajzol() const { std::cout << "Rajzol" << std::endl; }

    sorra az alakzat.h fájtban! Fordítsa le újra, és futtassa a programot! Mi tapasztal?

  5. Most törölje a virtual kulcsszót, majd fordítson, futtasson újra! Mi tapasztal?
  6. Legyen a függvény újból virtuális! Most törölje a const kulcsszót, majd fordítson, futtasson! Mi tapasztal?
  7. Állítsa vissza rajzol() tagfüggvény eredeti állapotát (const, tisztán virtuális), majd bővítse a modellt háromszöggel! A háromszög legyen létrehozható három pontból. A rajzol() tagfüggvénye a szakaszhoz hasolóan egyetlen sort írjon ki, amiben megjelenik a "Rajzol: Haromszog" szöveg, a három csúcspont koordinátája és az alakzat színe is. Valósítsa meg a hozzá tartozó globális insertert és a rajzol() tagfüggvényt. Az osztály deklarációit a haromszog.h, az implementációt, pedig a haromszog.cpp fájlba írja.
    Állítsa az ELKESZULT makró értékét 1-re, hogy az új osztály működését is ki tudja próbálni! Fordítson, futtasson!
    Amennyiben saját projektfájlt használ, ne felejtse el hozzáadni a projekthez az új fájlokat!


  8. Megvalósítottunk egy Ellipszis osztályt is az ellipszis.h fájlban. Az egyszerűség kedvéért az ellipszist a Kor osztályból származtattuk, ami geometriai szempontból vitatható, de most nem ez a lényeg. A kipróbálásához állítsa az ELKESZULT makró értékét 2-re!

    Vegye észre, hogy a "kirajzolásnál" az ellipszis körként rajzolódik ki! Pedig a rajzol() tagfüggvény jól van megvalósítva! Az inserter is jól ír ki!
    Ez egy tipikus hiba, amit szinte mágnesként vonz a "legyen minden tagfüggvény inline és egy fájlban" gondolat. A dolog kezelhető, de meg kell érteni!

  9. Az ellipszis.h fájlban class definíciója elé készítsen egy prototípust az inserter függvényhez! Ez a gyakorlatban azt jelenti, hogy másolja az inserter függvény első sorát a class elé és a nyitó kapcsos zárojelet cserélje pontosvesszőre.

  10. Az alakzat_main-ben egy tömbbe gyűjtöttük össze a különböző alakzatok pointereit, ami tulajdonképpen egy heterogén kollekció tárolójának felel meg. A példa egyszerűsége miatt az objektumok nem a dinamikus memóriában jöttek létre. A valós esetekben ez csak ritkán van így. Az objektumok a dinamikus tárban keletkeznek és gondoskodni kell a felszabadításról is. Célszerűen egy "okos" tárolóra bízzuk a dinamikusan létrehozott objektumokat azzal, hogy kezelje, szükség esetén törölje azokat.
    Készítsünk a síkidomokhoz egy "okos" Rajztabla osztályt, amiben max. 100 különböző alakzat tárolható! Mivel a síkidomokat egységesen szeretnénk kezelni, ezért heterogén kollekcióként tároljuk, ahogyan azt a 6. előadás EventList osztályánál is láthattuk. Esetünkben a közös ős az Alakzat. Legyen a rajztáblának:
    • size() tagfüggvénye, ami megadja, hogy héyn alakzat van a táblán,
    • felrak() tagfüggvénye, ami felrak a rajztáblára egy tetszőleges alakzatot (pointert),
    • rajzol() tagfüggvénye, ami "kirajzolja" összes alakzatot,
    • mozgat() tagfüggvénye, ami a táblán levő alakzatok origóját elmozdítja egy d vektorral,
    • torol() tagfüggvénye, ami "letörli" a táblát (minden objektumot megszüntet).
  11. A rajztabla.h fájlban elkészítettük az osztály deklarációját, ami még nem végleges. Az egyszerűség kedvéért fix méretű tömbben tároljuk a pointereket. A konstruktort és a size() tagfüggvényt inline megvalósítottuk, ami inicializálja a darabszámot. Az alapértelmezett másoló konstruktort, és az értékadás műveletet privátként felüldeklaráltuk, mivel az alapértelmezett változat nem megfelelő. Így ezek nem lesznek érhetőek kívülről. Ha nem valósítjuk meg, akkor belülről sem! (c++11-ben van más megoldás, de ez is tökéletesen megfelelő.)
    class Rajztabla  {
     class Rajztabla  {
        static const size_t MAXDB = 100;///< legegyszerűbb így belső (egész) konstanst felvenni egy osztályban.  
        Alakzat* tabla[MAXDB];          ///< tároló
        size_t db;                      ///< aktuális darabszám
    
        /// "Elrejtett" másoló konstruktor és értékadó operátor.
        /// Az alapértelmezett változat nem jó, ezért ne legyen elérhető.
        Rajztabla(const Rajztabla& rhs);
        Rajztabla& operator=(const Rajztabla& rhs);
    public:
        Rajztabla() :db(0) { }
    
        /// Visszadja, hogy hány síkidom van a táblán
        /// @return - darabszám
        size_t size() const { return db; }
    
        /// Alakzatot tesz a rajztáblára
        /// @param ap - pointer az alakzatra
        void felrak(Alakzat *ap);       
        
        /// Kirajzolja az összes alakzatot
        void rajzol() const;                  
    
        /// Az összes alakzatot elmozdítja
        /// @param d - eltolás vektora
        void mozgat(const Pont& d) const;     
    
    #if ELKESZULT >= 5
        /// letörli a táblát, eltávolítja az objektumokat
        void torol();                   
    #endif // ELKESZULT
    
        /// Lehet, hogy kell destruktor is.
    };
    
  12. CodeBloks-ban jobb gomb a file nevén, majd project -- Add files
  13. Vegye ki a projektből az alakzat_main.cpp fájlt és tegye helyére az alakzat2_main.cpp fájlt. Ez a főprogram már dinamikusan hozza létre az alakzatokat és a rajztáblára "bízza" az objektumokat, azaz a rajztábla fogja kezelni, és megszüntetni is azokat. Fordítsa le a kódot, ami alapesetben (ELESZULT==3) csak létrehoz egy Rajztabla objektumot, de nem használja.
  14. Valósítsa meg Razjtabla osztály tagfüggvényeit a torol() kivételével a rajztabla.cpp fájlban!
    Állitsa az ELKESZULT makró értékét 4-re. Fordítson és futtasson!


    Memóriaszivárgást kell tapasztalnia, hiszen az alapértelmezett destruktor nem "felügyelte" az objektumokat, amit rábíztunk.
  15. Valósítsa meg a torol() tagfüggvényt és a destruktort is! Gondolja meg, hogy a két funkciónak van közös része/feladata. Ellenőrizze, hogy van-e memóriaszivárgás!
  16. Állítsa az ELKESZULT makró értékét 5-re. Fordítson és futtasson! Könnyen lehet, hogy most elszáll a programja. Ellenőrizze a darabszámokat!
  17. Készítsen insertert a Rajztabla osztályhoz, ami kiírja a táblán levő síkidomok számát! Állítsa az ELKESZULT makró értékét 6-ra. Fordítson és futtasson!
  18. Ha elégedett az eredménnyel, akkor töltse fel az elkészített illetve módosított haromszog.h, haromszog.cpp, rajztabla.h rajztabla.cpp, ellipsis.h fájlokat a Jporta rendszerbe (7. labor önellenőrző feladat), hogy megkapja a labor elvégzéséért járó pontot! Legalább ELKESZULT==5 szintig el kell jutnia.

Szorgalmi feladat

  1. Bővítse a modellt Teglalap osztállyal! Az egyszerűség kedvéért feltételezhető, hogy a téglalapok oldalai párhuzamosak a koordináta tengelyekkel, így elegendő két szemközti csúcspont koordinátáit tárolni. Állítsa az ELKESZULT makró értékét 7-re. Fordítson és futtasson!

Jó munkát!

Szeberényi Imre

Utolsó frissítés: 2024-01-31 18.20