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
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 tartalmazzakomplex.cpp
: egyelőre üres, itt kell majd megvalósítanod a függvényeketteszt.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.
Í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.
Megoldás
struct Komplex {
// ...
Komplex(double _re = 0, double _im = 0) {
re = _re;
im = _im;
}
};
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!
Megoldás
class Komplex {
double re;
double im;
public:
Komplex(double _re = 0, double _im = 0) {
re = _re;
im = _im;
}
double get_re() const {
return re;
}
double get_im() const {
return im;
}
};
Í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.
Megoldás
A header-ben elő kell deklarálni, a definíció pedig a cpp fájlba kerül:
std::ostream& operator<<(std::ostream& os, Komplex const& k) {
os << k.get_re();
if (k.get_im() >= 0)
os << "+";
os << k.get_im() << "i";
return os;
}
Í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!
Megoldás
Komplex operator+(Komplex const& lhs, Komplex const &rhs) {
return Komplex(lhs.get_re() + rhs.get_re(), lhs.get_im() + rhs.get_im());
}
Komplex operator*(Komplex const& lhs, Komplex const &rhs) {
return Komplex(
get_re() * rhs.get_re() - get_im() * rhs.get_im(),
get_re() * rhs.get_im() + get_im() * rhs.get_re());
}
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.
Megoldás
Komplex& operator+=(Komplex& lhs, Komplex const &rhs) {
lhs = lhs + rhs;
return lhs;
}
Komplex& operator*=(Komplex& lhs, Komplex const &rhs) {
lhs = lhs * rhs;
return lhs;
}
Í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!
Megoldás
Komplex operator/(Komplex const &k, double d) {
return Komplex(k.get_re() / d, k.get_im() / d);
}
Komplex& operator/=(Komplex& lhs, double d) {
lhs = lhs / d;
return lhs;
}
Í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
Megoldás
Komplex Komplex_sqrt(double d) {
if (d >= 0)
return Komplex(sqrt(d));
else
return Komplex(0, sqrt(-d));
}
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
}
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)
Megoldás
Komplex Komplex_from_r_fi(double r, double fi) {
return Komplex(r * cos(fi), r * sin(fi));
}
- 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!