Labor, 3. hét: osztályok, tagfüggvények, operátorok

Dobra Gábor · 2019.02.27.

Heti kiegészítő feladatok az OOP alapok gyakorlásához

Ezen a héten az alapvető OOP újdonságokat gyakoroljuk.

1. Felkészülés a laborra

Olvasd el a jegyzet 2. fejezetét, különös tekintettel az alábbiakra:

  • OOP elvek áttekintése
  • konstruktor fogalma és szintaktikája
  • tagfüggvény fogalma és szintaktikája
  • operator overloading szintaktikája

2. Áttekintés

Kiindulási alapnak vegyünk egy C struktúrát. A mai labor során ezt kell apránként C++ osztállyá alakítani.

struct Komplex {
    double re;
    double im;
};

Töltsd le a kiinduló Code::Blocks projektet innen, és nyisd meg a projektfájlt. Ezeket a fájlokat találod benne:

  • komplex.h: a Komplex típus deklarációit tartalmazza
  • komplex.cpp: egyelőre üres, itt kell majd megvalósítanod a függvényeket
  • teszt.cpp: általunk előkészített tesztek, ami automatikusan ellenőrzi az elkészült feladatokat

A projektet lefordítva és lefuttatva azt kell látnod, hogy minden teszteset FAILED.

Fontos, hogy a Komplex adattagjait ne változtasd vagy cseréld meg, mert az automatikus tesztek – sajnos – függnek ettől.

Az eheti JPortás feladatok során fel kell majd használnod az itt megírandó függvényeket. (Egy mintamegoldást is ki fogunk adni.)

3. Konstruktor

Írj a Komplex struktúrának konstruktort! Így kell működnie:

Komplex k1;             // 0+0i
Komplex k2 = 3.0;       // 3+0i
Komplex k3(3.0, 4.0);   // 3+4i

Hány konstruktort kell ehhez ténylegesen megírni?

Válasz

Elég egyet, default paraméterekkel.

4. Adatrejtés

A Komplex belsejét még mindenki ismeri, hozzáférnek a re, im adattagokhoz. Ezért egyelőre nem lehet büntetlenül (a rá épülő kód eltörése nélkül) kicserélni az adattagokat például r, fi adattagokra.

Rejtsd el az adattagjait, és biztosíts elérést (get_re, get_im tagfüggvényekkel) a szám valós és képzetes részéhez!

5. Inserter (kiíró) operátor

Írj olyan operátort a Komplex osztályhoz, hogy kiírhatók legyenek std::cout-ra!

std::cout << Komplex(3, 4)  << std::endl; // 3+4i
std::cout << Komplex(3, -4) << std::endl; // 3-4i
Tipp

Ha a képzetes rész pozitív, akkor a képzetes rész előtti + jelet nekünk kell kiírni.

6. Összeadás és szorzás

Írj olyan operátorokat, amivel két komplex számot össze lehet adni és szorozni! Pl. ezt is le lehessen írni:

Komplex c1 = 2;
Komplex c2(3, 4);
std::cout << c1 + c2 << std::endl; // 5+4i
std::cout << c1 * c2 << std::endl; // 6+8i

Legyenek ezek az operátorok globális függvények, ne tagfüggvények! A paramétereik legyenek konstans referenciák!

Matek

Összeadás: (a1 + b1i) + (a2 + b2i) = (a1 + a2) + (b1 + b2)i

Szorzás: (a1 + b1i) * (a2 + b2i) = (a1a2 - b1b2) + (a1b2 + a2b1)i

Vigyázz, a és b a kódban re-ként és im-ként jelenik meg!

Ha van + és *, akkor elvárjuk, hogy legyen += és *= is. Írd meg ezeket is globális függvényként, visszavezetve a + és a * operátorokra! Mivel kell visszatérnie ezeknek az operátoroknak?

Válasz

A bal oldali paraméterrel, referencia szerint.

Miért jobb ebben az esetben, hogy globálisak ezek az operátorok?

Válasz
  • Nem szükséges ismerni az osztály belsejét (adattagjait), így az operátorok az osztály átírása esetén is maradhatnak változatlanok.
  • A double → Komplex implicit konverzió miatt automatikusan működik a double+Komplex és a Komplex+double összeadás, és a szorzás is.

7. Osztás valós számmal

Írj olyan operátort, hogy egy Komplex-et el lehessen osztani egy double-lel!

Komplex c(4, 6);
std::cout << c / 2.0 << std::endl; // 2+3i

Írd meg hozzá a /= operátort is, a fenti += és *= operátorokhoz hasonlóan!

8. Gyökvonás negatív számból

Írd meg a Komplex_sqrt globális függvényt, ami bármely valós számból négyzetgyököt tud vonni! Az eredmény mindig Komplex szám.

std::cout << Komplex_sqrt(4.0)  << std::endl; // 2+0i
std::cout << Komplex_sqrt(-4.0) << std::endl; // 0+2i

9. Váltás trigonometriai alakba

Előfordulhat, hogy egy komplex számot trigonometriai alakban érdemes használni (szorzásnál, osztásnál). Oldd meg, hogy a Komplex-től le lehessen kérdezni a hosszát és az irányszögét (get_r és get_fi tagfüggvények)! Ezeket a cpp fájlban definiáld, az osztályon belül csak a deklarációjuk szerepeljen!

Matek

r = sqrt(a2 + b2)

fi = atan2(b, a)

Megoldás
double Komplex::get_r() const {
    return sqrt(re * re + im * im);
}

double Komplex::get_fi() const {
    return atan2(im, re); // Imre, haha
}

10. Váltás trigonometriai alakból

Gyakran megesik, hogy egy komplex számot trigonometriai alakban kapunk meg, és abból szeretnénk Komplex objektumot előállítani. Írd meg a Komplex_from_r_fi globális függvényt, ami ezt az átváltást elvégzi!

std::cout << Komplex_from_r_fi(sqrt(2), M_PI/4)  << std::endl; // 1+1i
Matek

a = r cos(fi) b = r sin(fi)

komplex_minta.zip letöltése

11. Haladó (IMSC) feladatok

  • Írd át az operator*-ot, hogy trigonometriai alakban számoljon!
  • Legyen hatványozás operátor (operator^), amivel valós kitevőre haványozhatsz egy egész számot! Használd a trigonometriai alakot!
  • A teszt.cpp végére írj teszteseteket ezen feladat új operátoraihoz!

12. További feladatok

  • Ha van Komplex számok közötti + és * operátor, akkor legyen -, -=, / és /= is!
  • Ha van kétoperandusú + és -, akkor legyen egyoperandusú + és - is! (semmit-nem-csinál és ellentett operátorok)
  • Legyen == és != operátor két Komplex szám egyenlőségének a vizsgálatához! A valós számok számítási pontatlanságai miatt egymáshoz egy bizonyos tűréshatárnál (legyen ez 10-10) közelebb lévő számokat tekintsünk egyenlőnek: |x-y|<10-10
  • A teszt.cpp végére írj teszteseteket ezen feladat új operátoraihoz!