Sunteți pe pagina 1din 13

Cap.

3
Definirea i utilizarea claselor n limbajul C++
n acest capitol se ncepe studiul sistematic al claselor, un tip de date definit de programator, care realizeaz unificarea datelor i a procedurilor. Se vor aborda urmtoarele topici: declararea claselor i a obiectelor, accesul la date i la funcii membre ale unei clase, implementarea i utilizarea claselor, cuvntul cheie this, membri unei clase care au caracterul static. Dup cum s-a precizat n capitolul precedent, noiunea de clas nu este specific paradigmei programrii orientate pe obiecte, ns ea reprezint un element fundamental att pentru abstractizarea datelor, ct i pentru programarea orientat pe obiecte. Dintr-un anumit punct de vedere, clasa poate fi privit ca o extensie a noiunii de structur din limbajul C, fiind construcia prin intermediul creia limbajul C++ permite definirea unor noi tipuri de date. ns diferenele ntre struct i union pe de o parte i class pe de alt parte sunt eseniale, ele referindu-se n special la drepturile de acces ale membrilor i la posibilitatea derivrii claselor. Din punct de vedere conceptual, o clas reprezint proprietile comune ale unei colecii de obiecte nrudite, ceea ce justific alegerea cuvntului class pentru aceast noiune. Obiectele ce aparin unei clase se numesc instane ale clasei sau obiecte instan. Astfel, o clas poate fi privit ca un tip de date reprezentat de mulimea tuturor instanelor sale. Din punctul de vedere al compilatorului, un obiect reprezint, ca orice variabil, o zon de stocare a datelor, a crei adres de memorie este unic. n aceast zon se stocheaz valorile curente ale datelor membre corespunztoare obiectului respectiv, dar permite n plus funciilor membre ale clasei din care face parte s opereze asupra acestor valori. Din punct de vedere al implementarii, un program ce utilizeaz programarea orientat pe obiecte presupune att definirea claselor ce se utilizeaz, precum i declararea i utilizarea obiectelor instan cu care se lucreaz. Definirea unei clase const din dou pri distincte: declararea clasei i implementarea clasei respective. Partea de declaraie a unei clase trebuie s specifice att numele clasei i eventual clasele din care este derivat, ct i componentele sau membri clasei respective. Spre deosebire de limbajul C, componentele unei clase (i n mod implicit componentele unei structuri sau uniuni) pot fi att date (membri de tip date), ct i funcii (membri de tip funcie). Exemplul 3.1. Structura unui program ce utilizeaz o clas denumit stack pentru definirea unei stive de caractere poate fi urmtoarea: // fisierul stack.h - declaratia clasei class stack { int dim ; // data membru char *buff ; // data membru public: stack(int) ; // functia constructor ~stack() ; // functia destructor void push(char) ; // functie membru char& pop() ; // functie membru } ; // fisierul stack.cpp implementarea clasei stack :: stack(int n) { // codul pentru constructor } stack :: ~stack() { // codul pentru destructor } void stack :: push(char c) { // codul pentru functia push }

3-1

char& stack :: pop() { // codul pentru functia pop } // fisierul main.cpp utilizarea clasei void main() { // declararea obiectelor instanta stack st1(100), st2(50) ; // utilizarea obiectelor stack st1.push(a) ; // ... } Utilizarea claselor ntr-o aplicaie orientat pe obiecte presupune crearea unei mulimi de obiecte instan i transmiterea spre acestea a anumitor mesaje, precum i recepionarea altor mesaje de la obiecte. Mecanismul de transmitere i recepionare a mesajelor spre/dinspre obiecte l reprezint apelul funciilor membru. 3.1. Declararea claselor O sintax foarte simplificat a declarrii unei clase arat astfel: class Nume_clas { private: // List_membri_1 public: // List_membri_2 }; Cuvntul cheie class indic faptul c urmeaz descrierea unei clase avnd numele Nume_clas. Descrierea propriu-zis a unei clase const n declararea i/sau definirea cele dou liste de membri, prefixate eventual de cuvintele cheie private i public, care mpart clasa n dou seciuni: una privat i una public. Exemplul 3.2. Structura unui program care realizeaz translaia de-a lungul axelor de coordonate a unui punct din spaiul tridimensional poate fi urmtoarea: class coordonate_3D // Declararea clasei coordonate_3D { private: int x, y, z; // Declarare variabile membru public: // Declarare funcie membru void translateaza( int tx, int ty, int tz ); }; // Definirea funciei membru void coordonate_3D :: translateaza( int tx, int ty, int tz ) { x += tx; // x = x + tx y += ty; // y = y + ty z += tz; // z = z + tz } // Declararea a trei obiecte de tipul coordonate_3D class coordonate_3D obiect_1, obiect_2, obiect_3; Variabilele i funciile declarate n interiorul clasei se numesc membri ai clasei. n exemplul de mai sus, avem: trei variabile membru: x, y, z. o singur funcie membru: translateaza(). trei obiecte de tipul coordonate_3D: obiect_1, obiect_2, obiect_3. Membrii clasei coordonate_3D sunt de dou tipuri: privai - aparin seciunii private i nu pot fi accesai dect de ctre funciile publice ale clasei;

3-2

publici - aparin seciunii publice i pot fi accesai din orice punct al programului, dar trebuie prefixai de ctre un nume de obiect (o instaniere a clasei) i de operatorul de selectare ., la fel ca i cmpurile unei structuri. Exemplu: obiect_1.translateaza(1, 2, 3)

O sintax mai rafinat a declarrii claselor n C++ este urmtoarea: Specificator_clas Nume_clas { private: // List_membri_1 public: //List_membri_2 }; Diferena fa de definiia precedent const n nlocuirea lui class cu Specificator_clas. Acesta din urm poate fi unul din urmtoarele cuvinte cheie: class, struct sau union. Ultimele dou descriu structuri de date avnd aceleai proprieti ca i n limbajul C neobiectual, cu dou modificri: li se pot ataa funcii membru; toate elementele (variabile i funcii) sunt de tip public. O clas poate fi derivat din una sau mai multe clase care, n acest caz, se numesc clase de baz pentru clasa curent. n cazul n care clasa ce se declar este derivat din alte clase, acestea trebuie specificate prin numele lor i tipul de acces la ele. Tipurile de acces pot fi private i public. n mod implicit pentru class acesta este private, iar pentru struct tipul de acces este public. Despre tipul de acces la clasele de baz se va discuta ulterior, n cadrul unui capitolul asociat motenirii. Trebuie precizat c, dac o clas are specificatorul union, aceasta nu poate fi nici clas de baz pentru alte clase i nici o clas derivat din alte clase. Dac ntr-o clas declarat cu specificatorul class lipsesc specificatorii public i private, toi membrii clasei vor fi implicit privai. n cazul claselor declarate cu specificatorii struct i union, implicit, toi membrii structurii, respectiv uniunii vor fi de tip public. Pentru fixarea celor prezentate mai sus, n Exemplul 3.3 se prezint listingul unui program n care este declarat o clas NUMARATOR folosit pentru contorizarea numrului de rotaii ale unui dispozitiv magnetic sau mecanic. Exemplul 3.3. Definirea si utilizarea clasei NUMARATOR # define N_TURE 234 # include <iostream.h> class NUMARATOR { // Declararea clasei // Variabile de tip private int cifra_sute, cifra_zeci, cifra_unitati; public: void Setare(int cs, int cz, int cu); // Aici, funcia Setare() este numai declarat void Avans(void) { // Definitia functiei Avans() if (cifra_unitati++ == 9) { cifra_unitati = 0; if (cifra_zeci++ == 9) { cifra_zeci = 0; if (cifra_sute++ == 9) { cifra_sute = 0; } } } } void Afisare(void); // Funcia Afiare() este numai declarat };

3-3

// Definirea funciei Setare() void NUMARATOR :: Setare(int cs, int cz, int cu) { cifra_sute = cs; cifra_zeci = cz; cifra_unitati = cu; } // Definirea funciei Afiare() void NUMARATOR :: Afisare(void) { cout << "Indicatorul = " << "<" << cifra_sute << "|" << cifra_zeci << "|" << cifra_unitati << ">" << endl; } // Programul apelant void main(void) { NUMARATOR nr; // Declararea ob. nr de tip NUMARATOR nr.Setare(0, 0, 0); // Initializarea numaratorului nr int n = N_TURE; while (n--) { nr.Avans(); // Avans una tura nr.Afisare(); // Afisarea indicatorului } } Clasa NUMARATOR conine trei variabile de tipul int plasate n seciunea private a acesteia i trei funcii membru n seciunea public. Prima funcie, Setare() este numai declarat, definirea ei fcndu-se n afara clasei. A doua funcie, Avans() este definit n cadrul clasei. A treia funcie, Afisare(), definit de asemenea n afara clasei, servete la afiarea contorului. Dou aspecte se constat cu precdere n cadrul acestei clase, i anume: 1. Lipsa constructorului i destructorului clasei (funcii ce au fost definite n exemplele din Cap. 2); deci, pot exista clase chiar fr definirea explicit a celor dou funcii speciale: constructorul i destructorul clasei. 2. Funciile membru care sunt numai declarate n cadrul clasei trebuie definite n afara acesteia. La momentul executrii instruciunii NUMARATOR nr; chiar n absena constructorului, se aloc memorie pentru obiectul nr. Acest obiect este recunoscut n ntreaga funcie main(). Referirea la o metod (funcie membr a clasei) se realizeaz, aa cum se vede, prin intermediul construciei nume_obiect.nume_metod. Operatorul . are acelai sens cu cel din limbajul C, ntrebuinat la referirea unui cmp dintr-o nregistrare (struct, union). n C folosim cuplul nume_structur.nume_cmp. Nu trebuie confundat obiectul (variabila) nr cu ablonul su, clasa NUMARATOR. n C++, obiectul poate fi considerat precum o variabil n limbajul C. Numai c (atenie!), se spune c prin instruciunea: NUMARATOR nr; declarm obiectul nr, iar cnd se execut instruciunea: nr.Setare(0, 0, 0), definim valorile variabilelor sale de stare. Variabilele de tip private cifra_sute, cifra_zeci, cifra_unitati nu pot fi modificate direct din orice linie a funciei main(), ci numai prin intermediul funciilor membru Setare() i Avans(). Funcia Afiare() doar inspecteaz variabilele respective. Se poate defini astfel conceptul de ascundere a datelor (acesta se refer la date definite n cadrul unei clase, de regula n seciunea privat a acesteia, i care nu pot fi accesate direct din exteriorul clasei, ci numai prin intermediul funciilor membru ale clasei). Presupunem c n Exemplul 3.4, clasa NUMARATOR se declar sub forma: class NUMARATOR { public:

3-4

int cifra_sute, cifra_zeci, cifra_unitati; // Urmeaz cele trei funcii declarate mai sus }; n aceast situaie, vom avea acces direct din exteriorul clasei la cele trei variabile de stare, nu numai prin intermediul funciilor Setare(), Avans() i Afisare(). Este ca i cnd am fi declarat n C++ clasa sub forma structurii: struct NUMARATOR { int cifra_sute, cifra_zeci, cifra_unitati; void Setare(int cs, int cz, int cu); void Avans(void); void Afisare(void); }; ntr-o construcie de tipul struct totul este public, spre deosebire de aceea de tipul class. Limbajul C++ permite i crearea unor obiecte constante. Asupra unor astfel de obiecte pot aciona numai funcii membru constante ale clasei. n declaraia clasei, o funcie membru constant se specific cu ajutorul cuvntului cheie const plasat imediat dup antetul funciei. O asemenea funcie nu trebuie s modifice valorile datelor membre ale clasei din care face parte, iar acest lucru este verificat de ctre compilator. Exemplul 3.4. Definirea unei clase cerc: class cerc { double xc, yc ; double r ; public: cerc(double a, double b, double c) // functia { xc = a ; yc = b ; r = c ;} double GetXc() const { return xc ; } // functie double GetYc() const { return yc ; } // functie double GetR() const { return r ; } // functie void Translate(double dx, double dy) { xc += dx ; yc += dy ; } } ; Utilizarea clasei cerc: cerc c1(0, 0, 10) ; const cerc c2(8, 7, 5) ; // obiect constant c1.Translate(2, 3) ; // corect c2.Translate(2, 3) ; // incorect double x = c2.GetXc() ; // corect

constructor constanta constanta constanta

Funcii de acces Funciile de acces reprezint o categorie de funcii membru foarte utilizate. Acestea sunt funcii definite uzual inline, care permit citirea sau modificarea valorilor variabilelor membru private ale claselor, astfel nct utilizatorul s nu aib acces direct asupra acestora. Funciile de citire se numesc n mod uzual accesori, iar celelalte modificatori. Nu exist reguli predefinite pentru alegerea numelor acestora, dar de obicei accesorii sunt prefixai de Get, iar modificatorii de Set. De exemplu, funciile GetXc, GetYc i GetR sunt funcii accesor. O funcie modificator se poate defini astfel: void SetXc(double x) { xc = x; } O alt variant des utilizat const n scrierea unor funcii suprancrcate, att pentru accesori, ct i pentru modificatori. De exemplu, pentru data membru xc a clasei cerc, se pot defini urmtoarele funcii de acces: void Xc(double x) { xc = x; } double Xc() const { return xc; } Observaie. Nu este indicat ca funciile de tip accesor s returneze referine sau pointeri neconstani la datele private ale claselor (n caz contrar s-ar permite utilizatorilor accesul direct la acestea). 3.2. Accesul la datele i funciile membre ale unei clasei
3-5

ablonul complet de declarare a unei clase este urmtorul: class Nume_clas { private: // Seciunea privat; // de regul cuvntul cheie private lipsete protected: // Seciunea protejat (opional) public: // Seciunea public, // interfaa cu lumea exterioar clasei }; Deoarece membri unei clase pot fi date sau funcii, declararea lor se face n mod asemntor ca i declararea variabilelor i funciilor n limbajul C. Excepie fac dou categorii speciale de membri funcie, numii constructori i destructori, despre care se va vorbi ulterior. Aa cum s-a vzut, n mod opional, declararea unui membru al unei clase poate fi precedat de un modificator de acces al membrului respectiv (a nu se confunda cu modificatorul de acces al unei clase de baz). Acest modificator de acces poate fi private, protected sau public. Modificatorul de acces la un membru al unei clase specific modul n care membrul respectiv poate fi vzut n exteriorul clasei. Un membru public este vizibil n exterior, un membru private este inaccesibil, iar un membru protected poate fi accesibil doar ntr-o clas derivat din clasa respectiv cu modificatorul de acces public. Cu alte cuvinte, n afar de clasele derivate public din clasa curent, toi membri protected sunt inaccesibili n exterior. Despre tipul protected se va discuta ulterior. Membrii publici ai unei clase pot fi accesibili n exterior i reprezint interfaa prin intermediul creia clasa comunic cu exteriorul; membri privai sunt locali clasei respective. Din punct de vedere al domeniului de vizibilitate, numele declarate n interiorul unei clase sunt interne acesteia. Aceasta permite definirea unor membri diferii n clase diferite, dar cu acelai nume. Observaie. n clasa curent, un modificator de acces afecteaz accesibilitatea tuturor membrilor declarai dup acesta, pn la ntlnirea unui alt modificator de acces. Dac primul membru declarat al clasei nu are specificat un modificator de acces, atunci, n mod implicit, acesta este private pentru class i public pentru struct. 3.3. Implementarea claselor. Operatorul de rezoluie Pentru definirea complet a unei clase trebuie definite toate funciile membru din declaraia clasei respective care nu au fost definite in interiorul clasei (inline). n mod uzual, definirea funciilor se face ntr-un fiier distinct de fiierul header ce conine declaraia clasei. n multe aplicaii ns, un fiier header conine declaraiile mai multor clase nrudite ce pot forma una sau mai multe ierarhii de clase. n acest caz, este posibil s existe mai multe funcii cu acelai nume declarate n clase diferite ntre care nu exist relaia de motenire, care nu sunt suprancrcate i care reprezint funcii diferite (lucru permis de limbaj datorit domeniului de definiie al numelor declarate n interiorul unei clase). Pentru a putea specifica, n cazul fiecarei funcii membru, clasa de care aparine, limbajul C++ pune la dispoziie un nou operator numit operator de rezoluie - scope resolution operator (notat ::). Utiliznd operatorul de rezoluie, specificarea complet a numelui unei funcii se face astfel: <nume_clasa> :: <nume_functie_membru> n listingul programului din Exemplul 3.4 sunt prezentate dou exemple ale modului de utilizare a operatorului de rezoluie: // Definirea funciei Setare() void NUMARATOR :: Setare(int cs, int cz, int cu) { cifra_sute = cs;

3-6

cifra_zeci = cz; cifra_unitati = cu; } // Definirea funciei Afiare() void NUMARATOR :: Afisare(void) { // Codul functiei } n clasa NUMARATOR, variabilele cifra_sute, cifra_zeci, cifra_unitati pot fi considerate variabile locale n raport cu acest bloc delimitat de cele dou acolade. Dei definiiile funciilor membre sunt n afara clasei, totui operatorul :: conecteaz aceste funcii la clasa NUMARATOR. n aceast situaie, scopul celor trei variabile se extinde i n cadrul acestor funcii. Putem deci considera aceste variabile drept globale n raport cu funciile membre ale clasei NUMARATOR. Modul de utilizare a operatorului :: poate fi observat i n Exemplul 3.3 la definirea funciei translateaza(): void coordonate_3D :: translateaza(int tx, int ty, int tz) { // Codul functiei } Operatorul de rezoluie poate fi utilizat i n alte cazuri dect cel prezentat anterior. O utilizare frecvent a acestuia se refer la specificarea unui nume care este ascuns n cadrul unui bloc datorit regulii domeniului de vizibilitate. De exemplu, o variabil definit ntr-un fiier n afara oricrei funcii este vizibil n toate funciile din fiierul respectiv, mai puin n acele blocuri n care ea este redefinit: int k ; // var. k este definite in afara oricarei functii f1() { // k este vizibila in acest bloc } f2() { int k = 0 ; // variabila k redefinita in f2 k = k+2 ; // variabila k din f2 ::k = ::k+2 ; // variabila k din domeniul exterior } Operatorul de rezoluie poate fi utilizat n acest caz ca un operator unar ce prefixeaz un nume, caz n care el refer cea mai din exterior apariie a numelui respectiv, declarat la nivelul de fiier. n exemplul anterior, instruciunea: k = k+2; refer variabila definit n interiorul funciei f2, pe cnd: ::k = ::k+2; refer variabila definit la nivelul fiierului. Exemplul urmtor arat modul de acces la dou variabile notate la fel, una fiind global i cealalt local, de tip private. De asemenea, se arat modul de folosire a unei funcii proprii denumit puts(), o funcie cu acelai nume ca cea din fiierul antet stdio.h. Scopul acestui exemplu este de a lmuri notaia legat de folosirea operatorului unar ::. Exemplul 3.5. Definirea unei funcii proprii care are un nume identic cu numele uneia din funciile aflate n biblioteca C++. # include <stdio.h> # include <math.h> // Variabila v global double v; class ADHOC { double v, w; // Variabila v local public: void Init(double x, double y) {v = x; w = y;} // v este variabila local double Modul(void) {return sqrt (v*v + w*w);} // v este variabila local

3-7

void puts(char *); // Declaraia propriei funcii puts() }; void ADHOC :: puts (char *mesaj) // Definirea propriei funcii puts() { :: puts("Sir scris prin intermediul propriei functii puts:"); // Functia apelata puts() este cea din <stdio.h> :: puts(mesaj); :: v += 1; // v este variabila global } void main (void) { ADHOC a; // Se creeaz obiectul a v = 3.0; // v global = 3.0 a.Init(v, 4.); // v local = v global = 3.0, w = 4.0 v = 15.0; // v global = 15.0 printf( "Modulul = %f\n", a.Modul() ); a.puts("Apelez propria functie puts"); } Pentru a apela variabila v (global) avnd aceeai denumire cu variabila v (local) din interiorul unei funcii membre a clasei se folosete notaia ::v. Simpla ntrebuinare a lui v nseamn referirea la variabila v (local) de tip private. Instruciunea a.Init(v,4.) din funcia main() iniializeaz variabila local v a obiectului a cu valoarea variabilei globale v = 3 i variabila w cu valoarea 4. n program se definete o funcie membru denumit puts(), funcie care are un nume identic cu numele unei funcii ce are prototipul n fiierul antet stdio.h i care este utilizat pentru afiarea unui ir. Din definiia acestei funcii, se vede c aceasta apeleaz funcia puts() standard. Acest apel se realizeaz prin plasarea operatorului :: n faa numelui funciei definite n exteriorul clasei, n cazul nostru puts(). n urma executrii instruciunii ::v += 1 din funcia puts(), v (global) devine egal cu 16, n loc de 15. 3.4. Utilizarea claselor Dup cum s-a precizat anterior, utilizarea claselor nseamn crearea anumitor obiecte instan ale acestor clase i comunicarea cu obiectele respective prin intermediul mesajelor, adic a funciilor membru ale lor. Obiectele instan se comport ntr-un mod asemntor variabilelor, n sensul c li se rezerv o zon de memorie pentru stocarea valorilor datelor membre. Toate observaiile referitoare la clasele de memorare pentru variabile sunt valabile i n cazul obiectelor, astfel nct alocarea memoriei pentru obiecte se poate face att n zona de date, ct i n zona stiv i n zona heap. Indiferent de zona de alocare, crearea unui obiect presupune dou aciuni distincte: alocarea de ctre compilator a unei zone de memorie de dimensiune corespunztoare; apelul unei funcii constructor al clasei din care obiectul face parte, care realizeaz, printre altele, iniializarea anumitor date membre cu valori. Dimensiunea zonei de memorie alocat pentru un obiect instan este n general dat de suma dimensiunilor datelor membre ale obiectului, dar aceast dimensiune depinde de implementare. Exist situaii n care dimensiunea zonei de memorie a unui obiect este mai mare dect suma componentelor, n special n cazul utilizrii polimorfismului, sau a unor clase care nu conin dect funcii membru. De exemplu, pentru un anumit compilator Visual C++, urmtoarele declaraii: #include <iostream.h> struct A { int n; A(int k) { n = k ;} int N() { return n; } };
3-8

struct B { void Print() { cout<<"B"; } }; void main() { A a; B b; cout << sizeof(a) << endl; cout << sizeof(b) << endl; } au ca efect afiarea valorilor 4 i 1, deoarece valorile int se reprezint pe 4 octei. Valoarea 1 apare deoarece compilatorul nu permite existena unor obiecte cu dimensiunea zero. 3.5. Crearea mai multor obiecte Prin crearea mai multor obiecte ale unei clase, fiecare obiect recepioneaz copia sa proprie n memorie, n care apar datele membru specifice celor trei categorii de domenii definite mai sus (dac exist toate).

Dei zona de cod pentru funciile membre ale unei clase nu se copiaz n zonele de memorie alocate obiectelor instan ale clasei, apelul unei funcii membru a unui obiect este legat strict de obiectul respectiv, prin transmiterea unui parametru ascuns ce refer adresa de memorie a obiectului respectiv. n Exemplul 3.4 am fi putut scrie: NUMARATOR nr[10] i am fi creat zece obiecte identice. S considerm urmtorul program (Exemplul 3.6), n care apare o variabil global n_obiecte care va servi la numrarea obiectelor n via la un moment dat. Exemplul 3.6. Crearea obiectelor n cadrul blocurilor # include <iostream.h> int n_obiecte = 0; // Variabila global n_obiecte class OB { // Obiectul nu conine date private public: OB() { // Constr. clasei crete cu 1 variabila n_obiecte n_obiecte ++; // Putem s ne referim la variabile // globale din interiorul clasei cout << "Numarul obiectelor in viata " << n_obiecte << endl; } ~OB() { // Destruct. Cls. descrete cu 1 variabila n_obiecte n_obiecte --; cout << "Au mai ramas doar " << n_obiecte << " obiecte"<< endl; } }; void main (void) { OB a, b, c; // Se creeaz primele 3 ob., notate a, b, c { // Se deschide un nou context OB d, e; // Se creeaz nc 2 obiecte, notate d, e } // Aici destruct. va distruge ob. d i e { // Se redeschide un alt context OB f; // Se creeaz obiectul f } // Aici destructorul va distruge ob. f } // Aici se elibereaz memoria ocupata de obiectele a, b, c
Prin execuia acestui program se va obine numrul obiectelor alocate n memorie la un moment dat. Cum comentariile din acest program sunt lmuritoare, chiar exhaustive, considerm c alte explicaii nu mai sunt necesare. Precizm totui c variabila global n_obiecte poate fi modificat nu numai de ctre funciile clasei OB, ci chiar i direct din funcia main().

3-9

Din Exemplul 3.6 se observ c la fiecare declaraie a unui obiect (de exemplu, OB a, b, c) este apelat n mod automat constructorul care aloc/rezerv spaiu pentru membrii clasei. Obiectele au o existen efemer, deoarece la ntlnirea acoladei }, destructorul va elibera spaiul ocupat de obiectele create. Obiectele de mai sus nu au avut date proprii. Ele au folosit o variabil (n_obiecte) pe care constructorii i destructorii au modificat-o pe msura crerii i distrugerii acestor obiecte. Pentru ca starea (valoarea) unor variabile proprii obiectelor s fie pstrat (memorat) se poate folosi cuvntul cheie static care s prefixeze aceste variabile, aa cum se va vedea ntr-un paragraf urmtor. 3.6. Prevenirea redeclarrii claselor De regul, declaraiile i definiiile claselor nu sunt plasate n acelai fiier cu funcia main, ci ntr-un fiier antet inclus cu o comand de genul: # include <nume_fiier_antet>. Pentru evitarea redeclarrii unei clase se recomand utilizarea urmtorului grup de comenzi (instruciuni de preprocesare): # ifndef nume # define nume // Declaraia i definiia clasei # endif De exemplu, programul din Exemplul 3.4 poate fi alctuit dintr-un fiier antet NUMARATOR.H i programul propriu-zis. Structura fiierului antet NUMARATOR.H ar putea fi urmtoarea: Exemplul 3.7. Declararea i definirea clasei NUMARATOR # ifndef NUMARATOR.H # define NUMARATOR.H # include <iostream.h> class NUMARATOR { // Declaraia i definiia din Exemplul 3.4 }; // Implementarea funciilor membre # endif NUMARATOR.H n acest caz, programul din Exemplul 3.4 se rescrie astfel: # define N_TURE 234 # include NUMARATOR.H void main (void) { NUMARATOR nr; // Declararea nr.Setare (0, 0, 0); // int n = N_TURE; while (n--) { nr.Avans (); // nr.Afisare (); // } } 3.7. Cuvntul cheie this

obiectului nr de tip NUMARATOR Initializarea numaratorului nr Avans una tura Afisarea indicatorului

Din exemplele anterioare, se vede c pentru a defini funciile membre sunt necesare referiri la datele membre ale clasei, fr a specifica ns un obiect anume. Dei, dup declarare, pentru fiecare obiect al unei clase exist (ntr-o zon de memorie) cte o copie a variabilelor membru, nu i a funciilor membru, toate obiectele i mpart un singur set de funcii membru. La apelare, o funcie membru este informat asupra identitii obiectului asupra cruia va aciona prin transferul unui parametru implicit, care reprezint adresa obiectului. Deci, la apelul unei funcii membru, compilatorul C++ modific fiecare funcie membru dintr-o clas fcnd dou schimbri:
3 - 10

1. Transmite un argument suplimentar cu numele this, reprezentnd un pointer la obiectul specific care a apelat funcia. Acesta este numit pointer la self i indic ntotdeauna spre obiectul curent. 2. Adaug prefixul this -> tuturor variabilelor i funciilor membre. De exemplu, folosind operatorul this, funcia Setare() din clasa NUMARATOR putea fi definit, astfel: // Definirea funciei Setare() folosind operatorul this void NUMARATOR :: Setare (int cs, int cz, int cu) { this -> cifra_sute = cs; this -> cifra_zeci = cz; this -> cifra_unitati = cu; } Operatorul this va puncta pe nceputul ablonului clasei, n cazul nostru, NUMARATOR, Fig. 3.1. Putem astfel s ne referim la oricare element (membru de tip dat sau funcie) al obiectului this -> cifra_sute curent, nu ns i n interiorul unei funcii. this ntoarce deci cifra_zeci adresa de nceput a obiectului curent. cifra_unitati Notaia *this va nsemna referirea la ntregul obiect. De Setare() exemplu, dac se dorete a se returna obiectul curent programului Avans() apelant, se poate folosi instruciunea: Afisare() return *this; Fig. 3.1

Observaie. n cazul unei clase oarecare X, parametrul: X* this este transmis n toate funciile membru nestatice ale clasei X. La crearea unui obiect instan al unei clase, dup operaia de alocare a memoriei pentru obiect, se apeleaz n mod implicit un constructor pentru obiectul respectiv. Acest constructor va avea i el this ca parametru ascuns, iniializat cu adresa blocului de memorie asociat obiectului. S presupunem c ntr-o aplicaie se definesc dou obiecte de tip p_poligon: p_poligon* p = new p_poligon ; // ... p_poligon d ; // ... n ambele cazuri se apeleaz constructorul clasei p_poligon: n primul caz el este apelat implicit de ctre operatorul new, iar memoria se aloc n zona heap a programului, iar n cazul al doilea constructorul este de asemenea apelat implicit de ctre compilator, dar memoria pentru obiect se aloc n zona de date. n ambele cazuri, obiectele create vor conine o copie a tuturor datelor membre ale clasei p_poligon, iar unii dintre membri vor fi iniializati de ctre constructori cu anumite valori. n plus, toi parametri this ai funciilor membru, pentru fiecare obiect, vor fi iniializati cu adresa blocului de memorie asociat obiectului (n primul caz, de exemplu, cu valoarea pointerului p). Membri statici ai unei clase
Dup cum s-a mai precizat, n mod normal fiecare obiect instan a unei clase posed o copie a datelor membre ale clasei de care aparine. Din acest motiv orice modificare a valorii unui membru a obiectului este local instanei respective i nu se vede n cadrul altor instane ale aceleiai clase.

3 - 11

Limbajul C++ ofer posibilitatea n plus a definirii unor membri ce pot fi utilizai n comun de toate instanele unei clase. Aceti membri se numesc membri statici i sunt declarai cu ajutorul cuvntului cheie static. Exemplul 3.8. Se consider o clas Experiment ce permite descrierea observaiilor efectuate asupra unei mrimi fizice. Fiecare obiect al clasei memoreaz o valoare msurat a mrimii fizice. Clasa Experiment trebuie s determine numrul de observaii la un moment dat, precum i media aritmetic a valorilor irului de determinri. Se utilizeaz dou date membru statice (n i s) care memoreaz, respectiv, numrul de obiecte instan create pn la momentul curent i suma valorilor lor, precum i dou funcii membru statice (N i Med) pentru determinarea numrului de obiecte curente ale clasei i a mediei aritmetice a valorilor acestora. //fisierul experiment.h class Experiment { double x; static int n; static double s; public: Experiment(double); double X() const { return x; } static double Med() { return s/n; } static int N() { return n; } }; Datele membru de tip static sunt comune pentru toate obiectele unei clase i au alocat o zon de memorie diferit de datele nestatice. n acest mod se poate realiza comunicarea simpl i eficient ntre diferitele obiecte ale unei clase. Datorit modului de stocare, definirea efectiv (alocarea memoriei i iniializarea cu valori) a datelor membre statice trebuie fcut n afara declaraiei clasei i ntr-un singur loc din program. n mod uzual definirea lor se face n fiierul ce conine implementarea clasei respective, evitndu-se n acest mod definiiile multiple. Pentru exemplul anterior, n fiierul de implementare al clasei Experiment trebuie adaugate: int Experiment::n = 0 ; double Experiment::s = 0 ; ca n exemplul urmtor: //fisierul experiment.cpp int Experiment::n = 0; double Experiment::s = 0; Experiment::Experiment(double v) { x = v; n++; s += v; } Constructorul clasei Experiment trebuie s efectueze dou aciuni suplimentare pentru fiecare nou obiect creat: incrementarea numrului total n de obiecte ale clasei, precum i adugarea valorii curente v a obiectului la suma s a tuturor valorilor obiectelor clasei. Datele statice pot avea orice tip de acces (public, protected, private), ca orice membru al unei clase. Dei, exemplul anterior ar putea duce la concluzia c datele statice pot fi folosite oriunde ntr-un program, aceast concluzie este fals: fiind statice, compilatorul nu permite o alt iniializare a lor, iar modificarea valorii unor date membru statice private n afara clasei n care au fost declarate nu este permis. Utilizarea datelor membru statice conduce la o structurare mai bun a informaiei ntr-un program, deoarece acestea sunt globale doar pentru obiectele clasei n care au fost declarate. n exemplul anterior, N i Med sunt dou funcii membru statice a clasei Experiment. Ca i datele membru statice, funciile membru statice ale unei clase sunt unice pentru toate instanele clasei respective. Utilizarea lor este necesar n cazul n care anumite clase au date membru statice.

3 - 12

Funciile membru statice nu pot utiliza parametrul ascuns this. Aceast particularitate atrage dup sine o alt restricie: funciile membru statice nu pot avea acces dect la membrii statici ai clasei respective (date sau funcii). n exemplul anterior, funcia Med nu poate modifica valoarea membrului x i nici nu poate apela alte funcii membru ale clasei (funcia X, de exemplu). O alt particularitate a acestor funcii o constituie faptul c membri statici publici se pot apela i direct, fr ajutorul unui obiect din care fac parte, utiliznd operatorul de rezoluie. De exemplu, un fiier de utilizare pentru clasa Experiment ar putea fi urmtorul (s-a considerat irul de valori 0.5, 1.5, 2.5, , 9.5) : // fisierul main.cpp void main() { for (int i=0; i<10; i++) Experiment e(i+0.5); int n = Experiment::N(); double m = Experiment::Med(); cout << "n = " << n << endl << "Med = " << m << endl; } Se observ faptul c funciile N i Med au fost apelate fr intermediul vreunui obiect al clasei Experiment. Observaii: 1. Funciile membru nestatice pot referi membrii statici ai clasei respective (de exemplu, cazul constructorului clasei Experiment). 2. O funcie membru static privat nu poate fi apelat prin intermediul unui obiect al clasei respective. 3. Dac o funcie membru a fost declarat static n cadrul unei clase, dar nu a fost definit inline, n definiia efectiv a acesteia nu mai trebuie specificat cuvntul static. De exemplu: class A { // ... static int x ; static void SetX(int) ; // ... } ; void A::SetX(int k) { x = k ; }

3 - 13

S-ar putea să vă placă și