Sunteți pe pagina 1din 8

Lucrarea nr.

3 Clase C++
O clasa reprezinta o metoda logica de organizare a datelor si functiilor unei aceleasi familii. O clasa este declarata folosind cuvantul cheie class, care are o functionalitate similara cu cuvantului cheie struct din C. Diferenta fundamentala este aceea ca o clasa poate include intre membrii sai si functii, nu numai date ca in structura struct din C. Forma generala a unei clase este:
class class_name { permission_label_1: member1; permission_label_2: member2; ... } object_name;

unde class_name reprezinta numele clasei (tip definit de utilizator), iar campul optional object_name reprezinta unul sau mai multi identificatori valizi de obiecte. Corpul declaratiei poate contine membrii (members), care pot fi atat declaratii de date cat si declaratii de functii si, optional, etichete de permisiune (permission labels), care pot fi una din urmatoarele cuvinte cheie: private:, public: sau protected:. Acestea fac referinta la permisiunea pe care membrii o dobandesc:

private - membrii clasei sunt accesibili numai din cadrul altor membrii ai aceleiasi clase sau din clase prietene (friend) protected - membrii clasei sunt accesibili din cadrul membrilor acelorasi clase sau clase prietene (friend) si, totodata, din membrii claselor derivate (derived) public - membrii sunt accesibili de oriunde clasa este vizibila

Daca se declara membrii unei clase inainte de a include o eticheta de permisiunea, acestia vor fi considerati (implicit) private. Exemplu:
class CRectangle { int x, y; public: void set_values (int,int); int area (void); } rect;

S-a declarat clasa CRectangle si un obiect rect al acestei clase. Aceasta clasa contine 4 membrii: 2 variabile de tipul int (x si y) in sectiunea private (fiind sectiunea cu permisiune implicita) si 2 functii in sectiunea public: set_values() si area(), in care caz s-a declarat numai prototipul acestora. A se observa diferenta intre numele clasei si numele obiectului. In exemplul anterior CRectangle reprezinta numele clasei (adica un tip utilizator), pe cand rect este un obiect de tip CRectangle. Este vorba de exact aceeasi diferenta dintre int si a din declaratia urmatoare:
int a;

unde int este tipul (numele clasei), iar a este variabila (numele obiectului). In intructiuni succesive in corpul programului se poate referi orice membru public al obiectului rect, ca si cand ar fi functii sau variabile normale, prin simpla adaugare a numelui obiectului urmat de un punct si apoi membrul clasei (la fel ca in cazul structurii). De exemplu: Page 1 of 8

rect.set_value (3,4); myarea = rect.area();

In schimb nu se va putea accesa direct membrii privati x si y ai clasei decat de catre un alt membru al clasei (adica, de exemplu, de catre orice functie definita in cadrul clasei). Iata un exemplu:
// exemplu clasa #include <iostream.h> class CRectangle { int x, y; public: void set_values (int,int); int area (void) {return (x*y);} }; void CRectangle::set_values (int a, int b) { x = a; y = b; } int main () { CRectangle rect; rect.set_values (3,4); cout << "area: " << rect.area(); } area: 12

Ce apare nou in programul de mai sus este operatorul :: din definitia functiei set_values(). Acest operator este utilizat pentru a declara membrii clasei in afara acesteia. A se observa, de asemenea, ca functia area() a fost definita in interiorul clasei. Nota: Se prefera definirea in cadrul clasei a functiilor care au un corp mic (sau foarte mic). Acestea vor fi IMPLICIT considerate inline de catre compilator. Nota: Se prefera folosirea membrilor private pentru ca, accidental, sa nu se modifice valoarea acestora, modificarea (cat si citirea) fiind posibila numai prin intermediul functiilor publice.

Unul din avantajele mari ale clasei este acela ca se pot declara diferite obiecte ale clasei respective. Urmatorul exemplu adauga un al doilea obiect rectb:
// exemplu clasa 2 #include <iostream.h> class CRectangle { int x, y; public: void set_values (int,int); int area (void) {return (x*y);} }; void CRectangle::set_values (int a, int b) { x = a; y = b; rect area: 12 rectb area: 30

Page 2 of 8

} int main () { CRectangle rect, rectb; rect.set_values (3,4); rectb.set_values (5,6); cout << "rect area: " << rect.area() << endl; cout << "rectb area: " << rectb.area() << endl; }

A se observa ca rect.area() nu furnizeaza acelasi rezultat ca rectb.area() pentru simplul motiv ca fiecare obiect de tipul CRectangle are propriile lor variabile x si y. Pe acesta este si bazat conceptul de obiect si programare orientat-obiect (OOP - Object-Oriented Programming). In acest exemplu clasa (tipul obiectului) este CRectangle, iar acesta are doua instante (sau obiecte): rect si rectb, fiecare cu propriile variabile membru si propriile functii membru.

3.1 Constructori si destructori


In general obiectele au nevoie de initializarea variabilelor lor sau de atribuirea dinamica a memoriei pe parcursul operatiei de creare a acestora. Aceasta pentru a deveni operationale si a putea fi folosite fara surpriza de a intoarce rezultate neasteptate (in general generatoare de probleme) pe parcursul executiei programului. De exemplu, ce s-ar intampla daca s-ar apela functia membru area() inainte de functia membru set_values()? S-ar obtine, probabil, un rezultat nedeterminat, din moment ce membrii x si y nu au fost initializati. Pentru a evita astfel de rezultate nedeterminate o clasa poate contine o functie speciala, denumita constructor. Aceasta are acelasi nume ca si clasa. Functia constructor este apelata automat cand este creata o noua instanta (adica un nou obiect) al clasei respective - si numai atunci. Se observa in programul urmator includerea constructorului in programul anterior:
// exemplu clasa - 3 #include <iostream.h> class CRectangle { int width, height; public: CRectangle (int,int); int area (void) {return (width*height);} }; CRectangle::CRectangle (int a, int b) { width = a; height = b; } int main () { CRectangle rect (3,4); CRectangle rectb (5,6); cout << "rect area: " << rect.area() << endl; rect area: 12 rectb area: 30

Page 3 of 8

cout << "rectb area: " << rectb.area() << endl; }

Se observa ca rezultatele acestui exemplu sunt identice cu exemplul precedent. In acest ultim exemplu functia set_values a fost inclocuita cu constructorul clasei. A se observa modul in care parametrii sunt transmisi constructorului in momentul in care clasa este creata:
CRectangle rect (3,4); CRectangle rectb (5,6);

De asemeni se poate observa ca nu exista valoare intoarsa de catre constructor nici in prototipul acestuia, nici in corpul acestuia (nici macar de tipul void). Un constructor nu intoarce niciodata vreo valoare, deci nu trebuie specificat nici un tip pentru valoarea intoarsa de constructor, pentru ca acesta nu intoarce nici un fel de valoare de nici un fel de tip.
Exercitiu 3.1

Sa se modifice programul anterioar, astfel: - se se adauge clasei CRectangle functiile publice set_width() si set_height() care vor modifica valoarea membrilor width si, respectiv, length - sa se afiseze noua valoare intoarsa de functia membru area() Nota: in primul caz se va modifica clasa CRectangle prin adaugarea: - prototipurilor noilor functii in clasa si corpurile noilor functii dedesubt, SAU - direct corpul functiilor in clasa (pentru a fi tratate drept inline la compilare) Destructorul (destructor) are o functionalitate opusa constructorului. Acesta este chemat in mod automat atunci cand un obiect este eliberat din memorie, fie pentru ca scopul existentei obiectului s-a terminat (de exemplu cand este declarata o instanta a unei clase in interiorul unei functiei, iar aceasta se termina de executat) fie pentru ca obiectul a fost alocat dinamic (cu new) si este eliberat utilizand operatorul delete. Destructorul are intotdeauna acelasi nume ca si clasa si este precedat de tilda (~) ca un prefix. Totodata, la fel ca in cazul constructorului, destructorul nu returneaza nici o valoare. Utilitatea destructorului apare atunci cand un obiect atribuie memorie dinamica pe parcursul existentei acestuia si, in momentul cand este distrus, trebuie sa elibereze memoria alocata lui. Exemplu:.
// examplu constructor si deconstructor #include <iostream.h> class CRectangle { int *width, *height; public: CRectangle (int,int); ~CRectangle (); int area (void) {return (*width * *height);} }; rect area: 12 rectb area: 30

Page 4 of 8

CRectangle::CRectangle (int a, int b) { width = new int; height = new int; *width = a; *height = b; } CRectangle::~CRectangle () { delete width; delete height; } int main () { CRectangle rect (3,4), rectb (5,6); cout << "rect area: " << rect.area() << endl; cout << "rectb area: " << rectb.area() << endl; return 0; }

3.2 Supraincarcarea constructorilor


Precum alte functii si constructorul poate fi supraincarcat cu oricate functii care au acelasi nume, dar numar si tip diferit de parametrii. Compilatorul va compila apelul la acea functie care se potriveste atat la numarul de parametrii, cat si la tipul acestora. In cazul in care aceasta potrivire nu se poate efectua, se va genera o eroare de compilare. In cazul in care o clasa este declara fara nici un constructor, compilatorul, in mod automat, presupune 2 constructori supraincarcati: constructorul implicit (default constructor) si constructorul de copiere (copy constructor). De exemplu, pentru clasa:
class CExample { public: int a,b,c; void multiply (int n, int m) { a=n; b=m; c=a*b; }; };

care nu are constructori, compilatorul va asuma urmatorii constructori ca si functii membru :

constructorul vid (empty constructor) - un constructor care nu are parametrii si este definit ca un nop (No OPeration - un bloc vid de instructiuni) si, in consecinta, nu face nimic

CExample::CExample () { };

constructorul de copeiere (copy constructor) - un constructor cu un singur parametru de acelasi tip care atribuie fiecarei variabile membru de tip non-static al obiectului o copie a obiectului transmis ca parametru:

Page 5 of 8

CExample::CExample (const CExample& rv) { a=rv.a; b=rv.b; c=rv.c; }

Este important de observat ca ambii constructori impliciti exista numai daca nu sunt declarati explicit alti constructori. In cazul in care exista declarat un constructor cu oricati parametrii si de orice tip, atunci nici unul din constructorii impliciti nu vor exista. In cazul in care se doreste existenta acestora, se vor defini de catre utilizator. Bineinteles ca se poate supraincarca constructorul clasei prin furnizarea de diversi constructori pentru care se transfera parametrii intre paranteze, sau nu (empty):

// supraincarcarea constructorilor clasei #include <iostream.h> class CRectangle { int width, height; public: CRectangle (); CRectangle (int,int); int area (void) {return (width*height);} }; CRectangle::CRectangle () { width = 5; height = 5; } CRectangle::CRectangle (int a, int b) { width = a; height = b; } int main () { CRectangle rect (3,4); CRectangle rectb; cout << "rect area: " << rect.area() << endl; cout << "rectb area: " << rectb.area() << endl; }

rect area: 12 rectb area: 25

In acest exemplu rectb a fost declarat fara parametrii, ceea ce a condus la initializarea cu ajutorul constructorului fara parametrii, care atribuie ambelor variabile membru valoarea 5. Nota: a se observa declaratia unui nou obiect pentru care nu se doreste transmisia de parametrii, deci nu se includ paranteze:
CRectangle rectb; CRectangle rectb(); // GRESIT! // bine

Page 6 of 8

3.3 Pointeri la clase


Este perfect valid de a crea pointeri care sa "arate" (indice) catre clase. Pentru acest lucru se va considera ca odata declarata clasa, aceasta devine un tip valid, deci se va putea folosi numele clasei ca tip al acelui pointer. De exemplu:
CRectangle * prect;

este un pointer la un obiect al clasei CRectangle.

La fel ca in cazul structurilor, pentru a referi (accesa) direct un membru al obiectului de tip pointer la tipul clasa, se va folosi operatorul ->. Iata un exemplu de combinatii posibile:
// exemplu de pointeri la clase #include <iostream.h> class CRectangle { int width, height; public: void set_values (int, int); int area (void) {return (width * height);} }; void CRectangle::set_values (int a, int b) { width = a; height = b; } int main () { CRectangle a, *b, *c; CRectangle * d = new CRectangle[2]; b= new CRectangle; c= &a; a.set_values (1,2); b->set_values (3,4); d->set_values (5,6); d[1].set_values (7,8); cout << "a area: " << a.area() << endl; cout << "*b area: " << b->area() << endl; cout << "*c area: " << c->area() << endl; cout << "d[0] area: " << d[0].area() << endl; cout << "d[1] area: " << d[1].area() << endl; return 0; } a area: 2 *b area: 12 *c area: 2 d[0] area: 30 d[1] area: 56

In urmatorul tabel sunt recapitulate metodele pentru a citi pointeri si operatorii (*, &, ., ->, [ ]) care apar in exemplul anterior.

Page 7 of 8

*x &x x.y (*x).y x->y x[0] x[1] x[n]

se poate citi se poate citi se poate citi se poate citi se poate citi se poate citi se poate citi se poate citi

indicat de catre x adresa lui x membrul y al obiectului x membrul y al obiectului indicat de catre x membrul y al obiectului (echivalent cu expresia precedenta) primul obiect indicat de catre x al doilea obiect indicat de catre x al (n+1)-lea obiect indicat de catre x indicat de catre x

Pentru a intelege mai bine tabelul anterior trebuie mrevizuite notiunile de pointeri si structuri!

Exercitiul 3.2

Sa se rescrie programul creat la Exercitiul 3.1 astfel incat toate instantele clasei CRectangle sa fie de tip pointer (CRectangle *instance SAU CRectangle *instance[n]). Nota: la terminarea programului se va apela operatorul de stergere (eliberare a memoriei) delete pentru toate instantele create!

Page 8 of 8