POO - Laborator 7 Un exemplu complet de clasa aritmetica - numere rationale Lucrarea contine o implementare a clasei Rational, pentru lucrul cu numere rationale. Sunt implementate toate operatiile aritmerice unare si binare, operatiile de comparatie si de testare a egalitatii precum si operatii de I/O. Toate operatiile sunt definite prin supraincarcarea operatorilor respectivi. Membrii (datele) clasei sunt numaratorul si numitorul numarului rational, notati Numer si Denom. Trebuie acordata atentie tipurilor intoarse de diversi operatori, astfel incit sa se respecte semnificatia de la tipurile aritmetice standard. De exemplu, operatorul de atribuire trebuie sa intoarca valoarea atribuita. In general, sunt posibile 3 tipuri de abordari: se intoarce valoare (Rational), o referinta (Rational &) sau o referinta constanta (const Rational &). Varianta cu valoare implica un proces de copiere, care se face printr-un apel al functiei constructor de copiere. De aceea, este de preferat varianta cu referinte, dar trebuie acordata atentie semnificatiei. De exemplu, functia operator + (adunare) trebuie sa intoarca obiectul creat prin adunare. Daca s-ar scrie de forma: Rational & operator+(Rational & dreapta) { Rational temp(*this); temp += dreapta; return temp; } s-ar permite expresii de forma (a+b) = c (a+b intoarce o referinta). Aceasta s-ar rezolva printr-un prototip: const Rational & operator+(Rational &); (se intoarce o referinta constanta si deci (a+b) = c nu mai este permis). Problema este ca intoarcem o referinta catre un obiect in clasa auto - temp dispare o data cu incheierea functiei, deci solutia este incorecta. O varianta ar fi declararea lui temp in clasa static. Aceasta pune doua probleme. Prima este ca la variabile satice, initializarea se face o singura data, deci linia: Rational temp(*this); ar trebui inlocuita cu: Rational temp; temp = *this; A doua problema este mai complicata dar devine usor de inteles daca ne gindim la evaluarea unei expresii de forma: (r1 + r2 ) == (r3 + r4) Aceasta expresie se va evalua intotdeauna la adevarat, deoarece ceea ce se compara sunt referinte catre acelasi obiect static temp din functia operator+. Singura varianta corecta de implementare a acestei functii operator este prin prototipul: Rational operator+(Rational &); adica sa se intoarca un obiect prin copiere. Este util de testat aplicatia prin rulare pas cu pas (tasta F7),. pentru a vedea prin ce functii se trece la executia unui astfel de operator. In cazul de fata se va observa trecerea prin constructorul de copiere. La operatiile unare ++, --, limbajul C standard face distinctia intre forma prefixata (++n) si cea postfixata (n++), in sensul ca valoarea expresiei ++n este valoarea dupa incrementare, iar cea a expresiei n++ este valoarea dinainte de incrementare. Compilatoarele mai noi C++ permit aceasta diferentiere si in cazul operatorilor supraincarcati, prin definirea a doua functii operator++, cu semnaturi diferite. Concret, forma postfixata se declara ca avind un argument intreg (care nu e niciodata folosit). Expresia ++X va apela functia operator++ care nu are argumente, iar X++ va apela operator++ care are un argument intreg. Si in acest caz tipul functiei difera, deoarece forma postfixata trebuie sa intoarca un obiect temporar (obiectul curent dinainte de incrementare). Conform, celor discutate mai sus, se intoarce prin valoare, deci tipul functiei este Rational. La forma prefixata, se intoarce obiectul dupa incrementare, deci *this, ca atare putem intoarce o referinta constanta. Este posibil ca unele compilatoare mai vechi sa nu accepte ambele functii. In acest caz, se elimina functia cu un argument intreg. Functia operator>> (citire Rational) asteapta (de exemplu de la consola) o secventa de forma: 2/5 sau una de forma 2 pentru a permite si introducerea de numere intregi (rationale cu numitor 1). Cum comparatia cu '\' se poate face doar dupa ce s-a citit un caracter, apare situatia in care caracterul citit trebuie "pus inapoi", ca sa poata fi folosit la urmatoarea citire. Acest lucru este posibil prin metoda putback a fluxului de intrare. La operator>> numarul 0/0 este afisat prin sirul "nedeterminat", iar x/0 ca "infinit". De observat ca Metodele private FixSigns si Reduce forteaza numitorul sa fie pozitiv (2/-3 devine -3/2), respectiv simplifica numarul rational (4/8 devine 1/2). Metoda LongDecimal intoarce valoarea double asociata numarului rational. Metodele declarate const (care au cuvintul const la sfirsit) nu pot modifica membrii clasei. Mod de lucru 1) Se vor studia fisierele rational.h si rational.cpp (definitia si implementarea clasei). 2) Se va rula programul principal ratmain.cpp 3) Se va completa programul principal pentru a testa toate operatiile implementate in clasa Rational. 4) Sunt permise numerele rationale 0/0, 1/0 -1/0 in aceasta implementare? Cum se compara aceste valori cu numere obisnuite? De exemplu, este 1/0 mai mare decit orice alt numar? Sa se argumenteze raspunsul prin examinarea functiilor operator respective. 5) Cit fac 0/0 + 1/2 ? Dar 1/0 + 1/2 ? Sunt aceste rezultate consistente cu semnificatia de "nedeterminat" si "infinit"?