diff --git a/POO_22_C_2022-04-14.md b/POO_22_C_2022-04-14.md new file mode 100644 index 0000000..ef4d6e0 --- /dev/null +++ b/POO_22_C_2022-04-14.md @@ -0,0 +1,183 @@ +```cpp +//Punct2D.h +class Punct2D +{ + double m_x, m_y; +public: + Punct2D() {}; + Punct2D (double, double); + double dist (Punct2D p2); + double dist (Segment s); //nu am acces la membrii privati din Segment + + friend double dist (Punct2D p, Segment s); + friend double dist (Punct2D, Punct2D); +}; +``` + +```cpp +//segment.h +class Segment +{ + Punct2D m_p1, m_p2; +public: + double dist(Punct2D p); //nu am acces la membrii privati din Punct2D + + friend double dist (Punct2D p, Segment s); +}; +``` +Consideram doua clase ```Punct2D``` si o clasa ```segment``` pentru care nu avem metode get (getter-i) implentate. Dorim sa scriem o functie care calculeaza distanta de la un punct la un segment. Aceasta functie avem posibilitatea de a o scrie fie membra clasei ```Punct2D```, fie membra clasei ```segment```, fie externa ambelor clase. Niciuna dintre solutii nu pare sa fie viabila deoarece in oricare dintre cele trei functii nu avem acces la toti membrii privati din ```Punct2D``` cat si din ```segment```. +```cpp +double dist (Punct2D p, Segment s); //nu am acces la membrii privati ai celor doua clase +``` +Ultima solutie este cea fiabila datorita unui mecanism acceptat in C++. +In C++ se accepta scrierea unei functii externe, nemembra niciunei clase, dar care sa fie *prietena* cu una sau mai multe clase. Functia respectiva va avea acces la toti membrii claselor cu care este prietena. Fiind o functie prietena uneia sau mai multor clase se implementeaza in exteriorul claselor, dar antetul functiei va fi introdus in fiecare dintre clasele care va fi prietena precedat de cuvantul rezervat ```friend```. +O functie care prelucreaza datele unei clase avem posibilitatea sa o scriem membra acelei clase sau prietena clasei. Deosebirea esentiala dintre cele doua implementari este ca in cazul functiei prietene va aparea un parametru suplimentar (obiect instanta clasei). +```cpp +//implementare.cpp +double dist(Punct2D p1, Punct2D p2) +{ + return sqrt((p1.m_x - p2.m_x)*(p1.m_x - p2.m_x) + ...); +} +``` +Se observa in cazul functiei prietene ca putem accesa membrii privati functiei prietene ```dist``` ai celor doua puncte ```p1``` si ```p2```, chiar daca ```dist``` este functie externa. + +## Supraincarcarea operatorilor pentru clase +Pentru o clasa putem supraincarca un operator in general sub doua forme (cu mici exceptii): +- membru clasei respective +- prietene clasei + +```cpp +//Punct2D.h +class Punct2D +{ + double m_x, m_y; +public: + Punct2D() {}; + Punct2D (double, double); + double dist (Punct2D p2); + double dist (Segment s); //nu am acces la membrii privati din Segment + + friend double dist (Punct2D p, Segment s); + friend double dist (Punct2D, Punct2D); + + friend double operator!(Punct2D p) + { + return sqrt(p.m_x * p.m_x + p.m_y * p.m_y) + } + double operator-(); + Punct2D operator*(double s); + friend Punct2D operator*(double, Punct2D); + + friend ostream& operator<<(ostream&, Punct2D); + friend istream& operator>>(istream&, Punct2D); + + Punct2D operator=(Punct2D); +}; +``` +Supraincarcarea operatorului ```!``` pentru clasa ```Punct2D``` este absolut similiara cu supraincarcarea aceluiasi operator pentru structura ```complex```. In cazul clasei ```Punct2D``` am fost obligati ca aceasta supraincarcare sa o facem prietena clasei ```Punct2D``` pentru ca altfel nu am fi avut acces la membrii privati ai punctului. +Desi supraincarcarea lui ```!``` pentru ```Punct2D``` este externa clasei, compilatorul C++ accepta implementarea lui in momentul descrierii clasei ```Punct2D``` (in fisierul ```.h```). +```cpp +//implementare.cpp +double Punct2D::operator-() +{ + return Punct2D(-m_x, -m_y); +} + +Punct2D Punct2D::operator*(double s) +{ + return Punct2D(s * m_x, s * m_y); +} + +Punct2D Punct2D::operator*(double s, Punct2D p) +{ + return p*s; +} + +ostream& operator<<(ostream& fl, Punct2D p) +{ + return fl << p.m_x << " " << p.m_y; +} + +istream& operator>>(istream& fl, Punct2D p) +{ + return fl >> p.m_x >> p.m_y; +} +``` +In clasa ```Punct2D``` am supraincarcat operatorul unar ```-``` ca fiind membru clasei. In consecinta, aceasta supraincarcare nu are parametri, operatorul ```-``` aplicandu-se obiectului curent. +In general, supraincarcarea unui operator prieten unei clase se face cu un numar de parametri egal cu aritatea operatorului, iar cand supraincarcam un operator membru clasei, numarul de parametri este egal cu aritatea-1, iar primul operand este obligatoriu obiectul curent. +Amplificarea cu scalar la dreapta unui ```Punct2D``` folind operatorul ```*``` poate fi implementata atat membra clasei cat si prietena clasei. Amplificarea la stanga a unui ```Punct2D``` cu un numar real nu poate fi implementata decat prietena clasei deoarece primul operand nu este ```Punct2D```, ci este un ```double```. +```cpp +//source.cpp +void main() +{ + double x, y; + cin >> x >> y; + Punct2D p1 (x,y); + cout << !p1 << endl; //operator!(p1); + cout << -p1 << endl; //operator<<(cout, p1.operator-()); + + double s; + cin >> s; + cout << p1*s << endl; //p1.operator*(s); + cout << s*p1 << endl; //operator*(s,p1); +} +``` +Operatorii ```<<``` si ```>>``` pentru introducerea unui obiect intr-un flux de iesire si respectiv preluarea unui obiect dintr-un fluc de intrare se supraincarca obligatoriu ca fiind prieteni clasei. +TPA: ```==```, ```!=```, ```<```, ```>```, ```<=```, ```>=```, ```/```. + +## Pointerul ```this``` +In C++, ```this``` este cuvant rezervat si este folosit la descrierea unei clase. El retine atunci cand implementam clasa, pointerul catre obiectul curent. +Ne referim la un membru al clasei sub forma: +```cpp +this->membru; +``` +Acolo unde nu apare confuzie putem sa ne referim direct la membru fara prefixul ```this->```, insa in toate aceste situatii la compilare se va adauga prefixul ```this->```. +Exista situatii in care este neaparat nevoie sa ne referim la pointerul ```this```; mai exact exista situatii in care avem nevoie de obiectul curent (o referinta la obiectul curent) pe care o obtinem sub forma ```*this```. Una dintre aceste situatii clasice este atunci cand supraincarcam operatorul ```=```. Operatorul ```=``` este unul dintre putinii operatori care nu pot fi supraincarcati, decat membrii unei clase. +```cpp +p1=p2; +``` + +``` + +```cpp +//implementare.cpp +Punct2D Punct2D::operator=(Punct2D p2) +{ + m_x = p2.m_x; + m_y = p2.m_y; + return *this; +} +``` +Campurile ```m_x``` si ```m_y``` ale obiectului curent le-am initializat cu campurile corespunzatoare din ```p2```, operandul stang fiind obiectul curent, iar operandul drept fiind ```p2``` primit ca parametru. In final, filosofia implementarii lui ```=``` spune ca trebuie sa returnam ceea ce s-a atribuit operandului stang, adica returnam o copie a obiectului curent (o copie a lui ```*this```). +## Membri statici +Campuri cat si metode ale unei clase pot fi declarate ca fiind statice. +### Camp static +Un camp declarat static, folosind cuvantul rezervat ```static```, este un camp utilizat in comun de catre toate obiectele instanta clasei respective. +```cpp +class Test +{ + static int s_inst; +public: + Test(){s_inst++;} + Test(int x) + { + cout << x << endl; + s_inst++; + } + ~Test(){s_inst--;} + static int getinst(){return s_inst;} +}; +int Test::s_inst=0; +``` +In clasa ```Test``` avem un camp static ```s_inst``` care se doreste sa retina in permanenta numarul de obiecte aflate in memorie, obiecte instanta clasei ```Test```. Astfel, de fiecare data cand un obiect nou este creat se apeleaza un constructor care incrementeaza valoarea lui ```s_inst```. Cand un obiect este distrus ```s_inst``` este decrementat cu valoarea ```1```. +In C++, orice camp static (care nu este ```const```) trebuie declarat de doua ori: +- atunci cand descriem clasa +- in exteriorul descrierii clasei +Campul ```s_inst``` fiind ```private``` nu putem sa il accesam decat in interiorul clasei. Am putea scrie o functie membra care sa returneze valoarea campului. Daca este o functie membru obisnuita ea trebuie apelata obligatoriu dintr-un obiect de tip ```Test```. Poate exista situatia in care vrem sa afisam valoarea lui ```s_inst```, insa nu avem la dispozitie niciun obiect de tip ```Test```. Aceasta situatie poate fi elegant rezolvata daca functia membra ce returneaza campul ```s_inst``` este declarata public si static. +De oriunde din program unde avem acces la fisierul antet ```Test.h``` putem afla valoarea campului ```s_inst``` sub forma: +```cpp +Test::getinst() +``` +Exista situatii in care anumite metode le scriem statice. Acele metode au legatura cu contextul clasei, dar pot fi folosite si ca unelte separate fara a fi nevoie sa apelam la un obiect. +Exemplu: Presupunem ca scriem o clasa ```Rational``` pentru a retine un numar rational sub forma de fractie ireductibila ```numataror``` ```/``` ```numitor```. Fiind fractie ireductibila avem nevoie de mai multe ori la implementare sa simplificam fractia, de exemplu cand adunam doua numere (sub forma de fractii). La simplificare avem nevoie sa calculam cel mai mare divizor comun dintre numarator si numitor. Pentru aceasta operatie (cmmdc) putem scrie o metoda statica publica pe care o folosim la simplificare atunci cand implementam clasa ```Rational```, dar lasam posibilitatea sa fie folosita si in alte scopuri in afara clasei ```Rational```. +Orice membru declarat static este un pas in plus in a strica orientarea pe obiecte, deci trebuie sa limitam la strictul necesar declararea acestor membri ca fiind statici. \ No newline at end of file diff --git a/README.md b/README.md index e6a4151..50926b0 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,9 @@ - Clase - specificatori de acces - constructori - - destructori \ No newline at end of file + - destructori +#### [Curs 8](https://github.com/tgpetrica/POO-courses/blob/main/POO_22_C_2022-04-14.md) +- redactarea functiilor prietene claselor +- supraincarcarea operatorilor pentru clase +- pointerul ```this``` +- membri statici \ No newline at end of file