6. hét: heterogén kollekció, alakzatok

Czirkos Zoltán · 2020.02.10.

Heti kiegészítő feladatok

1. Alakzatok

Definiálj alakzat ősosztályt, és abból származtatott, annak interfészét megvalósító a) téglalapot és b) kört!

Az alakzatoknak általában a színét és a pozícióját szeretnénk eltárolni. A szín egy színkód, egész szám, 0xRRGGBB (red, green, blue) formában. Például 0xFF0000 (nagyon piros), 0xFFFF00 (piros+zöld = sárga), 0x7c2128 (BME-bordó), 0x052a4b (VIK-kék).

Az alakzat konstruktora vegye át a közös attribútumokat! A többi osztályé pedig vegye át paraméterként az összes attribútumot. Legyen mindegyik osztálynak adatokat kiíró függvénye – a kiírás formátuma lényegtelen, csak látszódjanak az attribútumok. Ezen felül pedig lehessen lekérdezni a kerületüket!

Teglalap t(0xFF0000, 100, 20, 50, 60);
Kor k(0x00FFFF, 50, 50, 100);

t.kiir();   /* Téglalap x=100 y=20 sz=50 m=60 */
k.kiir();   /* Kör x=50 y=50 r=30 */

std::cout << t.kerulet();   /* 220 */
std::cout << k.kerulet();   /* 628.3184 */

2. Rajztábla

Készíts rajztábla osztályt, amely legfeljebb 100 alakzat adatait tudja tárolni! (Jó most a statikus tömb is, nem ez a feladat lényege.) Lehessen új alakzatot hozzáadni, „beregisztrálni” a rajztáblába, és lehessen egy adott indexű alakzatot törölni belőle!

Teglalap t(0xFF0000, 100, 20, 50, 60);
Kor k(0x00FFFF, 50, 50, 100);

Rajztabla r;
r.hozzaad(t);
r.hozzaad(k);
r.mindet_rajzol();      /* megjelennek a téglalap és a kör adatai */
std::cout << r.osszkerulet();   /* az összes kerület összege */

r.torol(0);             /* elfelejti a téglalapot */

r.mindet_rajzol();      /* csak a kör jelenik meg */

Rajzolj UML diagramot, amely ábrázolja a megírt osztályok kapcsolatát!

3. Rajztábla, okosabban

A fenti megoldás szembetűnő problémája, hogy a törlés pillanatában tulajdonképp a téglalap objektum nem törlődött ki, továbbra is létezik lokális változóként. Csak a rajztábla látóköréből került ki. Jobb lenne, ha a rajztábla nem csak ismerné az alakzat objektumokat, hanem ő maga tartalmazná azokat.

Ez megoldható az alakzatok dinamikus foglalásával. Alakítsd át ezért úgy a kódot, hogy az egyes objektumokat dinamikusan hozod létre!

int main() {
    Rajztabla r;
    r.hozzaad(new Teglalap(0xFF0000, 100, 20, 50, 60));
    r.hozzaad(new Kor(0x00FFFF, 50, 50, 100));
    r.mindet_rajzol();

    r.torol(0);     /* elfelejti a téglalapot, delete is */

    r.mindet_rajzol();
}                   /* delete a megmaradt alakzatokra */

Módosítsd az UML diagramod, hogy ezt a működést tükrözze!

4. Rajztábla másolása

A rajzolóprogramunkban a rajztábla osztály nem csak az éppen kezelt rajzot modellezheti, hanem a vágólapot is. Ha a menüből kiválasztjuk a Másol funkciót, akkor a rajzelemek a vágólapra kerülnek. Ha ezek után a rajzot módosítjuk, akkor az alakzatok megváltozó attribútumai nincsenek hatással a vágólapon lévő objektumokra. Pl. ha a vágólapra egy piros téglalapot teszünk, és utána a rajzon az eredetit átszínezzük sárgára, akkor a Beillesztés művelet hatására piros téglalapot kapunk.

Írd meg ennek a műveletnek a támogatására a rajztábla osztály másoló konstruktorát! Ilyennek amúgy is kellene léteznie, ha megoldható... Emlékezz vissza: ha egy osztálynak van destruktora, kellene neki másoló konstruktora és értékadó operátora is legyen.

A másoló konsturktor megírásakor bele fogsz futni egy problémába. A lemásolandó rajztábla egy heterogén kollekciót tartalmaz, alakzatokat. Ezeknek az alakzatoknak a pontos típusát nem ismered, csak Alakzat* pointereket látsz. Ennek ellenére típushelyesen kell tudni másolni az alakzatokat: bár nem tudod, mi a típusuk, még háromszög másolata háromszög, téglalap másolata téglalap kell legyen.

Hogy lehet ezt megoldani? A kulcskérdés: ki tudja azt, hogy melyik alakzat milyen típusú?

5. Négyzet osztály

Definiálj négyzet osztályt is!

Negyzet n(0xFF00FF, 70, 80, 100);
n.kiir();   /* Négyzet x=70 y=80 a=100 */
std::cout << n.kerulet();   /* 400 */

6. std::vector

Az std::vector (#include <vector>) a C++ beépített dinamikus tömb osztálya. Használata a következő:

#include <vector>

std::vector<int> v1;        /* üres vektor, int tömb */
v1.resize(200);             /* átméretezés 200-ra, csupa 0 lesz */
v1[12] = 123;               /* indexelhető */
v1.push_back(9);            /* a végére 9 kerül */
std::cout << v1.size();     /* 201 lett a mérete */

v1.erase(v1.begin() + 12);  /* 12. elem törlése */
std::cout << v1.size();     /* 200 */

Írd át a rajztábla osztályod, hogy 100 elemű statikus tömb helyett vektort használjon! Ezzel a 100-as limit automatikusan megszűnik.

7. Sokszög

Írj sokszög osztályt! Ennek bárhány csúcsa lehet. A konstruktorban vegye át a szokásos módon a színt és a pozíciót, továbbá egy std::vector-t, amely a csúcspontokat tartalmazza páronként! Ezek a csúcspontok távolságai a referenciaponthoz képest. Ha páratlan a mérete, vagy túl kevés eleme van, kivételt kell dobni.
std::vector<double> csucsok = { 10, 20, 30, 40, 50, 60 };
Sokszog s1(0x00C000, 5, 6, csucsok);

Használd az alábbi Vektor osztályt!

struct Vektor {
    double x, y;
    Vektor() {}
    Vektor(double x, double y) : x(x), y(y) {}
    Vektor operator-(Vektor v) const {
        return Vektor(x-v.x, y-v.y);
    }
    double hossz() const {
        return sqrt(x*x + y*y);
    }
};

Ha kész, válaszold meg a kérdést: miért/hogyan működik a következő programrész?

Sokszog s2(0x00C000, 5, 6, { 10, 20, 30, 40, 50, 60 });
Megoldás

alakzatmegoldas.cpp letöltése

Az osztályok kapcsolata UML diagramon

UML