You are on page 1of 90

UNIVERSITATEA TITU MAIORESCU DIN BUCURE TI FACULTATEA DE INFORMATIC

Programarea orientat pe obiecte n limbajul C++


-suport curs pentru forma de nvatamnt ID -

Lector univ. drd. Dsclescu Ana Cristina

- 2010 -

Cuprins
Cap. 1 Introducere in programarea orientata pe obiecte ..................................................................3 1.2 Concepte ale programrii orientate pe obiecte.......................................................................5 1.3 Elemente introductive ale programrii orientate pe obiecte n limbajul C++ ......................7 1.4 Tipuri de date .........................................................................................................................9 1.5 Operatori specifici C++........................................................................................................17 Cap.2 Clase si Obiecte .................................................................................................................21 2.1 Definirea unei clase..............................................................................................................23 2.2 Constructori si destructor .....................................................................................................25 2.3 Tablouri de obiecte...............................................................................................................31 2.4 Funcii i clase friend ...........................................................................................................32 2.5 Funcii inline .......................................................................................................................33 2.6 Date si functii statice ............................................................................................................34 Capitolul 3 .Mostenire....................................................................................................................41 3.1 Relatia de mostenire. Clase de baz si clase derivate ..........................................................41 3.2 Constructori i destructori n clasele derivate ......................................................................44 3.3 Controlul accesului la membrii clasei de baz .....................................................................45 3.4 Mostenirea multipla..............................................................................................................47 3.4 Clase de baz virtuale...........................................................................................................50 3.6 Funcii virtuale i polimorfism.............................................................................................51 3.7 Clase abstracte......................................................................................................................54 3.8 Polimorfism..........................................................................................................................56 Cap. 4 Facilitti ale limbajului C++...............................................................................................64 4.1 Supraincarcarea operatorilor ................................................................................................64 4.2 Funcii operator membre ale claselor ...................................................................................64 4. 3 Template-uri ........................................................................................................................68 4.4 Stream-uri de I/E ..................................................................................................................69 4.5 Funcii de I/O pentru tipurile predefinite .............................................................................69 Funcii de I/O pentru tipuri definite de utilizator .......................................................................71 4. 6 Procesarea fisierelor ............................................................................................................72 Teste recapitulative: .......................................................................................................................76

Cap. 1 Introducere in programarea orientata pe obiecte

Obiective: Conceptul programre orientata pe obiecte; Paralela ntre programarea procedural si cea orientat pe obiecte; Tipuri de date abstracte. Abstractizare; Concepte de baz ale programrii orientate pe obiecte; Scurta incursiuni n programarea orientat pe obiecte prin intermediul limbajului C++.
Modalitile (tehnicile, paradigmele) de programare au evoluat de-a lungul anilor, reflectnd trecerea de la programe de dimensiuni reduse, la programe i aplicaii de dimensiuni foarte mari, pentru coordonarea crora sunt necesare tehnici evoluate. Software-ul de dimensiuni mari, care nu poate fi realizat de o singur persoan, intr n categoria sistemelor complexe, cu o mare diversitate de aplicaii. Situaiile reale i experiene ale psihologilor au relevat limitele capacitii umane n perceperea sistemelor complexe, adic imposibilitatea unei persoane de a percepe i controla simultan un numr mare de entiti de informaie. De aceea, este esenial descompunerea i organizarea sistemelor complexe, pentru a fi percepute, proiectate sau conduse. Modul n care este abordat programarea, din punct de vedere al descompunerii programelor, definete mai multe tehnici de programare , care s-au dezvoltat i au evoluat odat cu evoluia sistemelor de calcul. Programarea procedural este prima modalitate de programare care a fost i este nc frecvent folosit. n programarea procedural accentul se pune pe descompunerea programului n proceduri (funcii) care sunt apelate n ordinea de desfurare a algoritmului. Limbajele care suport aceast tehnic de programare prevd posibiliti de transfer a argumentelor ctre funcii i de returnare a valorilor rezultate. Limbajul Fortran a fost primul limbaj de programare procedural. Au urmat Algol60, Algol68, Pascal, iar C este unul din ultimele invenii n acest domeniu. Programarea modular. n cursul evoluiei programrii procedurale, accentul n proiectarea programelor s-a deplasat de la proiectarea procedurilor ctre organizarea datelor, aceast deplasare reflectnd creterea dimensiunilor programelor. O mulime de proceduri corelate, mpreun cu datele pe care le manevreaz, sunt organizate ca un modul. Tehnica de programare modular decide descompunerea unui program n module, care ncorporeaz o parte din datele programului i funciile care le manevreaz. Aceast tehnic este cunoscut ca tehnic de ascundere a datelor (data-hiding). n programarea modular stilul de programare este n 3

continuare procedural, iar datele i procedurile sunt grupate n module, existnd posibilitatea de ascundere a unor informaii definite ntr-un modul, fa de celelalte module. Gruparea de date i proceduri n module nu implic i o asociere strict ntre acestea. Programarea orientat pe obiecte apeleaza la o modalitate nou de gndire a unei probleme. Spre deosebire de programarea procedural care se concentreaz pe structuri de date si algoritmi, programarea orientat pe obiecte se concentreaz pe definirea de obiecte care modeleaz problema ce trebuie rezolvat. n programarea orientat pe obiecte (POO) un program are rolul de a simula strile si activittile obiectelor lumii reale. Pe lng structuri de date (care descriu strile obiectelor, atributele acestora) trebuie incluse si metodele asociate obiectelor, adic acele functii care modific atributele obiectelor si care descriu comportamentul lor.

1.1 Abstractizarea datelor. Tipuri de date abstracte


Primul pas pe care il facem cand scriem un program care sa realizeze diferite operatii, este sa gasim un model care simplifica realitatea, prin separearea detaliilor care interreseaza, de cele care nu afecteaza problema pe care o rezolvam asfel, datele cu care se lucreaza, operatiile , tin de specificul fiecarei probleme tratate. Acest proces de grupare a datelor si metodelor de prelucrare specifice rezolvarii unei probleme se numeste abstractizare. n cazul dezvoltrii unui produs software, abstractizarea se poate defini ca fiind o structurare a unei probleme n entitti bine precizate prin definirea datelor si a operatiilor asociate. Aceste entitti combin date si operatii care sunt necuplate ntre ele. Procedurile si functiile au fost primele dou mecanisme de abstractizare larg rspndite n limbajele de programare. Procedura a reprezentat si prima modalitate de ascundere a informatiei (interfata cu procedura). Modulele au reprezentat urmtorul mecanism de abstractizare prin gruparea procedurilor si functiilor ce sunt relationate. n continuare, tipurile de date abstracte au realizat un pas important ctre dezvoltarea unui software de calitate si au permis trecerea la abordarea orientat pe obiecte a problemelor. Un tip de date abstract (TDA) const dintr-o structur de date abstract si o multime de operatii. Interfata unui TDA este definit de multimea de operatii si reprezint acea portiune a unui TDA care este vizibil din exterior. Conceptul de tip de date abstract presupune existenta unui mecanism de separare a specificatiei de implementare. Aceasta nseamn c un utilizator al unui TDA trebuie s cunoasc doar modul de utilizare al tipului de date, nu si detaliile de implementare. Un tip de date abstract (TDA) este caracterizat de urmtoarele proprietti: 1. export un tip; 2. export o multime de operatii (furniznd interfata TDA); 3. singurul mecanism de acces la structura de date a tipului este furnizat de operatiile definite n interfat; 4. axiomele si preconditiile definesc domeniul deaplicatie al tipului.

De exemplu, dorim sa construim TDA persona. Sa presupunem ca realizam o aplicatie necesara pentru realizarea recesamnatului populatiei, atunci datele care interseaza pentru tipul persoana sunt : nume, prenume, loc_nastere, adresa etc. Daca aplicatia presupune, in schimb, gestiunea intretinerii pentru o asociatie de locatari, atunci pentru tipul persoana sunt necesare si date cum ar fi : spatiul_locuit, nr_persoane etc. Deci, prin procesul de abstarctizare, separam datele care intereseaza de cele care nu fac obiectul aplicatiei. Construim, ulterior, un tip abstract de date care inglobeaza o structura de date impreuna ca operatii aupra datelor ( ex: calculul intretinerii pentru o persoana, nr_persoane pe fiecare judet etc.). Exemplu: TDA_persona nume prenume spatiu_locuit

structura de date

calcul intretinere; - operatii asupra datelor Programarea orientat pe obiecte este programarea cu tipuri de date abstracte, care combin functionalittile acestora pentru rezolvarea problemelor. n programarea orientat pe obiecte, TDA-urile sunt numite clase.

1.2 Concepte ale programrii orientate pe obiecte


Conceptele programrii orientate pe obiecte au aprut din dezvoltrile realizate n cadrul limbajelor moderne de programare. Astfel, limbajele orientate pe obiecte au noi structuri care mbunttesc ntretinerea programului si fac ca portiuni mari de program s fie reutilizabile, conducnd astfel la scderea costului de dezvoltare a produselor software. Cele sase concepte de baz ce caracterizeaz programarea orientat pe obiecte sunt : Obiectele; Clasele; Mesajele Incapsularea; Mostenirea Polimorfismul. Obiectele Un obiect poate fi considerat ca fiind o entitate care ncorporeaz att structuri de date (denumite atribute) ct si comportament (actiuni). Obiectele sunt inteligente prin faptul c realizeaz anumite actiuni si stiu cum s execute aceste actiuni. Inteligenta unui obiect reprezint o form de abstractizare prin faptul c presupune c un obiect poate executa o actiune si ascunde detaliile referitoare la modul n care se va realiza efectiv actiunea. Obiectele pot fi de tipuri diferite: entitti fizice, algoritmi, relatii sau subsisteme. Practic, obiectele sunt componente de software reutilizabil care modeleaz elemente din lumea real. Pentru tipul de date abstract persoana definit mai sus un exemplu de obiect poate fi definit astfel :

Popescu Ion, 34, 56 m2 . Clasele Clasele desemneaz o colecie de obiecte (de natur material sau spiritual) care au n comun faptul c pot fi caracterizate similar din punct de vedere informaional i comportamental. Este evident faptul c identificarea unei clase este n mod normal, rezultatul unui demers cognitiv care presupune caracterizarea unui obiect prin nsuirile lui (informaionale i comportamentale) care i definesc apartenena la o anumit clas de obiecte. Aadar, conceptul de clas adun laolalt datele i metodele de prelucrare a acestora. O clasa reprezinta de fapt o implemntare a tipului abstract de date. O declarare a unei clase definete un tip nou care reunete date i funcii. Acest tip nou poate fi folosit pentru a declara obiecte de acest tip, deci un obiect este un exemplar (o instan) a unei clase.

Mesajele Obiectele pot comunica ntre ele prin intermediul mesajelor. Trimiterea unui mesaj care cere unui obiect s aplice o anumit actiune numit metod este similar apelului de procedur din limbajele de programare procedural. n programarea orientat pe obiecte se consider c obiectele sunt autonome si pot comunica ntre ele prin interschimb de mesaje. Obiectele reactioneaz atunci cnd primesc mesaje, aplicnd o anumit metod, de exemplu. Ele pot refuza executarea metodei respective dac, de exemplu, obiectului care este apelat nu i se permite s execute metoda cerut. Un mesaj este o cerere adresat unui obiect pentru a invoca una din metodele sale. Astfel, un mesaj contine numele metodei si argumentele metodei. Exemplul : calculul intretinerii pentru obiectul Popescu Ion. Incapsularea nelegerea acestui principiu presupune dou nivele de abordare. @ Ca metod de concepie, ncapsularea se refer la capacitatea de a separa aspectele externe ale unui obiect (interfaa), accesibile altor obiecte, de aspectele implementaionale, interne obiectului, care sunt ascunse fa de celelalte obiecte. Utilizatorul unui obiect poate accesa doar anumite metode ale acestuia, numite publice, n timp ce atributele i celelalte metode i rmn inaccesibile (acestea se numesc private). ncapsularea este foarte important atunci cnd dorim s schimbm implementarea anumitor metode (cu scopul de a optimiza un algoritm sau de a elimina posibile erori). ncapsularea ne va mpiedica s modificm toate caracteristicile obiectului iar aplicaiile care utilizeaz obiectul nu vor avea de suferit deoarece protocolul de comunicaie al obiectului motenit de la interfaa clasei (rezultatul ncapsulrii ca metod de concepie) nu s-a schimbat.

@Ca implementare, la nivelul unui limbaj de programare, ncapsularea este asigurat de exigenele sintactice specifice. Mostenirea Mecanismul derivarii permite crearea facila de noi clasa, care preiau caracteristicile unor clase de baza, deja definite. Derivarea are ca obiectiv reutilizarea soft-ului, prin folosirea uneor functii deja scrise pentru clasele existente si eliminarea redundantei descrierilor, in cazul claselor care au elemente comune, functii sau date. Acest concept este prezentat in detaliu in moulul 3. Polimorfismul Termenul polimorfism se refer la comportamente alternative ntre clase derivate nrudite. n cazul n care mai multe clase mostenesc atributele si comportamentele unei clase de baz, pot apare situatii n care comportamentul unei clase derivate ar trebui s fie diferit de cel al clasei de baz sau de cel al clasei derivate de tip frate (de pe acelasi nivel). Aceasta nseamn c un mesaj poate avea efecte diferite n functie de clasa obiectului care primeste mesajul. De exemplu sa considerm trei clase: clasa de baz Fisier si clasele derivate FisierASCII si FisierBitmap care mostenesc toate atributele si comportamentele clasei Fisier cu exceptia comportamentului Tiprire. Un mesaj care va activa comportamentul Tiprire al unui obiect al clasei Fisier poate determina afisarea atributelor mrime fisier, tip fisier si data/ora crerii/ultimei modificri a fisierului. Acelasi mesaj trimis unui obiect al clasei FisierASCII va determina afisarea textului din fisier, n timp ce dac va fi trimis unui obiect al clasei FisierBitmap va determina executia unui program de afisare grafic.

1.3

Elemente introductive ale programrii orientate pe obiecte n limbajul C++

Limbajul C++ este unul dintre cele mai utilizate limbaje de programare orientate pe obiecte; compilatoare, biblioteci i instrumente de dezvoltare a programelor C++ sunt disponibile att pentru calculatoare personale ct i pentru cele mai dezvoltate sisteme i staii de lucru. Limbajul C++ este o versiune extins a limbajului C elaborata de ctre B. Stroustrup n anul 1980 n laboratoarele Bell din Murray Hill, New Jersey. Extensiile dezvoltate de Stroustrup pentru limbajul C++ permit programarea orientat pe obiecte, pstrnd eficiena, flexibilitatea i concepia de baz a limbajului C. Numele iniial a fost C cu clase, numele de C++ fiindu-i atribuit n anul 1983. Scopul pentru care a fost creat C++ este acelai cu scopul pentru care este abordat n general programarea orientat pe obiecte: dezvoltarea i administrarea programelor foarte mari. Chiar dac superioritatea limbajului C++ este evident n cazul dezvoltrii programelor foarte mari, nu exist limitri n a fi folosit n orice fel de aplicaie, deoarece C++ este un limbaj tot att de eficient ca i limbajul C.

De la apariia sa, C++ a trecut prin trei revizii, n 1985, 1989 i ultima, prilejuit de definirea standardului ANSI pentru acest limbaj. O prim versiune a standardului a fost publicat n anul 1994, iar urmtoarea versiune este nc n lucru. n general, limbajul C++ prevede mai multe faciliti i mai puine restricii dect limbajul C, astfel nct majoritatea construciilor din C sunt legale i au aceeai semnificaie i n C++. n acest capitol sunt prezentate unitar i concis conceptele de baz n programarea C++, att cele care sunt preluate din limbajul C ct i cele nou introduse. Multe dintre ele sunt reluate i dezvoltate pe parcursul seciunilor urmtoare. Operatii de intrare/iesire. Stream-uri Cel mai scurt program C++ este:
main(){ }

Acesta definete o funcie numit main (), care nu primete nici un argument, nu execut nimic i nu returneaz nici o valoare. Dac se dorete ca programul s scrie un mesaj la consol (de tipul Primul program in C++!), pe lng utilizarea funciei obinuite din limbajul C (funcia printf()), n C++ se poate utiliza i o funcie de scriere la ieirea standard (cout). Aceast funcie este funcia operator de scriere << care este definit n fiierul antet iostream.h. Un astfel de program este urmtorul:
#include <iostream.h> void main(){ cout << Primul program in C++!<< endl; }

Prima operaie de scriere afieaz la consol irul, Primul program in C++!, iar urmtoarea (endl) introduce caracterul de linie nou. Operaia de citire de la tastatur poate fi realizat n C++ printr-o instruciune care folosete funcia operator de citire >>. De exemplu, n instruciunile care urmeaz se citete de la tastatur un numr ntreg:
int i; cin >> i;

Desigur, funcia scanf() de citire de la tastatur din limbajul C poate fi n continuare folosit, dar pe parcursul exemplificrilor se vor folosi mai mult aceste funcii C++ de citire i de scriere la consol. Programul P1.1 prezint un exemplu de utilizare a operatiilor de intrare/iesire n limbajul C++.
// fisierul sursa P1_1.cpp #include <iostream.h> void main() {

int a, b; float m; cout << "\n cin >> a; cout << "\n cin >> b; m = (float) cout << "\n }

Introduceti un numar intreg a = "; Introduceti un numar intreg b = "; (a+b)/2; Media aritmetica este " << m;

Programul P1.1 citeste dou numere ntregi, a si b si calculeaz media lor aritmetic, m, pe care o afiseaz pe ecran. Citirea celor dou numere se poate realiza si cu o singur operatie de citire asa cum se observ n programul P1.1_v2
// fisierul sursa P1_1_v2.cpp #include <iostream.h> void main() { int a, b; float m; cout << "\n Introduceti doua numere intregi: "; cin >> a >> b; m = (float) (a+b)/2; cout << "\n Media aritmetica este " << m; }

Sintaxa operatiei de intrare (citire):


cin >> var_1 >> var_2 >> >> var_n;

Se citesc de la dispozitivul de intrare valorile variabilelor var_1, var_2, , var_n. Sintaxa operatiei de iesire (scriere, afisare):
cout << expr_1 << expr_2 << << expr_p;

Se afiseaz pe dispozitivul de iesire valorile expresiilor expr_1, expr_2, , expr_p. Expresiile pot fi, de exemplu, expresii aritmetice, variabile sau pot contine text, care este marcat ntre ghilimele (). Textul poate contine secvente de tipul \n (trecere la linie nou), \t (afisarea se face la dreapta n pozitia dat de tab), \a (avertizare sonor) etc.

1.4 Tipuri de date


n C++ sunt definite urmtoarele tipuri fundamentale: Tipuri de ntreg, pentru definirea numerelor ntregi de diferite dimensiuni (ca numr de octei ocupai n memorie): 9

short int 2 octei int 2 sau 4 octei long 4 sau 8 octei Tipuri de numere flotante, pentru definirea numerelor reale (reprezentate ca numere cu virgul flotant): float 4 octei double 8 octei long double 12 sau 16 octei Aceste tipuri sunt denumite mpreun tipuri aritmetice. Pentru tipurile ntreg, exist variante de declaraie cu semn (signed) i fr semn (unsigned). Tipul caracter, pentru definirea caracterelor char 1 octet Tipul void specific o mulime vid de valori. Nu se poate declara un obiect cu acest tip, dar acest tip poate fi utilizat n conversii de pointeri i ca tip de returnare al unei funcii Pe langa tipurile de date fundamentale enumerate, se pot defini conceptual un numr infinit de tipuri derivate pornind de la tipurile fundamentale. Tipurile derivate sunt: tablouri de obiecte, pointeri la obiecte, referine, funcii, constante simbolice, clase, structuri, uniuni, pointeri la membrii claselor. n continuare se vor prezenta primele cinci tipuri derivate, iar celelate vor fi introduse pe parcursul seciuniunilor urmtoare. Ca terminologie, clasele (mpreun cu structurile i uniunile) sunt denumite tipuri definite de utilizator (user-defined types), iar celelate tipuri sunt denumite tipuri predefinite (built-in types). Tablouri de obiecte Un tablou (array) de obiecte poate fi construit din obiecte dintr-un tip fundamental (cu excepia tipului void), din pointeri, din enumeraii sau din alte tablouri. n traducere, pentru array se mai ntlnesc termenii vector i matrice. n acest text sunt folosii termenii tablou (pentru array multidimensional) i vector (pentru array unidimensional). Declaraia: T D[expresie] introduce un tablou de obiecte de tipul T, cu numele D i cu un numr de elemente al tabloului dat de valoarea expresiei, care trebuie s fie de tip constant ntreg. Pentru valoarea N a acestei expresii, tabloul are N elemente, numerotate de la 0 la N-1. Un tablou bidimensional se poate construi printr-o declaraie de forma:

10

T D[dim1][dim2]; i reprezint dim1 tablouri unidimensionale, fiecare de dimensiune dim2. Elementele tabloului bidimensional se memoreaz cu valori succesive pentru indicele din dreapta astfel:
D[0][0], D[0][1], D[0][dim2-1], D[1][0], D[1][1], D[1][dim2-1],. D[dim1-1][0], D[dim1-1][1], D[dim1-1][dim2-1].

ntr-un mod asemntor se pot construi tablouri multidimensionale, cu o limitare a numrului de dimensiuni care depinde de implementare. Pointeri Pentru majoritatea tipurilor T, T* este un tip denumit pointer la T, adic o variabil de tipul T* memoreaz adresa unui obiect de tipul T. Operaia fundamental asupra unui pointer este operaia de derefereniere (dereferencing), adic accesarea obiectului a crui adres o reprezint pointerul respectiv. Operatorul de derefereniere este operatorul unar *. De exemplu: char c1 = a; // variabila c1 char* p1 = &c1; // p memoreaz adresa lui c1 char c2 = *p1; // dereferentiere, c2 = a; Operatorul & este operatorul adres, care se utilizeaz pentru a obine adresa unei variabile. Tipul void* este folosit pentru a indica adresa unui obiect de tip necunoscut. Asupra pointerilor sunt admise unele operaii aritmetice. De exemplu, se consider un vector de caractere dintre care ultimul este caracterul 0 (se mai numete ir de caractere terminat cu nul). Pentru calculul numrului de caractere se pot folosi operaii cu pointeri astfel: int strlen(char* p){ int i = 0; while (*p++) i++; return i; } Funcia strlen() returneaz numrul de caractere ale irului, fr caracterul terminal 0, folosind operaia de incrementare a pointerului i operaia de derefereniere pentru a testa valoarea caracterului. O alt implementare posibil a funciei este urmtoarea: int strlen(char* p){ char* q = p; while (*q++); return q-p-1; } n C++, ca i n limbajul C, pointerii i tablourile sunt puternic corelate. Un nume al unui tablou poate fi folosit ca un pointer la primul element al tabloului. De exemplu, se poate scrie:
char alpha[] = abcdef;

11

char* p = alpha; char* q = &alpha[0]; // p = q

Rezultatul aplicrii operatorilor aritmetici +, -, ++, -- asupra pointerilor depinde de tipul obiectului indicat. Atunci cnd se aplic un operator aritmetic unui pointer p de tip T*, se consider c p indic un element al unui tablou de obiecte de tip T; p+1 va indica urmtorul element al tabloului, iar p-1 va indica elementul precedent al tabloului. Acest lucru nseamn c valoarea lui p+1 este cu sizeof(T) octei mai mare dect valoarea lui p. Referine O referin (reference) este un nume alternativ al unui obiect. Utilizarea principal a referinelor se face pentru specificarea argumentelor i a valorilor returnate de funcii, n general, i pentru suprancrcarea operatorilor n special. Notaia X& nseamn referin la un obiect de tipul X. De exemplu: int i = 1; int& r = i; // r i i se refer la aceeai entitate int x = r; // x = 1 r++; // i = 2; Implementarea obinuit a unei referine se face printr-un pointer constant care este derefereniat de fiecare dat cnd este utilizat. Aa cum se poate observa, pentru definirea unei referine se folosete operatorul adres &, dar difer tipul construciei n care este folosit. De exemplu:
int a = 5; int* pi = &a; // & calculeaz adresa; // pi este adresa lui a int& r = a; // & introduce o referinta; // r este o referin (alt nume) pt. a

O referin este utilizat ca argument pentru o funcie care poate s modifice valoarea acestui argument. De exemplu: void incr(int& x) {x++;} void f(){ int i = 1; incr(i); // i = 2; } O alt utilizare important a referinelor este pentru definirea funciilor care pot fi folosite att ca membru drept ct i ca membru stng al unei expresii de asignare. De exemplu:
#include <iostream.h> int& fr(int v[], int i){ return v[i]; } void main(){

12

int x[] = {1,2,3,4}; fr(x,2) = 7; cout <<fr(x,0)<<fr(x,1)<<fr(x,2)<< fr(x,3)<<endl; }

La execuia acestui program se obine mesajul: 1 2 7 4 Deoarece valoarea returnat de funcie este referina (numele) unui element al vectorului, acesta poate fi modificat prin folosirea funciei fr() ca membru stng al egalitii. Funcii Funciile sunt tipuri derivate i reprezint una din cele mai importante caracteristici ale limbajelor C i C++. Forma general de definire a unei funcii este:
tip_returnat nume_func(tip1 arg1,tip2 arg2,,tipn argn) { //corpul functiei }

Funcia cu numele nume_func returneaz o valoare de tip tip_returnat i are un numr n de argumente formale declarate ca tip i nume n lista de argumente formale. Argumentele formale din declaraia unei funcii se mai numesc i parametrii funciei. Dac o funcie nu are argumente, atunci lista din parantezele rotunde este vid. Notaia din C: f(void) este redundant. O funcie este un nume global, dac nu este declarat de tip static. O funcie declarat static are domeniul de vizibilitate restrns la fiierul n care a fost definit. Corpul funciei este propriu ei i nu poate fi accesat din afara acesteia (nici printr-o instruciune goto). Corpul unei funcii este o instruciune compus, adic o succesiune de instruciuni i declaraii incluse ntre acolade. n corpul funciei se pot defini variabile, care sunt locale i se memoreaz n segmentul de stiv al programului. Dac nu sunt declarate static, variabilele locale se creaz la fiecare apel al funciei i se distrug atunci cnd este prsit blocul n care au fost definite. Nu se pot defini funcii n interiorul unei funcii. Argumentele formale ale unei funcii sunt considerate variabile locale ale funciei, ca orice alt variabil definit n funcia respectiv. Dac o funcie nu are de returnat nici o valoare, atunci tip_returnat din declaraia funciei este tipul void i nu este necesar o instruciune de returnare (return) n funcie. n toate celelalte cazuri, n corpul funciei trebuie s fie prevzut returnarea unei variabile de tipul tip_returnat, folosind instruciunea return. Dac n definiie nu este prevzut un tip_returnat, se consider implicit returnarea unei valori de tip ntreg. Prototipurile funciilor. Pentru apelul unei funcii este necesar cunoaterea definiiei sau a prototipului acesteia. Prototipul unei funcii este de forma:
tip_returnat nume_func(tip1 arg1,., tipn argn);

Numele argumentelor formale sunt opionale n prototipul unei funcii. Prototipurile permit compilatorului s verifice tipurile argumentelor de apel i s semnaleze eroare la conversii

13

ilegale. Spre deosebire de limbajul C, unde este admis i simpla declaraie a numelui funciei (fr tipurile argumentelor de apel), utilizarea prototipurilor este obligatorie n C++. De exemplu:
double f2(int, double); // prototip functie f2 double f3(int a, double f){ // definitie functie f3 /*..*/ double t = f/a; return t; } void fp(){ double r1 = f1(7, 8.9); // eroare, // identificator nedeclarat double r2 = f2(7, 8.9); // corect, fol. prototipul char str[] = "abcde"; double r3 = f3(7, str); // eroare de tip argument } double f1(int a, double f) { /*..*/ double t = a + f; return t; } double f2(int a, double f) { // definiie funcie f2() /*...*/ double t = a*f; return t; }

La compilare apare o eroare datorit apelului funciei f1(), care nu este definit, nici declarat prin prototip n domeniul funciei apelante fp() i o eroare datorat apelului funciei f3() cu un argument (argumentul al doilea) care nu poate fi convertit la tipul argumentului formal. Transferul argumentelor funciilor. La apelul unei funcii, argumentele de apel (se mai numesc i argumente reale sau efective) iniializeaz argumentele formale din declaraia funciei, n ordinea din declaraie. Argumentele unei funcii se pot transfera n dou moduri: apelul prin valoare i apelul prin referin. n apelul prin valoare se copiaz valoarea argumentului real n argumentul formal corespunztor al funciei. n acest caz, modificrile efectuate asupra argumentului funciei nu modific argumentul real. n apelul prin referin este accesat direct variabila din argumentul real transmis funciei, care poate fi deci modificat. Ca exemplificare, se definete o funcie swap() care realizeaz intershimbul ntre valorile a dou variabile. Dac nu se folosesc referine, argumentele de apel ale funciei trebuie s fie pointeri la variabilele respective. Pointerii, ca argumente de apel, nu vor fi modificai, dar variabilele indicate de acetia pot fi modificate. Funcia swap() cu argumente pointeri arat astfel: void swap(int* x, int* y){ int t;

14

t = *x; // dereferentiere *x = *y; *y = t; } Aceeai funcie swap(), folosind argumente de tip referin, arat astfel: void swap(int& x, int& y){ int t; t = x; x = y; y = t; }

Se poate observa perfecta simetrie ntre cele dou implementri i c, n mod evident, referina folosete adresa variabilei pentru a o putea modifica (deci un pointer). Dar, n cazul referinelor, pointerul i defererenierea sunt ascunse, programatorul nu trebuie s le prevad explicit, programul rezultat este mai concis i mai clar. Referinele sunt deosebit de utile n apelul funciilor ale cror argumente sunt obiecte de dimensiuni mari i copierea lor n argumentele formale (plasate n segmentul de stiv al programului) ar fi foarte ineficient. Argumente implicite ale funciilor. Se ntmpl frecvent ca o funcie s aib un numr mai mare de argumente dect sunt necesare n cazurile simple dar frecvente de apel. Dac nu este necesar s fie transmis ntotdeauna valoarea real a unui argument i acesta poate lua, de cele mai multe ori, o valoare implicit, atunci n declaraia funciei se prevede o expresie de iniializare a acestui argument, iar din apel poate s lipseasc valoarea argumentului corespunztor. De exemplu, o funcie pentru stabilirea datei calendaristice, care prevede valori implicite pentru argumentelelunaian:
struct data{ int zi; int luna; int an; } g_data; void setdata(int zi, int luna=9, int an =1999){ g_data.zi = zi; g_data.luna = luna; g_data.an = an; } void main(){ setdata(15); // 15 9 1999 setdata(21,7); // 21 7 1999 setdata(20,1,2000); // 21 1 2000 }

Numai argumentele de la sfritul listei pot fi argumente implicite. De exemplu, este eronat urmtoarea declaraie:

15

void setdata(int zi, int luna=9, int an); // eroare

Constante simbolice O constant simbolic (sau constant cu nume) este un nume a crui valoare nu poate fi modificat n cursul programului. n C++ exist trei modaliti de a defini constante simbolice: Orice valoare, de orice tip care poate primi un nume, poate fi folosit ca o constant simbolic prin adugarea cuvntului-cheie const n declaraia acesteia. Orice nume de funcie sau de tablou este o constant simbolic. O enumeraie definete o mulime de constante ntregi. De exemplu, urmtoarele declaraii introduc constante simbolice prin folosirea cuvntului-cheie const:
const int val = 100; const double d[] = {1.2, 2.8, 9.5};

Deoarece constantele nu pot fi modificate, ele trebuie s fie iniializate n declaraie. ncercarea de modificare ulterioar este detectat ca eroare n timpul compilrii:
val++; // eroare d = 200; // eroare

Cuvntul-cheie const modific tipul obiectului, restricionnd modul n care acesta poate fi folosit. Un aspect interesant i intens folosit n programare, este acela de a declara pointeri la constante. Atunci cnd se folosete un pointer, sunt implicate dou obiecte: pointerul nsui i obiectul ctre care indic pointerul. Prin prefixarea declaraiei unui pointer cu cuvntul const, obiectul indicat este fcut constant, nu pointerul nsui. De exemplu:
const char* pc = abcd;// pc este pointer la o constant pc[2] = m; // eroare, nu se poate modifica // obiectul constant pc = ghij; // corect, este modificat // valoarea pointerului pc++; // corect Pentru ca pointerul nsui s fie constant, se folosete operatorul *const: char *const cp = abcd;// cp este pointer constant; cp[2] = m; // corect, modifica valoarea cp++; // eroare, pointer constant

16

Posibilitatea de declarare a pointerilor la constante este folosit n special pentru transmiterea argumentelor funciilor. Prin declararea unui argument de tip pointer la constant, este interzis modificarea de ctre funcie a obiectului indicat. De exemplu:
char* strcpy(char* d, const char* s);

n aceast funcie irul s nu poate fi modificat. n mod asemntor, specificatorul const care nsoete un argument tip referin la apelul unei funcii, mpiedic modificarea acestuia de ctre funcia respectiv.

1.5 Operatori specifici C++


Majoritatea operatorilor C++ sunt preluai din limbajul C, cu aceeai sintax i reguli de operare. n plus fa de operatorii C, n C++ mai sunt introdui urmtorii operatori: operatorul de rezoluie (::) operatorii de alocare-eliberare dinamic a memoriei new i delete. Operatorul de rezoluie Operatorul de rezoluie (::) este folosit pentru modificarea domeniului de vizibilitate al unui nume. Pentru acest operator (scope resolution operator), n traduceri se mai ntlnesc termenii de operator de domeniu sau operator de acces. Operatorul de rezoluie permite folosirea unui identificator ntr-un bloc n care el nu este vizibil. Dac operatorul de rezoluie nu este precedat de nici un nume de clas, atunci este accesat numele global care urmeaz acestui operator. De exemplu: int g = 10; int f(){ int g = 20; // return ::g; } void main(){ cout << f() << endl; // afiseaza 10 } n acest exemplu operatorul de rezoluie a fost folosit pentru a accesa variabila global g, ascuns de variabila local cu acelai nume din funcie. Deoarece utilizarea cea mai extins a operatorului de rezoluie este legat de utilizarea claselor, el va fi reluat pe parcursul seciunilor urmtoare. Operatorii new i delete n limbajul C se pot aloca dinamic zone n memoria liber (heap) folosind funcii de bibliotec (de exemplu, malloc(), calloc(), realloc()) i se pot elibera folosind funcia free(). La aceste posibiliti, care se pstreaz n continuare n C++, se adaug operatorii de alocare i eliberare dinamic a memoriei, new i delete. Aceti operatori unari prezint avantaje substaniale fa de funciile de alocare din C i de aceea sunt n mod evident preferai n programele scrise n C++. 17

Pentru alocarea unei singure date (obiect), operatorul new are urmtoarea form general:
tip_data* p = new tip_data(initializare); unde tip_data este un tip de date predefinit sau definit de utilizator (clas), p este pointerul (adresa de nceput) a zonei alocate n memoria liber, returnat la execuia operatorului new, iar initializare este o expresie care depinde de tipul datei i permite iniializarea zonei de memorie alocate. Dac alocarea nu este posibil, pointerul returnat este NULL.

Forma de utilizare a operatorului new pentru alocarea unui vector de date (tablou unidimensional) de dimensiune dim, este urmtoarea:
tip_data* p = new tip_data[dim];

La alocarea unui vector nu se poate transmite o expresie de iniializare a zonei de memorie alocat. Operatorul delete elibereaz o zon din memoria heap. El poate avea una din urmtoarele forme:
delete p; delete []p;

Prima form se utilizeaz pentru eliberarea unei zone de memorie ocupat de o singur dat (obiect), nu de un vector. Pointerul p trebuie s fie un pointer la o zon de memorie alocat anterior printr-un operator new. Operatorul delete trebuie s fie folosit doar cu un pointer valid, alocat numai cu new i care nu a fost modificat sau nu a mai fost eliberat zona de memorie mai nainte (cu un alt operator delete sau prin apelul unei funcii free()). Folosirea operatorului delete cu un pointer invalid este o operaie cu rezultat nedefinit, cel mai adesea producnd erori de execuie grave. Cea de-a doua form a operatorului delete[] se folosete pentru eliberarea unei zone de memorie ocupat de un vector de date. Pentru tipurile de date predefinite ale limbajului, se poate folosi i prima form pentru eliberarea unui vector, dar, n cazul obiectelor de tipuri definite de utilizator, acest lucru nu mai este valabil. Aceast situaie va fi detaliat n seciunea urmtoare. Cteva exemple de utilizare a operatorilor new i delete:
int *pi = new int(3); // alocare int i iniializare double *pd = new double; // alocare double neinitializat char *pc1 = new char[12]; // vector de 12 caractere char *pc2 = new char[20]; // vector de 20 caractere delete pi; deletepd delete pc1; //corect, char e tip predefinit delete []pc2; // corect, elibereaza vector

n legtur cu cele dou metode de alocare dinamic, prin operatorii new-delete i prin funciile de bibliotec malloc-free, fr s fie o regul precis, se recomand evitarea combinrii lor, deoarece nu exist garania compatibilitii ntre ele.

18

Teste de autocontrol 1.1 Care din afirmatiile urmtoare sunt adevrate si care sunt false? Justificati rspunsul n cazul afirmatiilor care sunt false. (a) Toate variabilele trebuie declarate nainte de a fi utilizate. (b) Un program C++ care afiseaz trei linii pe ecran trebuie s contin 3 instructiuni de afisare cout. (c) Comentariile dintr-un program C++ determin afisarea textului aflat dup // pe ecran la executia programului. (d) n limbajul C++ toate variabilele locale trebuie declarate la nceputul functiei de care apartin.
1.2 Descriei pe scurt diferena dintre funciile care returneaz valoare i cele care returneaz referin.

1.3 Presupunnd c variabilele a si b au valorile 3, respectiv 4, s se precizeze ce anume se va afisa pe ecran ca urmare a executiei urmtoarelor instructiuni: (a) cout << a+b; (b) cout << a=; (c) cout << a= << a; (d) cin >> a >> b; (e) // cout << a+b= << a+b; (f) cout << \n\t; (g) cout << a*b << = << b*a; (h) cout << a; (i) s = a+b; 1.4 Care sunt principalele diferente ntre programarea orientat pe obiecte si programarea procedural? 1.5 Care sunt principalele mecanisme de abstractizare? 1.6 Cum se numesc TDA-urile n programarea orientat pe obiecte? 1.7 Care sunt elementele principale ale unui TDA? 1.8 De ce sunt considerate a fi inteligente obiectele? 1.9 Dati exemple de clase si obiecte ale acestora. 1.10 Obiectele pot comunica ntre ele? Justificati rspunsul. 1.11 Care din urmtoarele concepte sunt concepte de baz ale programrii orientate pe obiecte? (a) obiect (b) mostenire (c) metod 19

(d) ncapsulare (e) modul (f) procedur (g) polimorfism (h) stream (i) cout 1.12 Definiti tipul de date abstract (TDA). 1.13 Definiti urmtoarele tipuri de date abstracte: (a) LISTA (b) COADA Se vor specifica: structura de date, operatorii de baz si cei suplimentari si axiomele n mod similar cu definitia TDA STIVA. 1.14 S se scrie declaraiile pentru urmtoarele tipuri de variabile: pointer la un caracter, un vector de 10 valori ntregi, pointer la un vector de 10 valori ntregi, un pointer la un pointer la un caracter. 1. 15 S se scrie un program care tiprete dimensiunea tipurilor fundamentale de date. Se va folosi operatorul sizeof.

20

Cap.2 Clase si Obiecte

Obiective Definirea notiunilor de clas, obiect, atribut, metod; ntelegerea notiunilor de instant, instantiere, constructor, destructor; nsusirea modului n care se realizeaz accesarea membrilor unei clase; Definirea constructorilor unei clase (implicit, cu parametri, de copiere, de convertire si atribuire); Definirea destructorului unei clase; Definirea si utilizarea claselor compuse si a obiectelor compuse; Pointerului this; Particularitti ale limbajului C++ (functii friend, functii inline, membrii statici, tablouri de obiecte, pointeri la metode)

Un tip de date ntr-un limbaj de programare este o reprezentare a unui concept. De exemplu, tipul float din C++, mpreun cu operaiile definite asupra acestuia (+, -, *, etc.) reprezint o versiune a conceptului matematic de numere reale. Pentru alte concepte, care nu au o reprezentare direct prin tipurile predefinite ale limbajului, se pot defini noi tipuri de date care s specifice aceste concepte. O clas este un tip de date definit de utilizator. O declarare a unei clase definete un tip nou care reunete date i funcii. Acest tip nou poate fi folosit pentru a declara obiecte de acest tip, deci un obiect este un exemplar (o instan) a unei clase. Definitie. O clas este implementarea unui TDA. Ea defineste atribute si metode care implementeaz structuri de date si operatii ale TDA-ului. Definitie. Un obiect este o instant a unei clase. El este unic identificat de numele lui si defineste o stare care este reprezentat de valorile atributelor, la un moment dat. Starea unui obiect se schimb n raport cu metodele care i sunt aplicate. Definitie. Comportamentul unui obiect este definit de multimea metodelor care i pot fi aplicate. Definitie. O metod este o functie asociat unei clase. Un obiect apeleaz (invoc) o metod drept reactie la primirea unui mesaj.

21

Primul pas pentru gruparea datelor si metodelor de prelucrare, l-au reprezentat stucturile; ele permiteau declararea unor ansambluri eterogene de date ce erau manipulate unitar. Consideram structura angajat care curinde urmatoarele date: nume, varsta, salariul.
struct angajat { char nume[20]; int varsta; float salariul; }

Declaratii de variabile: angajat a1; // o structura de tip angajat p1 angajat tablouangajat[10]; // un tablou cu zece elemente de tip angajat angajat *a2; // un pointer catre o structura de tip angajat a2 angajat &a3=a1; // o referinta catre o structura de tip angajat a3 Membrii unei structuri sau ai unei clase sunt accesati cu ajutorul operatorilor de acces: operatorul ., respectiv, operatorul ->. 9 Operatorul . acceseaz o structur sau un membru al unei clase prin numele variabilei pentru obiect sau printr-o referint la obiect. 9 Operatorul -> acceseaz un membru al unei structuri sau clase printr-un pointer la obiect (a2 = &a1) Programul P2.1 implementeaz tipul de date angajat cu ajutorul unei unei structuri C. Functia afisare( ) are rolul de a afisa datele unui angajat. Programul realizeaz constructia tipului de data abstract angajat prinn definirea unei structuri , setarea valorilor pentru cmpurile structurilor (membrii structurilor) si afisarea acestora.
// fisierul sursa P2_1.c #include <stdio.h> #include <conio.h> #include <string.h> // definitia structurii angajat struct angajat { char num[20]; int varsta; float salariul; } a1 = {"Pop", 28,1530}; // definitia functiei de afisare a datelor unei angajat void afisare(struct angajat a) { printf("\n Nume \t Varsta \t Salariul"); printf("%s\t%i\t%d",a.nume,a.varsta,a.salariul); } void main()

22

{ struct angajat a2; strcpy(a2.nume, "Ion"); a2.varsta = a1.varsta; a2.salariul = 1600; printf("\n Angajat nr 1 \n"); afisare(a1); printf("\n Angajat nr 2 \n"); afisare(a2); getch(); }

2.1 Definirea unei clase


Clasele permit programatorului s modeleze obiectele care au atribute (reprezentate de datele membru) si comportament (reprezentat de functiile membru, numite si metode). Practic, o clas ncapsuleaz o multime de valori si o multime de operatii. Sintaxa definirii unei clase:
class <nume_clas> { private: // membrii privati public: // membrii publici protected: // membrii protejati };

Cuvntul-cheie class introduce declaraia clasei (a tipului de date) cu numele nume_clasa. Dac este urmat de corpul clasei (cuprins ntre acolade), aceast declaraie este totodat o definiie. Dac declaraia conine numai cuvntul-cheie class i numele clasei, atunci aceasta este doar o declaraie de nume de clas. Corpul clasei conine definiii de date membre ale clasei i definiii sau declaraii de funcii membre ale clasei, desprite printr-unul sau mai muli specificatori de acces. Un specificator_acces poate fi unul din cuvintele-cheie din C++:
public private protected

Specificatorii private i protected asigur o protecie de acces la datele sau funciile membre ale clasei respective, iar specificatorul public permite accesul la acestea i din afara clasei. Efectul unui specificator de acces dureaz pn la urmtorul specificator de acces. Implicit, dac nu se declar nici un specificator de acces, datele sau funciile membre sunt de tip private. De aceea, toate datele sau funciile declarate de la nceputul blocului clasei pn la primul specificator de acces sunt de tip private. ntr-o declaraie de clas se poate schimba specificatorul de acces ori de cte ori se dorete: unele declaraii sunt trecute public, dup care se poate reveni la declaraii private, etc. Diferena ntre tipurile de acces private i protected const n modul n care sunt motenite drepturile de acces n clase derivate.

23

Definirea obiectelor Dup definirea unei clase C, aceasta poate fi utilizat ca tip n definirea obiectelor, tablourilor de obiecte si a pointerilor, astfel:
C C C C o1, // obiect de tip C Tablou[20], // tablou de obiecte de tip C *ptrC, // pointer la un obiect de tip C &tC = o1; // referinta la un obiect de tip C

Numele unei clase devine specificator de tip si se pot defini, teoretic, o infinitate de obiecte ale clasei. Accesarea membrilor unei clase Membrii publici ai unei clase pot fi accesati cu ajutorul operatorilor . si ->, dup cum obiectul de care apartin este desemnat prin nume sau printr-un pointer. Variabilele membru private nu pot fi accesate dect n cadrul metodelor clasei respective. Programul 2.2 implementeaz tipul de date angajat sub forma unei clase C++. Clasa angajat cuprinde datele membre: varsta, salariul, nume si functii membre: init() pentru initializarea datelor membre, functiia membra de acces spune_varsta(), functia afisare() pentru afisarea datelor membre.
class angajat { private: int varsta; protected float salariul; public: char nume[20]; void init ( char n[]=Anonim, int v=0, float s=0) {strcpy(nume,n); varsta=v; salariul=s; } int spune_varsta() { return varsta}; void afisare() {cout<<nume: <<nume<<endl; cout<<varsta: <<varsta<<endl; cout<<salariul: <<salariul; } } void main() { angajat a; a.init(); // apelul functiei membre init() pentru obiectul a

24

cout<<a.spune_varsta();//apelul functiei membre soune_varsta() pentru obiectul a }

Dupa rolul pe care il joaca in cadrul clasei, functiile membre ale unei clase se impart in patru categorii: 9 constructori, responsabili cu crearea obiectelor; 9 destructor, reponsabil cu distrugere obiectelor si eliberarea memoriei ocupate de acesta; 9 functii de acces, care mediaza legatura obiectului cu exteriorul; 9 metode, functii care introduc operatiile si prelucraile specifice obiectului.

2.2 Constructori si destructor


Utilizarea unor funcii membre ale unei clase, aa cum este funcia init() din clasa angajat, pentru iniializarea obiectelor este neelegant i permite strecurarea unor erori de programare. Deoarece nu exist nici o constrngere din partea limbajului ca un obiect s fie iniializat (de exemplu, nu apare nici o eroare de compilare dac nu este apelat funcia init() pentru un obiect din clasa angajat), programatorul poate s uite s apeleze funcia de iniializare sau s o apeleze de mai multe ori. n cazul simplu al clasei prezentate ca exemplu pn acum, acest lucru poate produce doar erori care se evideniaz uor. n schimb, pentru alte clase, erorile de iniializare pot fi dezastruoase sau mai greu de identificat. Din aceast cauz, limbajul C++ prevede o modalitate elegant i unitar pentru iniializarea obiectelor de tipuri definite de utilizator, prin intermediul unor funcii speciale numite funcii constructor (sau, mai scurt, constructori). Constroctorul este o functie membra speciala care este apelata automat atunci cand este creat un obiect. El poarta numele clasei si nu are nuci un tip la intoarcere, deoarece este apelat automat. Principalele motivatii pentru care se utilizeaza constructorii sunt: - complexiatatea structurii obiectelor date de de existenta variabilelor si functiilor, de existenta sectiunii privata, publica si protejata, face dificila initializarea directa a obictelor; - exista situatii in care doar unele date membre trebuie intializate, altele sunt incarcate in urma apelarii unor metode; - datele de obicei sunt declarate in sectiunea private si deci nu pot fi accesate din exterior ci prin intermediul metodelor. O clasa poate mentiona mai multi construcori, prin supraincarcare, folosirea unuia dintre ei la declararea unei variabile de clasa data, fiind dedusa in functie de numarul si tipul parametrilor de apel. Vom redefinii calsa angajat pentru care vom defini doi constructori asfel:
class angajat {int varsta; float salariul; char nume[20]; public: angajat() { strcpy(nume,Noname); varsta=0;salariul=0}

25

angajat(char *n, int v, float s) {strcpy(nume,n);varsta=v;salariul=s;} char *spune_nume(){return nume;} int spune_varsta(){return varsta;} }

Se observa ca in clasa angajat s-au definit doi constructori: - unul care nu are nici un parametru, angajat() care intilaizaza datele membre cu valori constante; constructorul fara parametrii se numeste constructor impicit; - al doilea constructor primeste trei parametri si are ca scop initializarea datelor membre din variabile elemntare; un asfel de constructor se numeste constructor cu argumente. Constructorii definiti mai sus se pot apela asfel: angajat a1 // aplelul constructorului implicit angajat a2(Pop,28,1200) //apelul constructorului cu argumente. Daca clasa nu mentioneaza nici un constructor, atunci se defineste automat de catre compilator un constructor care este utilizat pentru generarea de obiecte ale casei respective. In multe aplicatii este recomandat ca o clasa sa mentoineze mai multi constructori deoarece modurile de intializare a datelor membre sun diverse: - initializarea membrilor cu constante; - intializarea din datele elementare; - initializarea prin citire de la tastatura; - initializarea prin citire din fisier; - initializarea din datele unui obiect existent. Observatie. Pentru un obiect este selectat un singur constructor care este apleat o singura data. Programul 2.2 impementeaza clasa Complex care descrie un numar cpmplex. Clasa contine datele membre partea reala a numarului complex re, partea imaginara a numarului complex im. Sunt definiti trei constructori Complex(), Complex (double v), Complex ( double x, double y) precum si o functie membra care permite afisarea datelor unui numar complex.
#include <iostream.h> class Complex{ double re; double im; public: Complex(){cout << "Constructor fara argumente\n"; re = 0; im = 0; } Complex(double v){cout << "Constructor cu 1 arg\n"); re = v; im = v; } Complex(double x, double y){

26

cout << "Constructor cu 2 arg\n"; re = x; im = y; } void afisare() {cout<<partea reala<<re<<endl; Cout<<partea imaginara<<im; }; void main (){ Complex c1; Complex c2(2); Complex c3(3,5); }

La execuia funciei main(), sunt afisate urmtoarele mesaje:


Constructor fara argumente Constructor cu 1 arg Constructor cu 2 arg

n fiecare dintre aceste situaii a fost creat un obiect de tip Complex, c1, c2, c3 i de fiecare dat a fost apelat constructorul care are acelai numr i tip de argumente cu cele de apel. Dintre modurile de intializare amintite mai sus, un rol important o are intializarea unui obiect prin copierea datelor unui alt obiect de acelai tip. Aceast operaie este posibil prin intermediul unui constructor mai special al clasei, numit constructor de copiere. Forma general a constructorului de copiere al unei clase X este:
X::X(X& r){ } // initializare obiect folosind referina r

Constructorul primete ca argument o referin r la un obiect din clasa X i iniializeaz obiectul nou creat folosind datele coninute n obiectul referin r. Pentru crearea unui obiect printr-un constructor de copiere, argumentul transmis trebuie s fie o referin la un obiect din aceeai clas. De exemplu, pentru obiecte de tip Complex:
void main(){ Complex c1(2,3); // Constructor initializare Complex c2(c1); // Constructor copiere Complex c3 = c2; // Constructor copiere c3.afisare(); // afiseaza 2 3 }

La crearea primului obiect c1 este apelat constructorul de iniializare cu dou argumente al clasei Complex. Cel de-al doilea obiect c2 este creat prin apelul constructorului de copiere al clasei Complex, avnd ca argument referina la obiectul c1. Este posibil i declaraia de forma Complex c3 = c2; a unui obiect prin care se apeleaz, de asemenea, constructorul de copiere.

27

Constructorul de copiere poate fi definit de programator; dac nu este definit un constructor de copiere al clasei, compilatorul genereaz un constructor de copiere care copiaz datele membru cu membru din obiectul referin n obiectul nou creat. Aceast modalitate de copiere mai este denumit copie la nivel de bii (bitwise copy) sau copie membru cu membru. Pentru clasa Complex, constructorul de copiere generat implicit de compilator sau definit de programator arat astfel:
Complex(Complex &r) { cout << Constructor copiere\n; re = r.re; im = r.im; }

Importanta constructorului de copiere este subliniata de situatia in care datele membre ale unei clase sunt alocate dinamic. Constructorul de copiere generat implicit de compilator copiaz doar datele membre declarate n clas (membru cu membru) i nu tie s aloce date dinamice pentru obiectul nou creat. Folosind un astfel de constructor, se ajunge la situaia c dou obiecte, cel nou creat i obiectul referin, s conin pointeri cu aceeai valoare, care indic spre aceeai zon din memorie. O astfel se situaie este o surs puternic de erori de execuie subtile i greu de depistat. Exemplificam acest caz definind clasa angajat asfel:
class angajat {float salariul; public: char *nume; angajat(char *n,int s) {int nr=strlen(n); nume=new char[nr];// alocare dinamica pentru sirul nume strcpy(nume,n); salariul=s; } int spune_salariul(){return salariul;} } void main() { angajat a1(Popescu,35); angajat a2=a1; strcpy(a2.nume,Ion); cout<<a1.nume<< <<a1.salariul<<endl; cout<<a2.nume<< <<a2.salariul; }

Dupa rularea programului se va afisa:

Ion Ion

35 35

La construirea obiectului a2 s-a aplelat constructorul de copiere implicit care a copiat membrul nume al obiectului a1, dar nu a realizat alocarea dinamica. Asfel, obiectele a1 si a2 folosesc aceeasi zona de memorie alocata pentru campul nume. 28

Se observa asfel, necesiatea de a introduce in clasa amgajat un constructor de copiere care sa aloce explitit memorie , dupa care sa faca copierea continutului zonei de memorie a obiectului sursa la destinatie:
class angajat {float salariul; public: char *nume; angajat(char *n,int s) {int nr=strlen(n); nume=new char[nr]; strcpy(nume,n); salariul=s; } angajat(angajat &a) { int nr= strlen(a.nume) strcpy(nume,p.nume); varsra=a.varsta; } int spune_salariul(){return salariul;} } void main() { angajat a1(Popescu,35); angajat a2=a1; strcpy(a2.nume,Ion); cout<<a1.nume<< <<a1.salariul<<endl; cout<<a2.nume<< <<a2.salariul; } Rezultatul afisat prin rularea aceeluiasi program este: Popescu Ion

35 35

In concluzie, pentru un obiect cu un membru pointer spre o zona alocata dinamic progrmatorul va furniza un contructor de copiere care sa aloce memorie pentru noul obiect. Multe clase definite ntr-un program necesit o operaie care efectueza tergerea complet a obiectelor. O astfel de operaie este efectuat de o funcie membr a clasei, numit funcie destructor. Cand este declarat explicit in cadrul calsei, destructorul porta numele clasei, precedat de semnul ~ (de ex. ~angajat()). Destructorul, spre deosebire de constructor, este unic si nu are parametrii de apel. Dac programatorul nu defineste un destructor explicit, compilatorul C++ va genera unul implicit. Destructorul generat de compilator apeleaz destructorii pentru variabilele membru ale clasei. Membrii unei clase sunt ntotdeauna distrusi n ordinea invers celei n care au fost creati.

29

#include <iostream.h> class Complex { double re; double im; public: Complex(double x, double y){ cout << "Constructor cu 2 arg\n"; re = x; im = y; } ~Complex(){cout << "Destructor"; } Void main() { Complex z(3,4); }

Rezultatul afisat dupa rularea programului este:

Constructor cu 2 arg Destructor Se observa in programul anterior ca nu s-a realizat un apel explicit al destructorului. Acesta a fost apelat automat la sfarsitul programului. Destructorii sunt apelai implicit si in alte situaii, cum ar fi: atunci cnd un obiect local sau temporar iese din domeniul de definiie; la apelul operatorului delete, pentru obiectele alocate dinamic.

Pointerul this Fiecare obiect al unei clase are acces la propria adres prin intermediul unui pointer numit this. Acest pointer este utilizat n mod implicit pentru referirea att la membrii date ct si la functiile membru ale unui obiect. El poate fi utilizat si explicit cand se doreste folosirea adresei obiectului. Adresa obiectului, desi este transparenta pentru utilizator, exista memorata intr-un pointer numit this. Ca expemplul vom defini, in programul 2.3, o clasa care va contine o metoda ce utilizeza pointerul this.
class C { int x; public: C(int a){ x=a;} void afisare(){ cout<<this->x;} } void main() {C ob(7); ob.afisare; }

30

2.3 Tablouri de obiecte


n limbajul C++ implementarea tablourilor de obiecte se poate realiza cu conditia respectrii urmtoarei restrictii: clasa care include obiectul trebuie s contin un constructor implicit. Aceast restrictie se datoreaz imposibilittii de a furniza argumentele necesare constructorilor. Programul P2.4 defineste un tablou de obiecte de tip angajat. // fisierul sursa P24.cpp
class angajat { private: int varsta; float salariul; public: char nume[20]; angajat() {strcpy(nume,Anonim); varsta=0; salariul=0; } angajat( char n[]=Anonim, int v=0, float s=0) {strcpy(nume,n); varsta=v; salariul=s; } void seteaza_valori() {cout<<Numele: cin>>nume; cout>>salariul: cin>>salariul; cout<<varsta cin>>varsta; } void afisare() {cout<<nume: <<nume<<endl; cout<<varsta: <<varsta<<endl; cout<<salariul: <<salariul; } } void main() { angajat tab[20]; // tablou static sau automatic int i, n; cout<<"\n Cate obiecte doriti sa creati?"; cin>>n; // initializeaza n obiecte for (i=0;i<n;i++)

31

tab[i].seteaza_valori(); // afiseaza cele n obiecte for (i=0;i<n;i++) tab[i].afisare(); }

2.4 Funcii i clase friend


Este cunscut deja faptul ca aceesul la mebrii unei clase declarati in sectiunile private si protected este restrictionat. Acestea pot fi utilizate doar de functiile membre ale clasei. Accesul la membrii unei clase poate fi ingadiut, totusi si unor functii externe clasei sau apartinand altor clase. Astfel de functii se numesc functii prietene clasei respective (functii friend) si sunt declarate astfel cu ajutorul specificatorului friend. Functiile friend raman externe, nefiind legate de clasa si cu atat mai putin de un obiect anume. Pentru a avea acces la datele unui obiect, functia friend trebuie sa primeasca drept parametru de intrare referinta la obiectul respectiv. Declararea unei funcii f() de tip friend se realizeaza in interiorul clasei iar funcia nsi se definete n alt parte n program astfel:
class C{ //.. friend tip_returnat f(lista_argumente); }; .. tip_returnat f(lista_argumente){ // corpul functiei } Programul P2.5 prezint un exemplu de declarare si utilizare a unei functii friend a clasei Nr_Complex, modulul(), care calculeaz modulul unui numar complex.

// fisierul sursa P2_5.cpp #include <iostream.h> #include <math.h> class Complex { double re, im; public: Complex(double x, double y) { real = x; imag = y; } friend double modul(Complex &); };
double abs(Nr_Complex& z) { return sqrt(z.real*z.real+z.imag*z.imag); }

32

void main() { Complex Z=Complex(3,4); cout<<"\n Modulul lui numarului complex z este "<< modul(Z); }

Trebuie subliniat faptul ca utilizarea functiilor friend trebuie fcut cu mare grij ntruct acestea ncalc principiul ncapsulrii, respectiv, ascunderea informatiei, principiu fundamental n POO. Avantajul principal al declarrii unei functii friend este accesul rapid pe care aceasta l are direct la membrii privati ai clasei cu care este prieten.

2.5 Funcii inline


n programare aplelul functiilor poate produce un cost ridicat de execuie, datorit operaiilor necesare pentru rezervarea spaiului n stiv, pentru transferul argumentelor i returnarea unei valori. Pentru a reduce timpul de executii C++ pune la dispozitie functii inline. n general, o funcie declarat inline se schimb la compilare cu corpul ei, i se spune c apelul funciei se realizeaz prin expandare. n felul acesta se elimin operaiile suplimentare de apel i revenire din funcie. Dezavantajul funciilor inline este acela c produc creterea dimensiunilor programului compilat, de aceea se recomand a fi utilizate pentru funcii de dimensiuni mici (maximum 3-4 instruciuni). n plus, mai exist i unele restricii privind funciile inline: ele nu pot fi declarate funcii externe, deci nu pot fi utilizate dect n modulul de program n care au fost definite i nu pot conine instruciuni ciclice (while, for, do-while). n cazul claselor, functiile membru care sunt definite n interiorul clasei devin n mod implicit inline. n cazul n care le definim n afara clasei si vrem s fie inline trebuie s utilizm sintaxa declarrii unei functii inline. Sintaxa declarrii unei functii inline:
inline <tip> <nume_functie>([<lp>])

unde <tip> reprezint tipul ntors de functie, iar <lp> lista de parametri. Programul P2.6 prezint un exemplu de definire si utilizare a unei functii inline pentru calculul ariei unui patrat cu latura data, arie_patrat() .

// fisierul sursa P2_6.cpp #include <iostream.h> inline double arie_patrat(double a) { return a*a; }

33

void main() { double x; cout<<"\n Latura patratului:"; cin>>x; cout<<"\n Aria patratului este "<<arie_patrat(x); }

2.6 Date si functii statice


Implicit membrii de date sunt alocati pe obiecte. De exemplul fiecare angajat al unei firme are propiul sau nume, cnp etc. Exista, insa unele propietati care sunt impartite de catre toate obiectele unei clase, cum ar fi de exemplul, pentru clasa angajat, numarul total de angajati ai unei firme, salariul mediu al firmei etc. O varinata posibila ar fi stocarea acestor informatii intr-o variabila globala uzuala. De exemplu, am putea utiliza o variabila de tip intreg pentru a pastra numarul de obiecte angajat. Problema acestei solutii este ca variabilele globale sunt declarate in afara clasei; pentru putea fi aduse in interiorul calsei aceste variabile trebuie sa fie declarate de tip static. Datele statice nu se regasesc in fiecare set de valori ale clasei, ci intr-un singur exemplar, pentru toate obiectele clasei. Datele ce apar in toate obiectele se aloca de catre un constructor , dar cum un membru static nu face parte din nici un obiect, nu se aloca prin constructor. Asfel, la definirea clasei, o data statica nu se considera definita, ci doar declarata, urmand a avea o definitie externa clasei. Legatura cu declaratia din interiorul clasei se face prin operatorul de rezolutie :: precedat de numele clasei din care face parte precum si de tipul variabilei statice, asfel:
class angajat { //... Static int total_ang; //... } int angajat::total_ang=0;;

Variabila total_ang va fi unica pentru toti angajatii. Se poate observa ca o data cu definirea varbilei statice s-a realizat si intializarea acesteia. Functiile membre statice efectueaza prelucrari care nu sunt individualizate pe obiecte, ci prelucrari care au loc la nivelul clasei. Functiile statice nu apartin unui obiect anume si deci nu beneficiaza de referinta implicita a obiectului asociat (pointerul this). Daca functia membra statica opereaza pe o data nestatica a clasei, obiectul trebuie transmis explicit ca parametru , in timp ce cu datele membre statice , lucreaza in mod direct. Programul 2.7 redefineste clasa angajat, prin adaugarea datelor membre statice total_ang si total_b care vor retine numarul total de angajati si numarul total de barbati. Clasa va contine si trei functii membre statice care vor returna valorile retinute in cele doua date statice.

34

// fisierul sursa P2.7.cpp class angajat { private: int varsta; float salariul; char gen; static int total_ang; static int total_b; public: char nume[20]; angajat() {strcpy(nume,Anonim); varsta=0; salariul=0; total_ang++; } ~angajat(){total_ang--;} void seteaza_valori() {cout<<Numele: cin>>nume; cout>>salariul: cin>>salariul; cout<<varsta cin>>varsta; } static int spune_total_ang(){return total_ang;} static int spune_total_b(){return total_b;} static int numara_total_b(angajat *ang) {if(ang->gen==B) total_b++;} void afisare() {cout<<nume: <<nume<<endl; cout<<varsta: <<varsta<<endl; cout<<salariul: <<salariul; } } int angajat::total_ang=0; int angajat::total_b=0; void main() { angajat tab[20]; int i, n; cout<<"\n Cate obiecte doriti sa creati?"; cin>>n; for (i=0;i<n;i++) {tab[i].seteaza_valori(); angajat::numara_total_b(&tab[i]); }

35

cout<<firma are un total de angajati<<angajat::spune_total_ang; cout<<din care<<angajat::spune_total_b<< sunt barbati; }

In programul de mai sus se remarca faptul ca intretinerea variabilei total_ang cade in sarcina constructorilor si a destructorului, care incrementeaza sau decrementreaza acesta variabila. De asemenea, functia membra statica numara_total_b() primeste ca parametru referinta la un obiect de tip angajat pentru ca utilizeaza data membra gen care nu este statica. ntruct variabilele membru statice nu apartin unui obiect anume, ele pot fi accesate si prin prefixarea numelui clasei de operatorul ::.

36

Teste de autocontrol 2.1 Care este valoarea de adevr a afirmatiilor urmtoare: (a) O clas C++ si o structur C definesc acelasi concept, de TDA. (b) O clas C++ ncorporeaz att date ct si operatii asociate datelor, n timp ce o structur C defineste doar datele, operatiile nefiind legate direct de datele structurii. (c) O clas C++ ascunde datele n zona privat, n timp ce structura C permite accesul direct la datele sale neasigurnd consistenta acestora. (d) Structurile C nu pot fi afisate ca o singur entitate si nu pot fi comparate dect membru cu membru. 2.2 Care este numrul maxim de constructori si destructori care se pot defini ntr-o clas? Selectati rspunsul corect. (a) un constructor si un destructor (b) un constructor si mai multi destructori (c) 10 constructori si un destructor (d) o infinitate de constructori (teoretic) si un singur destructor 2.3 Considerm clasa Carte definit astfel:
class Carte { char* titlu; int an_aparitie; char* editura; char* ISBN; public: Carte(); Carte(char* T, int A, char* Ed, char* Nr); Carte(char* T, char* Ed); Carte(char* T, int A); ~Carte(); void afisare_date(); };

si obiectele ob1, ob2, ob3, ob4 definite astfel: Carte ob1=Carte(Compilatoare, 2000, Teora, 052-1432); Carte ob2=Carte(Limbajul Java, Editura Tehnica); Carte ob3=Carte(Programare logica, 2000); Carte ob4; (a) Explicati cum va diferentia compilatorul cei trei constructori cu parametri, lund drept exemplu obiectele ob1, ob2 si ob3. (b) Ce constructor va aplica compilatorul pentru crearea obiectului ob4? 2.4 Care este rolul functiilor inline? 2.5 Care este valoarea de adevr a urmtoarelor afirmatii? (a) Functiile friend se pot declara numai n sectiunea privat a clasei cu care sunt prietene. (b) Utilizarea functiilor friend ncalc principiul ncapsulrii, principiu fundamental n POO. (c) Avantajul principal al utilizrii functiilor friend este accesul rapid la membrii privati ai clasei cu care sunt prietene. (d) Relatia de prietenie este simetric si tranzitiv. 37

2.6 Explicati de ce este necesar definirea unui constructor implicit n cazul utilizrii tablourilor de obiecte n limbajul C++? 2.7 Care este valoarea de adevr a afirmatiilor urmtoare: (a) Membrii statici ai unei clase pot fi att variabile (date membru) ct si metode (functii membru). (b) Cuvntul cheie static desemneaz o proprietate a clasei si nu a unui obiect specific clasei. (c) Domeniul de valabilitate al membrilor statici este tot programul n care este definit clasa din care fac parte. (d) n limbajul C++ datele statice trebuie initializate o singur dat. 2.8 S se defineasc o clas Date pentru memorarea datei sub forma (zi, lun, an). Clasa va conine atia constructori ct sunt necesari pentru urmtoarele definiii de obiecte:
Date Date Date Date d1(15, 3, 99); // zi, lun, an d2(20, 4); // zi, lun, an curent d3(18); // zi, lun curent, an curent d4; // zi curent,lun curent, an curent

2.9 Construiti clasa student care contine datele membre: nume, prenume, nr_matricol, an, grupa. a). Introduceti in clasa cel putin doi constructori. b). Realizati un tablou de obiecte de tipul clasei student; c). Afisati numarul studentilor din grupa 201, utilizand date si functii membre statice; 2.10 Se consider urmtorul program n care o variabil global numbers memoreaz numrul obiectelor n via la un moment dat:
#include <iostream.h> int numbers = 0; class Item { public: Item(){ // constructorul creste cu 1 nr obiectelor numbers++; cout << "Nr. obiecte " << numbers << endl; } ~Item() { // destructorul descreste cu 1 nr ob. numbers--; cout << "Nr. obiecte " << numbers << endl; } }; void main() { Item ob1, ob2, ob3; { // se deschide un nou bloc Item ob4, ob5; } Item *pob6 = new Item[3]; delete [] pob6; }

Care sunt mesajele care apar la consol la execuia acestui program? S se explice evoluia numrului de obiecte n via n cursul execuiei.

38

2.11 Construiti clasa credite care contine datele membre private val_credit, nr_cont, si date membre publice nume, tip_credit. a). Introduceti in clasa cel putin doi constructori pentru intializarea obectelor din clasa credite. b). Realizati un tablou cu 20 obiecte din clasa credite. c). Realizati functii de acces la datele membre private. d). Afisati numarul creditelor de tip ipotecar, utilizand date si functii membre. 2. 12 Definii clasa pacient care contine datele private: vrsta, profesie, salariul i data membra public nume. Realizai: a). un tablou de 10 obiecte de tipul clasei definit anterior; b). funcii membre de acces la datele private ale clasei; c). numarul pacientilor care au varsta peste media vrstelor utiliznd o funcie membr static; d). realizai o funcie operator pentru a calcula suma veniturilor pentru pacienii cu acelai nume 2. 13 Realizai clasa fractie care cuprinde datele membre private :numitor, numarator. 1. realizai funcii de acces la datele membre ale clasei definit anterior; 2. realizai funcii membre pentru a calcula suma si inmultirea a doua fractii, utlizand functii operator; 3. construii un tablou cu 10 obiecte de tipul definit anterior; 4. realizai ordonarea fraciilor in ordine crescatoare dupa numitor.
2.14 Spunei de cte ori se execut fiecare constructor n programul de mai jos i n ce ordine.
#include <iostream.h> class cls1 { protected: int x; public: cls1(){ x=13; } }; class cls2: public cls1 { int y; public: cls2(){ y=15; } int f(cls2 ob) { return (ob.x+ob.y); } }; int main() { cls2 ob; cout<<ob.f(ob); return 0; }

2.15 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, altfel, spunei de ce nu este corect. #include <iostream.h>
class cls1 { int x; public: cls1(){ x=13; } int g(){ static int i; i++; return (i+x); } }; class cls2 { int x; public: cls2(){ x=27; } cls1& f(){ static cls1 ob; return ob; } };

39

int main() { cls2 ob; cout<<ob.f().g(); return 0; }

2.16 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, altfel, spunei de ce nu este corect. #include <iostream.h>
class cls1 { protected: int x; public: cls1(int i=10) { x=i; } int get_x() { return x;} }; class cls2: cls1 { public: cls2(int i):cls1(i) {} }; int main() { cls d(37); cout<<d.get_x(); return 0; }

40

Capitolul 3 .Mostenire
Obiective Definirea notiunilor de clas de baz, clas derivat, ierarhie de clase, mostenire nsusirea modalittii de creare de noi clase pornind de la clase existente ntelegerea rolului membrilor protejati ai unei clase nsusirea mecanismului de redefinire a membrilor unei clase ntr-o clas derivat

3.1 Relatia de mostenire. Clase de baz si clase derivate


Derivarea permite definirea ntr-un mod simplu, eficient i flexibil a unor clase noi prin adugarea unor funcionaliti claselor deja existente, fr s fie necesar reprogramarea sau recompilarea acestora. Clasele derivate exprim relaii ierarhice ntre conceptele pe care acestea le reprezint i asigur o interfa comun pentru mai multe clase diferite. De exemplu, entitile cerc, triunghi, dreptunghi, sunt corelate ntre ele prin aceea c toate sunt forme geometrice, deci ele au n comun conceptul de form geometric. Pentru a reprezenta un cerc, un triunghi sau un dreptunghi, ntr-un program, trebuie ca aceste clase, care reprezint fiecare form geometric n parte, s aib n comun clasa care reprezint n general o form geometric. Mostenirea reprezint o form de implementare a reutilizrii codului. Ea apare n urma crerii de noi clase prin operatia de derivare. Derivarea reprezint definirea unei noi clase prin extinderea uneia sau a mai multor clase existente. Noua clas se numeste clas derivat, iar clasele existente din care a fost derivat se numesc clase de baz. n cazul n care exist o singur clas de baz, mostenirea se numeste mostenire singular. Limbajul C++ accept existenta mai multor clase de baz. O clas derivat mosteneste toti membrii tuturor claselor sale de baz. Adic, clasa derivat contine toate variabilele membru continute n clasele de baz si suport toate operatiile furnizate de clasele de baz. O clas derivat poate fi la rndul ei clas de baz pentru noi clase. Astfel se poate genera o ierarhie de clase (graf de mostenire). Sintaxa declarrii unei clase derivate dintr-o clas de baz :
class <clasa_derivat> : public <clasa_de_baz> { // membrii clasei derivate };

Mostenirea sau relatia de derivare este indicat n antetul clasei derivate prin cuvntul cheie public prefixat de caracterul : si urmat de numele clasei de baz. n cadrul relatiei de mostenire poate apare n clasa de baz o sectiune protejat, marcat prin cuvntul cheie protected, care permite accesul claselor derivate din ea la datele si functiile membru din sectiunea respectiv.

41

Observatii O clas derivat nu poate accesa direct membrii privati ai clasei sale de baz. Dac s-ar permite asa ceva s-ar nclca unul din principiile fundamentale ale POO (ncapsularea). O clas derivat poate accesa membrii privati doar prin intermediul functiilor publice si protejate ale clasei de baz. O clas derivat poate accesa membrii publici si protejati ai clasei de baz Relatia de mostenire este tranzitiv. Functiile friend nu se mostenesc. Se consider un program P3.1 care descrie organizarea personalului unei instituii fr folosirea claselor derivate. O clas numit Angajat deine date i funcii referitoare la un angajat al instituiei:
class Angajat{ char *nume; float salariu; public: Angajat(); Angajat(char *n, float sal); Angajat(Angajat& r); void display(); }; Angajat::display(){ cout << nume << << salariu << endl; }

Diferite categorii de angajai necesit date suplimentare fa de cele definite n clasa Angajat, corespunztoare postului pe care l dein. De exemplu, un aministrator este un angajat (deci sunt necesare toate datele care descriu aceast calitate) dar mai sunt necesare i alte informaii, de exemplu precizare seciei pe care o conduce. De aceea, clasa Administator trebuie s includ un obiect de tipul Angajat, la care adaug alte date:
class Administrator{ Angajat ang; int sectie; public: void display(); }

Posibilitatea de a include ntr-o clas date descrise ntr-o alt clas are n limbajele orientate pe obiecte un suport mai eficient i mai simplu de utilizat dect includerea unui obiect din tipul dorit: derivarea claselor, care motenesc (date i funcii membre) de la clasa de baz. Un administrator este un angajat, de aceea clasa Administrator se poate construi prin derivare din clasa Angajat astfel:

42

class Administrator : public Angajat { int sectie; public: void display(); } In clasa Administrtrator nu se pot aceesa datele private din clasa Angajat, chiar data tipul mostenerii este public. Asa cu s-a subliniat inainte, datele private ale clasei de baza nu pot fi utilizate de catre clasa derivata. Metoda cea mai adecvat de acces la membrii private clasei de baz din clasa derivat este prin utilizarea funciilor membre publice ale clasei de baz. De exemplu, nu se poate implementa funcia display() din clasa Administrator prin accesarea membrilor private ai clasei Angajat: void Administrator::display(){ cout << nume << << salariu << endl; // eroare cout << sectie << endl; } n schimb, se poate folosi funcia membr public display() a clasei Angajat: void Administrator::display(){ Angajat::display(); cout << sectie << endl; }

Redefinirea funciei display() n clasa derivat ascunde funcia cu acelai nume din clasa de baz, de aceea este necesar calificarea funciei cu numele clasei de baz folosind operatorul de rezoluie: Angajat::display(). Din clasa derivat (n funcii membre ale acesteia sau pentru obiecte din clasa derivat) este accesat membrul redefinit n clasa derivat. Se spune c membrul din clasa de baz este ascuns (hidden) de membrul redefinit n clasa derivat. Un membru ascuns din clasa de baz poate fi totui accesat dac se folosete operatorul de rezoluie (::) pentru clasa de baz. De exemplu:
class Base { public: int a, b; } class Derived { public: int b, c; // b este redefinit }; void fb() { Derived d; d.a = 1; // a din Base d.Base::b = 2; // b din Base d.b = 3; // b din Derived d.c = 4; // c din Derived }

43

3.2 Constructori i destructori n clasele derivate


Constructorii i destructorii sunt funcii membre care nu se motenesc. La crearea unei instane a unei clase derivate (obiect) se apeleaz implicit mai nti constructorii claselor de baz i apoi constructorul clasei derivate. Ordinea n care sunt apelai constructorii claselor de baz este cea din lista claselor de baz din declaraia clasei derivate. Constructorii nu se pot redefini pentru c ei, n mod obligatoriu, au nume diferite (numele clasei respective). La distrugerea unui obiect al unei clase derivate se apeleaz implicit destructorii n ordine invers: mai nti destructorul clasei derivate, apoi destructorii claselor de baz, n ordinea invers celei din lista din declaraie. La instanierea unui obiect al unei clase derivate, dintre argumentele care se transmit constructorului acesteia o parte sunt utilizate pentru iniializarea datelor membre ale clasei derivate, iar alt parte sunt transmise constructorilor claselor de baz. Argumentele necesare pentru iniializarea claselor de baz sunt plasate n definiia (nu n declaraia) constructorului clasei derivate. Un exemplu simplu, n care constructorul clasei derivate D transfer constructorului clasei de baz B un numr de k argumente arat astfel:
class B{ //. public: B(tip1 arg1,,tipk argk); }; class D:public B { //. public: D(tip1 arg1, ,tipk argk,,tipn argn); }; D::D(tip1 arg1, ,tipk argk, .,tipn argn) :B(arg1, arg2, ,argk); { // initialzare date membre clasa derivata } Sintaxa general pentru transmiterea de argumente din clasa derivat ctre clasa de baz: <construct_cls_derivata>(<L1>) : <construct_cls_baza>(<L2>) { // corpul constructorului clasei derivate }

<L1> reprezint lista de argumente ale constructorului clasei derivate, iar <L2> lista de argumente ale constructorului clasei de baz. Lista <L1> include lista <L2>. Observatii: Constructorii si destructorul clasei de baz nu se mostenesc.

44

Constructorul clasei de baz se va executa naintea constructorului clasei derivate, iar destructorul clasei derivate se va executa naintea destructorului clasei de baz.

3.3 Controlul accesului la membrii clasei de baz


Accesul la membrii clasei de baz motenii n clasa derivat este controlat de specificatorul de acces (public, protected, private) din declaraia clasei derivate. O regul general este c, indiferent de specificatorul de acces declarat la derivare, datele de tip private n clasa de baz nu pot fi accesate dintr-o clas derivat. O alt regul general este c, prin derivare, nu se modific tipul datelor n clasa de baz. Un membru protected ntr-o clas se comport ca un membru private, adic poate fi accesat numai de membrii acelei clase i de funciile de tip friend ale clasei. Diferena ntre tipul private i tipul protected apare n mecanismul de derivare: un membru protected al unei clase motenit ca public ntr-o clas derivat devine tot protected n clasa derivat, adic poate fi accesat numai de funciile membre i friend ale clasei derivate i poate fi transmis mai departe, la o nou derivare, ca tip protected. Motenirea de tip public a clasei de baz Dac specificatorul de acces din declaraia unei clase derivate este public, atunci: Datele de tip public ale clasei de baz sunt motenite ca date de tip public n clasa derivat i deci pot fi accesate din orice punct al domeniului de definiie al clasei derivate. Datele de tip protected n clasa de baz sunt motenite protected n clasa derivat, deci pot fi accesate numai de funciile membre i friend ale clasei derivate. n programul urmator P3.2 sunt prezentate i comentate cteva din situaiile de acces la membrii clasei de baz din clasa derivat atunci cnd specificatorul de acces este public.
class Base { inta; protected: int b; public: int c; void seta(int x){a = x; cout << "seta din baza\n";} void setb(int y){b = y; cout << "setb din baza\n";} void setc(int z){c = z; cout << "setc din baza\n";} }; class Derived : public Base { int d; public: void seta(int x) { a = x; // eroare, a este private } void setb(int y) {

45

b = y; cout << "setb din derivata\n"; } void setc(int z) { c = z; cout << "setc din derivata\n"; } }; void fb(){ Derived obd; obd.a = 1; // eroare, a este private in baza obd.seta(2); // corect, se apeleaz baza::seta obd.b = 3; // eroare, b este protected obd.Base::setb(5);// corect, Base::setb este public obd.setb(4); // corect, Derived::setb este public obd.c = 6; // corect, c este public obd.Base::setc(7);// corect, Base::setc este public obd.setc(8); // corect, Derived::setc este public }

Dac se comenteaz liniile de program care provoac erori i se execut funcia fb(), se obin urmtoarele mesaje la consol:
seta setb setb setc setc din din din din din baza baza derivata baza derivata

Motenirea de tip protected a clasei de baz Dac specificatorul de acces din declaraia clasei derivate este protected, atunci toi membrii de tip public i protected din clasa de baz devin membri protected n clasa derivat. Bineneles, membrii de tip private n clasa de baz nu pot fi accesai din clasa derivat. Se reiau clasele din exemplul precedent cu motenire protected:
class Derived : protected Base { // acelasi corp al clasei };

n aceast situaie, n funcia fb() sunt anunate ca erori de compilare toate apelurile de funcii ale clasei de baz pentru un obiect derivat, precum i accesul la variabila c a clasei de baz:
void fb(){ Derived obd; obd.a = 1; // eroare, a este private in baza obd.seta(2); // eroare, Base::seta()este protected obd.b = 3; // eroare, b este protected

46

obd.Base::setb(5); // eroare, Base::setb este prot. obd.setb(4); // corect, Derived::setb este public obd.c = 6; // eroare, c este protected obd.Base::setc(7); // eroare, Base::setc este prot. obd.setc(8); // corect, Derived::setc este public }

Dac se comenteaz toate liniile din funcia fb() care produc erori, la execuia acesteia se afieaz urmtoarele rezultate:
setb din derivata setc din derivata

Din acest exemplu reiese pregnant faptul c n motenirea protected a unei clase de baz nu mai pot fi accesai din afara clasei derivate nici unul dintre membrii clasei de baz. Motenirea de tip private a clasei de baz Dac specificatorul de acces din declaraia clasei derivate este private, atunci toi membrii de tip public i protected din clasa de baz devin membri de tip private n clasa derivat i pot fi accesai numai din funciile membre i friend ale clasei derivate. Din nou trebuie reamintit c membrii de tip private n clasa de baz nu pot fi accesai din clasa derivat. Din punct de vedere al clasei derivate, motenirea de tip private este echivalent cu motenirea de tip protected. ntradevr, dac modificm clasa derivata din Exemplul 5.2 astfel:
class Derived : private Base { // acelasi corp al clasei };

mesajele de erori de compilare i de execuie ale funciei fb() sunt aceleai ca i n motenirea protected. Ceea ce difereniaz motenirea de tip private fa de motenirea de tip protected este modul cum vor fi trasmii mai departe, ntr-o nou derivare, membrii clasei de baz. Toi membrii clasei de baz fiind motenii de tip private, o nou clas derivat (care motenete indirect clasa de baz) nu va mai putea accesa nici unul din membrii clasei de baz.

3.4 Mostenirea multipla


Relatia de derivare conduce la generarea unor ierarhii de clase. n astfel de ierarhii poate apare att mostenirea singular ct si cea multipl. Un exemplu de ierarhie de clasa este reprezentat in figura 3.4
Persoana

Student

Angajat

Elev

Medic

Profesor

47

Fig. 3.4 Ierarhie de clase

Clasa de baz a ierarhiei de clase este clasa Persoana. Din aceast clas sunt derivate direct clasele Elev, Student si Salariat. La rndul ei clasa Salariat este clas de baz pentru clasele Medic si Profesor. Programul P3.3 prezint un exemplu de implementare a claselor Persoana, Salariat, Arhitect, Inginer si Medic. // fisierul sursa p3_5.cpp #include <iostream.h> #include <string.h> #include <assert.h> // definitia clasei de baza Persoana class Persoana { char* nume; char* pren; public: Persoana(char*, char*); ~Persoana(); void afisare(); }; Persoana::Persoana(char* N, char* P) { nume = new char[strlen(N)+1]; strcpy(nume, N); pren = new char[strlen(P)+1]; strcpy(pren, P); } Persoana::~Persoana() { delete nume; delete pren; } void Persoana::afisare() { cout << "\n Nume: "<< nume << " Prenume: " << pren; } // definitia clasei Salariat derivata din clasa Persoana class Salariat : public Persoana { float salariu; public: Salariat(char *n, char *p, float s=0); void seteazaSalariu(float s); void afisare(); }; 48

Salariat::Salariat(char* n, char* p, float S) :Persoana(n, p) { seteazaSalariu(s); } void Salariat::seteazaSalariu(float S) { salariu = s; } void Salariat::afisare() { cout << "\n Salariat:"; Persoana::afisare(); cout << "\n Salariu:" << salariu; } // definitia clasei Inginer derivata din clasa Salariat class Inginer : public Salariat { char* domeniu; public: Inginer(char* n, char* p, float s, char* d); void seteazaDomeniu(char *d); void afisare(); }; Inginer::Inginer(char* n, char* p, float s, char * d) :Salariat(n, p, s) { domeniu = new char[strlen(d)+1]; strcpy(domeniu, d); } void Inginer::seteazaDomeniu(char* d) { strcpy(domeniu, d); } void Inginer::afisare() { cout << "\n Inginer "; Salariat::afisare(); cout << "\n\t Domeniu de lucru este " << domeniu; } class Profesor : public Salariat { char* tip; public: Profesor(char* n, char* p, float s, char* t); void seteazaTip(char *t); void afisare(); }; Profesor::Profesor(char* n, char* p, float s, char* t) :Salariat(n, p, s) {

49

tip = new char[strlen(t)+1]; strcpy(tip_inginer, t); } void Profesor::seteazaTip(char* t) { strcpy(tip_inginer, T); } void Profesor::afisare() { cout << "\n Profesor "; Salariat::afisare(); cout << "\n\t Tip profesor " << tip; } void main() { Persoana P1=Persoana("Popescu", "Ana"); Salariat S1=Salariat("Ion", "Alexandru", 2300); Inginer I1=Inginer("Syan", "Maria", 3400, "inginer civile"); Profesor PR1=Inginer("Pop", "Cristi", 2200, "informatica"); P1.afisare(); S1.afisare(); I1.afisare(); PR1.afisare(); } Mostenirea utilizat este cea public. Functia afisare() a fost redefinit n toate clasele derivate, ea apelnd varianta din clasa imediat urmtoare pe nivelul superior din ierarhia de clase. De exemplu, functia afisare() din clasa Salariat apeleaz functia afisare() din clasa Persoana astfel, Persoana::afisare();

Functia afisare() din clasele Inginer si Profesor apeleaz functia afisare() din clasa Salariat: Salariat::afisare();

3.4 Clase de baz virtuale


ntr-o motenire multipl este posibil ca o clas s fie motenit indirect de mai multe ori, prin intermediul unor clase care motenesc, fiecare n parte, clasa de baz. De exemplu:
class class class class L A B D { : : : public: int x;}; public L { /* */}; public L { /* */}; public A, public B { /* */};

Acest motenire se poate reprezenta printr-un graf aciclic direcionat care indic relaiile dintre subobiectele unui obiect din clasa D. Din graful de reprezentare a motenirilor, se poate observa faptul c baza L este replicat n clasa D.

50

Un obiect din clasa D va conine membrii clasei L de dou ori, o dat prin clasa A (A::L) i o dat prin clasa B (B::L). In acest caz ser creaza o ambiguitate in situatia urmatoare :
D ob; ob.x = 2; // eroare D::x este ambiguu; poate fi n baza L a clasei A sau n baza L a clasei B

Aceste ambiguitati se pot elimina prin urmatoarele doua metode: - prin calificarea variabilei cu domeniul clasei creia i aparine:
ob.A::x = 2; // corect, x din A ob.B::x = 3; // corect, x din B

- crearea unei singure copii a clasei de baz n clasa derivat. Pentru aceasta este necesar ca acea clas care ar putea produce copii multiple prin motenire indirect (clasa L, n exemplul de mai sus) s fie declarat clas de baz de tip virtual n clasele care o introduc n clasa cu motenire multip. De exemplu:
class class class class L A B D { : : : public: int x; }; virtual public L { /* */ }; virtual public L { /* */ }; public A, public B { /* */ };

O clas de baz virtual este motenit o singur dat i creeaz o singur copie n clasa derivat. Graful de reprezentare a motenirilor din aceste declaraile de mai sus poate fi ilustrat asfel:

3.6 Funcii virtuale i polimorfism


O funcie virtual este o funcie care este declarat de tip virtual n clasa de baz i redefinit ntro clas derivat. Redefinirea unei funcii virtuale ntr-o clas derivat domin definiia funciei n

51

clasa de baz. Funcia declarat virtual n clasa de baz acioneaz ca o descriere generic prin care se definete interfaa comun, iar funciile redefinite n clasele derivate precizeaz aciunile specifice fiecrei clase derivate. Mecanismul de virtualitate asigur selecia funciei redefinite n clasa derivat numai la apelul funciei pentru un obiect cunoscut printr-un pointer. n apelul ca funcie membr a unui obiect dat cu numele lui, funciile virtuale se comport normal, ca funcii redefinite. n limbajele de programare, un obiect polimorfic este o entitate, ca de exemplu, o variabil sau argumentul unei functii, creia i se permite s pstreze valori de tipuri diferite n timpul executiei programului. Functiile polimorfice sunt acele functii care au argumente polimorfice. n limbajele de programare orientate pe obiecte, polimorfismul mpreun cu legarea dinamic reprezint una din caracteristicile extrem de utile care conduc la cresterea calittii programelor. Implementarea obiectelor polimorfice se realizeaz prin intermediul functiilor virtuale. Sintaxa declarrii unei functii virtuale:
virtual <tip_functie> <nume_functie> ([<lp>]); <tip_functie> reprezint tipul ntors de functie, <lp> este

Cnd un pointer al clasei de baz puncteaz la o functie virtual din clasa derivat si aceasta este apelat prin intermediul acestui pointer, compilatorul determin care versiune a functiei trebuie apelat, tinnd cont de tipul obiectului la care puncteaz acel pointer. Astfel, tipul obiectului la care puncteaz determin versiunea functiei virtuale care va fi executat. In programul P3.4 se consider o clas de baz B i dou clase derivate D1 i D2. n clasa de baz sunt definite dou funcii: funcia normal f()i funcia virtual g(). n fiecare din clasele derivate se redefinesc cele dou funcii f() i g(). n funcia main() se creeaz trei obiecte: un obiect din clasa de baz B indicat prin pointerul B* pb i dou obiecte din clasele derivate D1 i D2. Fiecare dintre obiectele derivate poate fi indicat printr-un pointer la clasa derivat respectiv (D1* pd1, respectiv D2* pd2), precum i printr-un pointer la baz corespunztor (B* pb1 = pd1, respectiv B* pb2 = pd2).
class B { public: void f() { cout << "f() din B\n"; } virtual void g(){ cout << "g() din B\n"; } }; class D1:public B { public: void f() { cout << "f() din D1\n"; } void g() { cout << "g() din D1\n"; } }; class D2:public B { public: void f() { cout << "f() din D2\n"; }

52

void g() { cout << "g() din D2\n"; } }; void fv1 { B* pb = new B; D1* pd1 = new D1; D2* pd2 = new D2; B* pb1 = pd1; B* pb2 = pd2; // f() este functie normala,g() este functie virtuala // Obiect B, pointer B* pb->f(); // f() din B pb->g(); // g() din B // Obiecte D1, D2 , pointeri D1*, D2* pd1->f(); // f() din D1 pd2->f(); // f() din D2 pd1->g(); // g() din D1 pd2->g(); // g() din D2 // Obiecte D1, D2 , pointeri B*, B* pb1->f(); // f() din B pb2->f(); // f() din B pb1->g(); // g() din D1 pb2->g(); // g() din D2 delete pb; delete pd1; delete pd2; };

n primele situaii, cnd pointerul este pointer la tipul obiectului, nu se manifest nici o deosebire ntre comportarea unei funcii virtuale fa de comportarea unei funcii normale: se selecteaz funcia corespunztoare tipului pointerului i obiectului. Diferena de comportare se manifest n ultima situaie, atunci cnd este apelat o funcie pentru un obiect de tip clas derivat printr-un pointer la o clas de baz a acesteia. Pentru funcia normal f() se selecteaz varianta depinznd de tipul pointerului. Pentru funcia virtual g() se selecteaz varianta n funcie de tipul obiectului, chiar dac este accesat prin pointer de tip baz. Polimorfismul, adic apelul unei funcii dintr-o clas derivat prin pointer de tip clas de baz, este posibil numai prin utilizarea pointerilor la obiecte. Obiectele nsele determin varianta funciei apelate, deci nu se pot selecta alte funcii dect cele ale obiectului de tipul respectiv. De exemplu, pentru aceleai clase definite ca mai sus, se consider funcia fv2():
void fv2(){ B obB; D1 obD1; D2 obD2; obB.f(); // f() din B obB.g(); // g() din B obD1.f(); // f() din D1 obD1.g(); // g() din D1

53

obD2.f(); // f() din D2 obD2.g(); // g() din D2 }

Observati Constructorii nu pot fi functii virtuale. n schimb, destructorii pot fi functii virtuale. Functiile inline nu pot fi virtuale. Functiile virtuale sunt ntotdeauna functii membru nestatice ale unei clase.

3.7

Clase abstracte

De cele mai multe ori, o funcie declarat de tip virtual n clasa de baz nu definete o aciune semnificativ i este neaprat necesar ca ea s fie redefinit n fiecare din clasele derivate. Pentru ca programatorul s fie obligat s redefineasc o funcie virtual n toate clasele derivate n care este folosit aceast funcie, se declar funcia respectiv virtual pur. O funcie virtual pur este o funcie care nu are definiie n clasa de baz, iar declaraia ei arat n felul urmtor: virtual tip_returnat nume_functie(lista_argumente) = 0; O clas care conine cel puin o funcie virtual pur se numete clas abstract. Deoarece o clas abstract conine una sau mai multe funcii pentru care nu exist definiii, nu pot fi create instane din acea clas, dar pot fi creai pointeri i referine la astfel de clase abstracte. O clas abstract este folosit n general ca o clas fundamental, din care se construiesc alte clase prin derivare. Orice clas derivat dintr-o clas abstract este, la rndul ei clas abstract (i deci nu se pot crea instane ale acesteia) dac nu se redefinesc toate funciile virtuale pure motenite. Dac o clas redefinete toate funciile virtuale pure ale claselor ei de baz, devine clas normal i pot fi create instane ale acesteia. Exemplul urmtor (5.6) evideniaz caracteristicile claselor abstracte i ale funciilor virtuale pure. Programul P3.5 se realizeaza conversia unor date dintr-o valoare de intrare ntr-o valoare de ieire; de exemplu, din grade Farenheit n grade Celsius, din inch n centimetri, etc.
class Convert{ protected: double x; // valoare intrare double y; // valoare iesire public: Convert(double i){x = i;} double getx(){return x;} double gety(){return y;} virtual void conv() = 0; }; // clasa FC de conversie grade Farenheit in grade Celsius class FC: public Convert{ public: FC(double i):Convert(i){} void conv(){y = (x-32)/1.8;}

54

}; // clasa IC de conversie inch in centimetri class IC: public Convert{ public: IC(double i):Convert(i){} void conv(){y = 2.54*x;} }

void main (){ Convert* p = 0; // pointer la baza cout<<"Introduceti valoarea si tipul conversiei: "; double v; char ch; cin >> v >> ch; switch (ch){ case 'i': //conversie inch -> cm (clasa IC) p = new IC(v); break; case 'f': //conv. Farenheit -> Celsius (clasa FC) p = new FC(v); break; } if (p){ p->conv(); cout << p->getx() << "---> " << p->gety()<< endl; delete p; } }

Clasa de baz abstract Convert este folosit pentru crearea prin derivare a unei clase specifice fiecrui tip de conversie de date dorit. Aceast clas definete datele comune, necesare oricrui tip de conversie preconizat, de la o valoare de intrare x la o valoare de ieire y. Funcia de conversie conv() nu se poate defini n clasa de baz, ea fiind specific fiecrui tip de conversie n parte; de aceea funcia conv() se declar funcie virtual pur i trebuie s fie redefinit n fiecare clas derivat. n funcia main() se execut o conversie a unei valori introduse de la consol, folosind un tip de conversie (o clas derivat) care se selecteaz pe baza unui caracter introdus la consol. Acesta este un exemplu n care este destul de pregnant necesitatea funciilor virtuale: deoarece nu se cunoate n momentul compilrii tipul de conversie care se va efectua, se folosete un pointer la clasa de baz pentru orice operaie (crearea unui obiect de conversie nou, apelul funciei conv(), afiarea rezultatelor, distrugerea obiectului la terminarea programului). Singura difereniere care permite selecia corect a funciilor, este tipul obiectului creat, care depinde de tipul conversiei cerute de la consol.

55

3.8

Polimorfism

Polimorfismul permite unei entitti (de exemplu, variabil, functie, obiect) s aib o varietate de reprezentri. El este furnizat att la momentul compilrii (legare timpurie), prin folosirea operatorilor si a functiilor redefinite, ct si la momentul executiei (legare trzie), prin utilizarea functiilor virtuale. Conceptul de legare dinamic permite unei variabile s aib tipuri diferite n functie de continutul ei la un moment dat. Aceast abilitate a variabilei se numeste polimorfism, iar variabila se numeste variabil polimorfic. n limbajul C++ variabilele polimorfice apar doar prin utilizarea pointerilor sau referintelor. n cazul n care un pointer al clasei de baz puncteaz ctre o functie virtual, programul va determina la momentul executiei la care tip de obiect puncteaza pointerul si apoi va selecta versiunea corespunztoare functiei redefinite. Programul P4.4 defineste clasa de baz Persoana si dou clase derivate, Student si Salariat. Clasa de baz Persoana este o clas abstract avnd declarat o functie virtual pur, venit(), definit n clasele derivate. De asemenea, clasa Persoana mai contine o alt functie virtual, afisare(), care este redefinit n clasele derivate. n programul principal sunt create dou obiecte S1 si T1 din clasele Student si respectiv Salariat. Pentru fiecare din cele dou obiecte se execut o secvent de patru instructiuni: primele dou instructiuni ilustreaz legarea static, prin apelul celor dou functii afisare() si venit(), corespunztoare obiectului referit prin nume; ultimele dou instructiuni ilustreaz legarea dinamic apelnd cele dou functii afisare() si venit() prin intermediul a dou functii, Ref_Pointer(.), Ref_Referinta(.), care utilizeaz un pointer, respectiv o referint ctre clasa de baz Persoana. Aceste ultime dou instructiuni genereaz un comportament polimorfic, la momentul executiei programului. // fisierul sursa p4_4.cpp #include <iostream.h> #include <string.h> #include <assert.h> // clasa de baza Persoana, clasa abstracta class Persoana { char* prenume; char* nume; public: Persoana(char*, char*); ~Persoana(); char* preiaPrenume(); char* preiaNume(); // functie virtuala pura virtual double venit() = 0; virtual void afisare(); }; Persoana::Persoana(char* P, char* N) { prenume = new char[strlen(P)+1];

56

strcpy(prenume, P); nume = new char[strlen(N)+1]; strcpy(nume, N); } Persoana::~Persoana() { delete prenume; delete nume; } char* Persoana::preiaPrenume() { return prenume; } char* Persoana::preiaNume() { return nume; } void Persoana::afisare() { cout << prenume << " " << nume << "\n"; } // definim clasa Student derivata din clasa Persoana class Student : public Persoana { double bursa; double media; public: Student(char*, char*, double = 0.0, double = 0.0); void seteazaBursa(double); void seteazaMedia(double); virtual double venit(); virtual void afisare(); }; Student::Student(char* P, char* N, double B, double M) :Persoana(P,N) { M>=8.50?seteazaBursa(B):seteazaBursa(0.0); seteazaMedia(M); } void Student::seteazaBursa(double B) { bursa = B>0?B:0; } void Student::seteazaMedia(double M) { media = M>0?M:0.0; } double Student::venit() { return bursa;

57

} void Student::afisare() { cout << "\n Student:"; Persoana::afisare(); cout << "\t Media = " << media << "\n"; } // definim clasa Salariat derivata din clasa Persoana class Salariat:public Persoana { double salariu; double venit_ora; int nr_ore; public: Salariat(char*, char*, double = 0.0, double = 0.0, int = 0); void seteazaSalariu(double); void seteazaVenitOra(double); void seteazaNrOre(int); virtual double venit(); virtual void afisare(); }; Salariat::Salariat(char* P,char* N,double S,double V,int nr) :Persoana(P, N) { seteazaSalariu(S); seteazaVenitOra(V); seteazaNrOre(nr); } void Salariat::seteazaSalariu(double S) { salariu = S>0 ? S : 0; } void Salariat::seteazaVenitOra(double V) { venit_ora = V>0.0 ? V : 0.0; } void Salariat::seteazaNrOre(int nr) { nr_ore = nr>0 ? nr : 0; } double Salariat::venit() { return salariu + nr_ore*venit_ora; } void Salariat::afisare() { cout << "\n Salariat:"; Persoana::afisare(); } // functie care apeleaza functiile virtuale prin legare

58

// dinamica, in cazul unui pointer al clasei de baza // referire prin pointer void Ref_Pointer(Persoana *Ptr) { Ptr->afisare(); cout << " venit (lei) " << Ptr->venit(); } // functie care apeleaza functiile virtuale prin legare // dinamica, in cazul unei referinte la clasa de baza // referire prin referinta void Ref_Referinta(Persoana &Ptr) { Ptr.afisare(); cout << " venit (lei) " << Ptr.venit(); } void main() { Student S1("Alexandra", "Stoica", 1500000, 10); cout << "\n Legare statica:\n"; S1.afisare(); cout << " venit (lei) "<<S1.venit(); cout << "\n Legare dinamica:\n"; Ref_Pointer(&S1); Ref_Referinta(S1); Salariat T1("Silvan", "Manole", 50000000, 100000, 30); cout << "\n Legare statica:\n"; T1.afisare(); cout<<" venit (lei) "<<T1.venit(); cout << "\n Legare dinamica: \n"; Ref_Pointer(&T1); Ref_Referinta(T1); } Observatii Principiul care st la baza polimorfismului este o singur interfat, metode multiple. Practic, polimorfismul reprezint abilitatea obiectelor din clase diferite ale unei ierarhii de clase de a rspunde diferit la acelasi mesaj (adic,la apelul unei functii membru). Implementarea polimorfismului este realizat prin intermediul functiilor virtuale. n cazul n care o functie membru ne-virtual este definit ntr-o clas de baz si redefinit ntr-o clas derivat, comportamentul este ne-polimorfic. Astfel, dac functia membru este apelat printr-un pointer al clasei de baz la obiectul clasei derivate, se utilizeaz versiunea clasei de baz. Dac functia membru este apelat printr-un pointer al clasei derivate, se utilizeaz versiunea clasei derivate.

59

Teste de autocontrol
3.1 Definiti relatia de derivare. 3.2 Definiti mostenirea. 3.3 Dac din clasa X se genereaz o clas Z cum se numesc cele dou clase? (a) X clas derivat, Z clas de baz (b) X superclas, Z subclas (c) X clas copil, Z clas printe (d) X clas de baz, Z clas derivat 3.4 Care dintre afirmatiile urmtoare sunt adevrate si care sunt false? (a) Obiectele unei clase derivate au acces la membrii privati ai clasei sale de baz. (b) Relatia de mostenire este tranzitiv. (c) Functiile friend ale clasei de baz se mostenesc de ctre clasa derivat. (d) Constructorul si destructorul clasei de baz se mostenesc n clasa derivat. 3.5 Selectati rspunsul corect referitor la ordinea de apelare a constructorilor si a destructorilor n cazul claselor derivate dintr-o clas de baz. Ordinea de apelare este urmtoarea: (a) constructorul clasei derivate constructorul clasei de baz destructorul clasei derivate destructorul clasei de baz (b) constructorul clasei de baz constructorul clasei derivate destructorul clasei derivate destructorul clasei de baz (c) constructorul clasei derivate constructorul clasei de baz destructorul clasei de baz destructorul clasei derivate (d) constructorul clasei de baz constructorul clasei derivate destructorul clasei de baz 3.6 Care este avantajul principal oferit de mecanismul mostenirii? 3.7 Utilizarea mostenirii si a polimorfismului permite eliminarea unei anumite instructiuni. Care este aceast instructiune n limbajul C++? 3.8 Cum este specificat o functie virtual pur? 3.9 Cum se numeste o clas care contine una sau mai multe functii virtuale pure? 3.10 Dac apelul unei functii este rezolvat la momentul executiei legarea este (a) static (b) timpurie (c) nul (d) dinamic

60

3.11 Care dintre afirmatiile urmtoare sunt adevrate? (a) Constructorii pot fi functii virtuale. (b) Destructorul poate fi functie virtual. (c) Orice functie membru static este functie virtual. (d) Functiile inline nu pot fi functii virtuale. 3.12 Care este diferenta ntre o functie virtual si o functie virtual pur? 3.13 Ce se ntelege prin polimorfism pur? Comparati cu notiunea de suprancrcare. 3.14 Cum pot fi definite variabilele polimorfice n limbajul C++? 3.15 Explicati pe scurt notiunea de polimorfism n cazul programrii orientate pe obiecte. 3.16 Care este rolul claselor abstracte? 3.17 Scrieti un program care defineste clasa de baz Aparat si clasa derivat Radio. Printre functiile membru includeti si o functie de afisare a datelor membru ale clasei. n programul principal se vor crea dou obiecte ob1 al clasei Aparat si ob2 al clasei Radio si se vor apela functiile de afisare. 3.18 Redefiniti functia de afisare a clasei Aparat n clasa Radio si rescrieti programul de la exercitiul 3.17. 3.19 Rescrieti programul de la exercitiul T3.2 realiznd suprancrcarea constructorului clasei derivate Radio. 3.20 Care sunt erorile din urmtoarea secvent de program? // class A { int a,b; public: A(int , int, double); void afisare(); // protected: double t; }; // class B::public A { double w; public: B(int, int, double); void afisare(); void setValori(int x, int y, double z) { a=x; b=y; t=z;} }; // 61

3.21 Fiind date urmtoarele tipuri de clase:


class B { /* instructiuni */ };

class D1: virtual B { /* instructiuni */ }; class D2: virtual B { /* instructiuni */ }; class D3: B { /* instructiuni */ }; class D4: private B { /* instructiuni */ }; class D5: virtual public B { /* instructiuni */ }; class M1: D1, public D2, D3, private D4, virtual D5 { /* instructiuni */ }; class M2: D1, D2, virtual D3, virtual D4, virtual D5 { /* instructiuni */ };
spunei de cte ori este motenit clasa B n clasa M1. Dar n clasa M2 ? Justificai. 3.22 Spunei care dintre declaraiile funciei main() sunt corecte n programul de mai jos. Justificai.
#include <iostream.h>

class B1 { public: int x; }; class B2 { int y; }; class B3 { public: int z; }; class B4 { public: int t; }; class D: private B1, protected B2, public B3, B4 { int u; }; int main() { D d; cout<<d.u; cout<<d.x; cout<<d.y; cout<<d.z; cout<<d.t; return 0; }
3. 23 Spunei dac programul de mai jos este corect. n caz afirmativ, precizai exact constructorii care se execut i n ce ordine. n caz negativ spunei de ce nu este corect. #include<iostream.h>

class cls1 { public: int x; cls1(int i=13) { x=i; } }; class cls2: virtual public { public: cls2(int i=15) { class cls3: virtual public { public: cls3(int i=17) {

cls1 x=i; } }; cls1 x=i; } }; 62

class cls4: public cls1 { public: cls4(int i=19) { x=i; } }; class cls5: public cls2, public cls3, public cls4 { public: int y; cls5(int i,int j):cls4(i),cls2(j){ y=i+j; } cls5(cls5& ob) ){ y=-ob.y; }}; int main() { cls5 ob1(-9,3), ob2=ob1; cout<<ob2.y; return 0; } 3.24 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect. #include <iostream.h> class B { int a; public: B(int i=0) { a=i; } int get_a(){ return a; } }; class D: private B { public: D(int x=0): B(x) {} int get_a() { return B::get_a(); } }; int main() { D d(-89); cout<<d.get_a(); return 0; }

63

Cap. 4 Facilitti ale limbajului C++

Obiective Redefinirea operatorilor Definirea template-urilor nsusirea modului de lucru cu fisiere n limbajul C++

4.1 Supraincarcarea operatorilor


Limbajul C++ permite programatorului s defineasc diverse operatii cu obiecte ale claselor, utiliznd simbolurile operatorilor standard. Pentru tipurile fundamentale ale limbajului sunt definite seturi de operatori care permit operaii de baz executate ntr-un mod convenabil. Dar, dup cum este cunoscut, n limbaj sunt definite prea puine tipuri de date ca date fundamentale, iar pentru reprezentarea altor tipuri care sunt necesare n diferite domenii (cum ar fi aritmetica numerelor complexe, algebra matricilor, etc.), se definesc clase care conin funcii ce pot opera asupra acestor tipuri. Limbajul C++ nu permite crearea de noi operatori, n schimb permite redefinirea majorittii operatorilor existenti astfel nct atunci cnd acesti operatori sunt aplicati obiectelor unor clase s aib semnificatia corespunztoare noilor tipuri de date. Principalele avantaje ale redefinirii operatorilor sunt claritatea si usurinta cu care se exprim anumite operatii specifice unei clase. Solutia alternativ ar fi definirea unor functii si apelul functiilor n cadrul unor expresii. Funciile operator pentru o anumit clas pot s fie sau nu funcii membre ale clasei. Dac nu sunt funcii membre ele sunt, totui, funcii friend ale clasei i trebuie s aib ca argument cel puin un obiect din clasa respectiv sau o referin la aceasta.

Funcii operator membre ale claselor


Forma general pentru funciile operator membre ale clasei este urmtoarea:
tip_returnat operator # (lista_argumente){ // operaii } Semnul # reprezint oricare dintre operanzii care pot fi suprancrcai.

Programul P 4.1 prezinta clasa Point care descrie un vector ntr-un plan bidimensional prin dou numere de tip float, x i y. Valorile x i y reprezint coordonatele punctului de

64

extremitate al vectorului. Pentru aceast clas se pot defini mai multe operaii cu vectori, ca de exemplu: Suma a doi vectori Diferena a doi vectori Produsul scalar a doi vectori Multiplicarea unui vector cu o constant (scalare)

Aceste operaii se pot implementa prin suprancrcarea corespunztoare a operatorilor. Pentru nceput se vor defini funciile operator+() i operator() pentru calculul sumei, respectiv a diferenei a doi vectori.
class Point{ float x; float y; public: Point(){ x = 0; y = 0; } Point(double a, double b){ x = a; y = b; } void Display() { cout << x << " " << y << endl; } Point operator+(Point op2); // suma a doi vectori Point operator-(Point op2); // diferena a doi vect double operator*(Point op2);// produs scalar Point& operator*(double v); // multipl. cu o const. }; Point Point::operator+(Point op2){ point temp; temp.x = x + op2.x; temp.y = y + op2.y; return temp; } Point Point::operator-(Point op2){ point temp; temp.x = x + op2.x; temp.y = y + op2.y; return temp; } double Point::operator*(Point op2){ return x*op2.y + y*op2.x; }

65

Point& Point::operator*(double x *=v; y *=v; return *this; } void f1(){ Punct pct1(10,20); Punct pct2(30,40); Punct pct3; pct1.Display(); pct2.Display(); pct3 = pct1 + pct2; pct3.Display(); pct3 = pct2 pct1; pct3.Display(); }

v){

// afiseaza 10 20 // afiseaza 30 40 // afiseaza 40 60 // afiseaza 20 20

Funcia operator+() are un singur argument, chiar dac ea suprancarc un operator binar pentru ca argumentul transmis funciei este operandul din dreapta operaiei, iar operandul din stnga este chiar obiectul pentru care se apeleaz funcia operator. Pentru acelai operator se pot defini mai multe funcii suprancrcate, cu condiia ca selecia uneia dintre ele n funcie de numrul i tipul argumentelor s nu fie ambigu. n clasa Point s-a suprancrcat operatorul * cu dou funcii: prima pentru calculul produsului scalar a doi vectori, cealalt pentru multiplicarea vectorului cu o constant. n implementarea prezentat, funcia operator+() creaz un obiect temporar, care este distrus dup returnare. n acest fel, ea nu modific nici unul dintre operanzi, aa cum nici operatorul + pentru tipurile predefinite nu modific operanzii. n general, un operator binar poate fi suprancrcat fie printr-o funcie membr nestatic cu un argument, fie printr-o funcie nemembr cu dou argumente. Un operator unar poate fi suprancrcat fie printr-o funcie membr nestatic fr nici un argument, fie printr-o funcie nemembr cu un argument. La suprancrcarea operatorilor de incrementare sau decrementare (++, --) se poate diferenia un operator prefix de un operator postfix folosind dou versiuni ale funciei operator. n continuare sunt prezentate cteva funcii operator ale clasei Point pentru operatori unari.
class Point{ // public: Point operator!(); Point operator++(); Point operator(); Point operator++(int x); Point operator(int x); };

66

Point operator!(){ x = -x; y = -y; return *this; } Point Point::operator++(){ x++; y++; return *this; } Point Point::operator--(){ x--; y--; return *this; } Point Point::operator ++(int x){ ++x; ++y; return *this; } Point Point::operator --(int x){ --x; --y; return *this; }

Dac ++ precede operandul, este apelat funcia operator++(); dac ++ urmeaz operandului, atunci este apelat funcia operator++(int x), iar x are valoarea 0. La suprancrcarea unui operator folosind o funcie care nu este membr a clasei este necesar s fie transmii toi operanzii necesari, deoarece nu mai exist un obiect al crui pointer (this) s fie transferat implicit funciei. Din aceast cauz, funciile operator binar necesit dou argumente de tip clas sau referin la clas, iar funciile operator unar necesit un argument de tip clas sau referin la clas. n cazul operatorilor binari, primul argument transmis este operandul stnga, iar al doilea argument este operandul dreapta In programul P4.2 o parte din funciile operator ale clasei Point sunt implementate ca funcii friend ale clasei.
class Point { int x; int y; public: //. friend Point operator+(Point op1, Point op2); friend Point operator-(Point op1, Point op2);

67

Point operator+(Point op1, Point op2){ Point temp; temp.x = op1.x + op2.x; temp.y = op1.y + op2.y; return temp; } Point operator-(Point op1, Point op2){ Point temp; temp.x = op1.x - op2.x; temp.y = op1.y - op2.y; return temp; } void f2(){ Punct pct1(10,20); Punct pct2(30,40); Punct pct3; pct1.Display(); pct2.Display(); pct3 = pct1 + pct2; pct3.Display(); pct3 = pct2 pct1; }

// afiseaza 10 20 // afiseaza 30 40 // afiseaza 40 60

Observatii Dac functia operator este implementat ca o functie membru, operandul cel mai din stnga (eventual, unicul operand) trebuie s fie un obiect al clasei sau o referint ctre un obiect al clasei. Implementarea sub form de functie nemembru este indicat n cazul n care cel mai din stnga operand este un obiect al unei clase diferite sau al unui tip predefinit. O functie operator ne-membru trebuie declarat functie friend dac functia respectiv trebuie s acceseze direct membrii privati sau protejati ai clasei respective.

4. 2 Template-uri
O alt facilitate important a limbajului C++ este dat de posibilitatea definirii unor sabloane numite template-uri. Un template reprezint o modalitate de parametrizare a unei clase sau a unei functii prin utilizarea unui tip n acelasi mod n care parametrii unei functii furnizeaz o modalitate de a defini un algoritm abstract fr identificarea valorilor specifice. O clas template specific modul n care pot fi construite clase individuale, difereniate prin tipul sau tipurile de date asupra crore se opereaz. Sintaxa definirii unui template: template <class <parametru> > class <nume_clas> 68

{ // definitia clasei sablon }; Programul P4.3 prezint un exemplu de definire a unei functii template, afisare_vector(.). Aplicarea functiei template celor trei variabile T1, T2 si respectiv T3 va determina afisarea unui vector de numere ntregi, a unui vector de numere reale, respectiv a unui vector de caractere.

// fisierul sursa P4.3.cpp #include <iostream.h> template <class T> void afisare_vector(const T *t, const int nr) { for (int i=0;i<nr;i++) cout<<t[i]<<" "; } void main() { const int nr1 = 5, nr2 = 7, nr3 = 4; int T1[nr1] = {1,2,3,4,5}; double T2[nr2] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7}; char T3[nr3] = "Ana"; cout<<"\n Vectorul contine: "; // functia template pentru vector de numere intregi afisare_vector(T1, nr1); cout<<"\n Vectorul contine: "; // functia template pentru vector de numere reale afisare_vector(T2, nr2); cout<<"\n Vectorul contine: "; // functia template pentru vector de caractere afisare_vector(T3, nr3); } Observatie Template-urile furnizeaz o alt form de polimorfism.

4.3 Stream-uri de I/E


Bibliotecile standard ale limbajului C++ furnizeaz un set extins de facilitti pentru operatii de intrare/iesire (I/E). Dac o functie de I/E este definit corespunztor unui anumit tip de date, ea va putea fi apelat pentru a lucra cu acel tip de date. Se pot specifica att tipuri standard, ct si tipuri definite de utilizatori. Funcii de I/O pentru tipurile predefinite Operaiile de I/O din C++ se efectueaz folosind funciile operator de inserie << i operator de extragere >>.

69

Funciile de operare asupra streamurilor specific modul n care se execut conversia ntre un ir de caractere din stream i o variabil de un anumit tip. Aceste funcii operator sunt definite n clasa ostream, respectiv istream, pentru toate tipurile predefinite ale limbajului, iar pentru tipurile definite de utilizator ele pot fi suprancrcate. Funcia de citire de la consol a unei secvene de numere ntregi separate prin spaii albe (whitespace adic unul din caracterele blanc, tab, newline, carriage return, formfeed) poate arta asfel:
void main(){ int size = 10; int array[10]; for(int i=0;i<size;i++){ if (cin >> array[i]) cout << array[i] << " "; else { cout << "eroare non-int"; break; } } }

O intrare diferit de ntreg va cauza eroare n operaia de citire i deci oprirea buclei for. De exemplu, din intrarea: 1 2 3 4.7 5 6 7 8 9 0 11 se va citi primele patru numere, dup care apare eroare n operaia de intrare, citirea numerelor ntregi se ntrerupe i pe ecran apare mesajul: 1 2 3 4 eroare non-int Caracterul punct este lsat n streamul de intrare, ca urmtor caracter de citit. O alt soluie pentru citirea unei secvene de intrare este prin folosirea uneia din funciile get() definite n clasa iostream astfel:
class iostream : public virtual ios { // istream& get(char& c); istream& get(char* p, int n, char ch=\n); };

Aceste funcii treateaz spaiile albe la fel ca pe toate celelalte caractere. Funcia get(char& c) citete un singur caracter n argumentul c. De exemplu, o funcie fg() de copiere caracter cu caracter de la intrare (streamul cin) la ieire (streamul cout) poate arta astfel:
void stream(){ char c; while(cin.get(c)) cout << c;

70

i funcia operator >> () are un echivalent ca funcie de scriere la consol fr suprancrcarea operatorului >>, numit funcia put(), astfel nct funcia fg() se poate rescrie astfel:
void stream(){ char c; while(cin.get(c)) cout.put(c); }

Funcii de I/O pentru tipuri definite de utilizator Funciile de I/O pentru tipuri definite de utilizator se obin prin suprancrcarea operatorilor de inserie i de extragere, care au urmtoarea form general:
ostream& operator<<(ostream& os,tip_clasa nume){ // corpul functiei return os; } istream& operator<<(istream& is,tip_clasa& nume){ // corpul functiei return is; }

Primul argument al funciei este o referin la streamul de ieire, respectiv de intrare. Pentru funcia operator de extragere << al doilea argument este dat printr-o referin la obiectul care trebuie s fie extras din stream; n aceast referin sunt nscrise datele extrase din streamul de intrare. Pentru funcia operator de inserie >> al doilea argument este dat prin tipul i numele obiectului care trebuie s fie inserat, sau printr-o referin la acesta. Funciile operator de inserie i extracie returneaz o referin la streamul pentru care au fost apelate, astfel nct o alt operaie de I/O poate fi adugat acestuia. Funciile operator << sau >> nu sunt membre ale clasei pentru care au fost definite, dar pot (i este recomandabil) s fie declarate funcii friend n clasa respectiv. In programul P 4.4 se defineste clasa Complex care cuprinde functiile operator << si >>.
class Complex { double x, y; public: Complex(){x = 0; y = 0} Complex(double r, double i){ x = r; y = i; } .. friend ostrem& operator << (ostrem& os,Complex z);

71

friend istream& operator >>(istream& is,Complex& z); }; ostream& operator<<(ostream& os, Complex& z){ os << ( << z.x << ,<< z.y << ); return os; } istream& operator>>(istream& is, Complex z){ is >> z.x >> z.y; return is; } void main(){ Complex z; cout << "Introduceti x, y :"; cin >> z; cout << "z = " << z << '\n'; cin.get(); int size = 10;

Execuia acestei funcii produce urmtoarele mesaje la consol:


Introduceti x, y: 1.3 4.5 z = (1.3,4.5) Introduceti un sir:123456 123456

4. 4 Procesarea fisierelor
Procesarea fisierelor n limbajul C++ necesit includerea fisierelor antet <iostream.h> si <fstream.h>. Fisierul antet <fstream.h> cuprinde definitiile claselor streamului: ifstream (pentru intrrile dintr-un fisier operatii de citire din fisier), ofstream (pentru iesirile ctre un fisier operatii de scriere n fisier) si fstream (pentru intrrile/iesirile de la/ctre un fisier). Pentru utilizarea unui fiier pe disc acesta trebuie s fie asociat unui stream. Pentru aceasta se creaz mai nti un stream, iar apelul funciei open() a streamului execut asocierea acestuia cu un fiier ale crui caracteristici se transmit ca argumente ale funciei open(). Funcia open() este funcie membr a fiecreia dintre cele trei clase stream (ifstream, ofstream i fstream) Implicit, fiierele se deschid n mod text. Valoarea ios::binary determin deschiderea n mod binar a fiierului. Pentru nchiderea unui fiier se apeleaz funcia close(), care funcie membr a claselor stream (ifstream, ofstream i fstream). Pentru scrierea i citirea dintr-un fiier de tip text se folosesc funciile operator << i >> ale streamului asociat acelui fiier. Aceste funcii operator suprancrcate pentru un anumit tip de date pot fi folosite fr nici o modificare att pentru a scrie sau citi de la consol ct i pentru a scrie sau citi dintr-un fiier pe disc. 72

Programul P4.5 creaz un fiier de inventariere inventar.txt care conine numele articolului, preul unitar, numrul de buci i valoarea total.
void finv(){ ofstream output("inventar.txt"); if (!output) cout << "Nu se poate deschide fisierul inventar.txt\n"; char buffer[80]; double pret,total; int buc; while(1){ cout << "\nArticol: "; cin.get(buffer,80); if (buffer[0] == 0) break; cout << "Pret unitar: "; cin >> pret; cout << "Nr. bucati: "; cin >> buc; cin.get(); total = buc*pret; output << buffer << endl; output << pret<< endl; output << buc << endl; output << total << endl; } output.close(); }

La citirea dintr-un fiier de tip text de pe disk folosind operatorul >> apar, la fel ca la citirea de la consol, anumite modificri de caractere. Pentru a evita astfel de modificri se folosesc funciile de I/O binare care vor fi prezentate n seciunea urmtoare.

73

Teste de autocontrol 4.1 Care este rolul redefinirii operatorilor n limbajul C++? 4.2 Care dintre afirmatiile urmtoare sunt adevrate? (a) Precedenta unui operator poate fi modificat prin redefinire. (b) Aritatea unui operator nu poate fi modificat prin redefinire. (c) Asociativitatea unui operator poate fi modificata prin redefinire. (d) Semnificatia modului n care lucreaz un operator asupra obiectelor de tipuri predefinite nu poate fi schimbat prin redefinire. 4.3 Care sunt clasele predefinite, dedicate procesrii fisierelor C++? 4.4 S se citeasc un fiier care conine numere flotante, s se construiasc numere complexe din perechi de cte dou numere flotante i s se afieze la consol numerele complexe. 4.4 Pentru clasa String s se defineasc urmtoarele operaii de comparaie folosind funcii operator friend: int operator ==( const String& s1, const String& s2 ); int operator ==( const String& s1, const char* s2 ); int operator ==( const char* s1, const String& s2 ); int operator !=( const String& s1, const String& s2 ); 4.5 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect. #include<iostream.h> class B { protected: int x; B(int i=10) { x=i; } public: virtual B operator+(B ob) { B y(x+ob.x); return y;} }; class D: public B { public: D(int i=10) { x=i; } void operator=(B p) { x=p.x; } B operator+(B ob) { B y(x+ob.x+1); return y; } void afisare(){ cout<<x; } }; int main() { D p1(-59),p2(32),*p3=new D; *p3=p1+p2; p3->afisare(); return 0; }

74

4.6 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect. #include<iostream.h> class cls { public: int sa; cls(int s=0) { sa=s; } operator int() { return sa; } int f(int c) { return (sa*(1+c/100)); } }; int main() { cls p(37); cout<<p.f(p); return 0; } 4.7 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect. #include<iostream.h> class B { public: int x; B(int i=0) { x=i; } virtual B aduna(B ob) { return(x+ob.x); } B minus() { return(1-x); } void afisare(){ cout<<x; } }; class D: public B { public: D(int i=0) { x=i; } B aduna(B ob) { return(x+ob.x+1); } }; int main() { B *p1, *p2; p1=new D(138); p2=new B(-37); *p2=p2->aduna(*p1); *p1=p2->minus(); p1->afisare(); return 0; }

75

Teste recapitulative:
Specificai varianta corect : 1. Clasa
class c { float a; void afisisare(); }

are membrii: a). publici; c). protected; 2. Fie secvena:

b). privai d). date private i metode pubice.


class c {....}; void main() { c e /* instructiuni */ }

n acest caz: a). c este un obiect i e este clas; b). c este o instana a clasei i e este un obiect; c). c este o clasa i e este un obiect; d). descrierea este eronat deoarece se folosete acelai identificator pentru clas i obiect. 3. Avnd declaraia:
class persoana { char nume[20]; int varsta; public: persoana(); int spune_varsta() { return varsta;} }; void main() {persoana p; cout<<p.nume<<p.varsta; }

n acest caz : a). programul afiseaz numele i vrsta unei persoane; b). nu este permis afisarea datelor unei persoane; c). descriere eronat pentru c funcia membra nu este utilizat; d). afiseaz doar vrsta unei persoane. 4. Fie clasa :
class c { int a,b; public: float c (int, int) int det_a {return a;} c (); }

Declaraia float c(int, int) ar putea corespunde unui constructor al clasei? 76

a). da, fiind o supraincarcare a celui existent; b). nu., deoarece creaz ambiguitate; c). nu, deoarece constructorul nu are tip returnat; d). nu, deoarece nu este de tip friend. 5. Fie declaraia :
class c1 {int a ;} class c2 :public c1 { public : int b ; void scrie_a() {cout<<a ;} } void main () { c2 ob ; ob.scie_a () ; }

Selectai afirmaia corect : a). funcia scrie_a() nu are drept de acces asupra unui membru privat; b). programul afiseaz valoare lui a; c). derivarea public este incorect relaizat; d). prin derivare public, accesul la membrii moteniti devine public. 6. Fie declaraia
class c1{/* instructiuni */} class c2:public c1 {/* .*/}

Atunci clasa c2 faa de clasa c1 este: a). derivat; b). de baz; c). friend 7. In secvena urmatoare:

d). virtual.

class cls { public:static int s;}; int cls::s=0; int main(){int i=7;cls::s=i;cout<<cls::s;}

Utilizarea lui s este: a). ilegal, deoarece nu exist nici un obiect creat; b). ilegal, deoarece variabilele statice pot fi doar private; c). ilegal, deoarece s este dublu definit, n clas i n afara ei; d). corect, deoarece membrii statici exist nainte de a se crea obiecte din clasa. 8. Secvena urmatoare:
class persoana { int varsta, salariul; friend ostream & operator<<(ostream &out,persoana p) {out<<p.varsta<< <<p.salariul; return out;} public: persoana(int v){varsta=v;salariul=0;} persoana(){varsta=0;salariul=0;} } int main() {persoana p(1);cout<<p;}

Afiseaz: a). afiseaz 1 0;

b). afiseaz 0 0

77

c). afiseaz 1 1;

d). afiseaz 0 1.

9. Secvena urmatoare:
class vector{int*pe,nr_c; public: operator int(){return nr_c;} vector(int n){ pe=new int[n];nr_c=n; while(n--) pe[n]=n;} void f(int i){cout<<i<<endl;} int main() {vector x(10); f(x)} Afiseaz:

a). 10 b). 9 c). numerele de la 1 la 10 d). numerele de la 0 la 10. Secventa urmatoare:


class vector{int*pe,nr_c; public: operator int(){return nr_c;} vector(int n){ pe=new int[n];nr_c=n; while(n--) pe[n]=n;} void f(int i){cout<<i<<endl;} int main() {vector x(10); f(x)}

Afiseaza: a). 10 b). 9 c). numerele de la 1 la 10 d). numerele de la 0 la 9 11. Secvena urmatoare afiseaz:
class persoana {int varsta; public: persoana(int v=18){varsta=18;} operator int(){return varsta;} persoana& operator++() {varsta++;return *this;} persoana operator ++(int) {persoana aux=*this;varsta++;return aux;} int main(){ persoana p(20); int x=p++,y=++p; cout<< x<< y ;}

Afiseaza : a).20 20; b).20 21 ; c).21 22; d).20 22. 78

12 . Fie secvena :
class c { int a; public: c(); c(const c&); void operator=(c&);} int main() { c a; //instructiuni; c b=a;}

Linia c b=a; determin: a). execuia constructorului de copiere; b). execuia metodei prin care se supraincarc operatorul =; c). execuia att a constructorului de copiere, ct i a metodei operator =; d). execuia contructorului implicit 13. Se considera urmatoarea secventa de program:
class complex { double real; double imag; public: complex(double x=1.0, double y=20.0){real=x; imag=y;} complex( const complex &u) { real=u.real; imag=u.imag; } .............. }

Precizati n care situatie se creaza un obiect anonim: a) complex z1(3.42, -12.9); b) complex z2=z1; c) complex z3(z1); d) complex z1= complex(45.0,0.9); e) complex z(23.25); 14. Se considera urmatoarea secventa de program:
class complex { double real; double imag; public: complex(double x=10.0, double y=10.0){real=x; imag=y;} complex(const complex &u) { real=u.real; imag=u.imag; } .............. }

Precizati n care situatie se realizeaza o copiere a unui obiect n alt obiect: 79

a) complex z1(3.42, -12.9); b) complex z2=z1; c) complex z3(1.0,-1.0); d) complex z(10.7,0.8); e) complex z(23.25); 15. Se considera urmatoarea secventa de program:
class complex { double real; double imag; public: complex(double x=-11.0, double y=-56.90){real=x; imag=y;} complex( const complex &u) { real=u.real; imag=u.imag; } .............. }

Precizai in care situatie se creaza un obiect anonim: a) complex z1(3.42, -12.9); b) complex z1= complex(0.0,0.9); c) complex z2=z1; d) complex z3(z1); e) complex z(23.25); 16. Se considera urmatoarea secventa de program:
class complex { double re; double im; public: complex(double x=-11.0, double y=-56.90){re=x; im=y;} complex( const complex &u) { real=u.re; imag=u.im; } ............ }

Precizati n situatie se utilizeaza constructorul de copiere: a) complex z1(3.4, -12.9); b) complex z3(0.0,-10.9); c) complex z2(0.0,1.0); d) complex z3(z1); e) complex z(2.25); 17. Se considera urmatoarea secventa de program:
class complex { double real;

80

double imag; public: complex(double x=-11.0, double y=-56.90){real=x; imag=y;} complex( const complex &u) { real=u.real; imag=u.imag; } ..............

} Precizati situatia n care nu era necesara folosirea unui constructor cu parametri care iau valori n mod implicit: a) complex z2(3.42,-12.9); b) complex z3(z2); c) complex z=z2; d) complex z4(z); e) complex z5=z4; 18. Se da secventa de program:
class A { int a[3]; public: A(int i, int j, int k){a[0]=i; a[1]=j; a[2]=k;} int &operator[](int i){return a[i];} }; void main(void) { A ob(1,2,3); cout << ob[1]; ob[1]=25; cout<<ob[1]; }

Ce se poate afirma despre operator[]()? a) produce suprancarcarea unei functii; b) produce suprancarcarea unui operator unar; c) suprancarca operatorul []; d) este o functie membru oarecare a clasei A care nu produce suprancarcarea unui operator; e) reprezinta un operator ternar; 19. Suprancarcarea unor operatori se poate realiza prin functii operator sau functii friend. Diferenta ntre aceste doua posibilitati consta n: a) lista de parametri; b) obiect returnat; c) precedent a operatorilor; d) n-aritatea operatorului; e) alte situatii. 20. In secventa de program:
................. int k=100;

81

void fct() { int k; ........... k++; ........... } void gct() { int k=2; ........... ::k++; // (?) ........... }

In instructiunea marcata cu (?), k este o variabila: a) externa; b) statica; c) registru; d) globala; e) automatica; 21. Se considera secventa de program:
class B1 { class D1:public B1,public B2 { public: public: B1(){cout<<"(1)\n";} D1(){cout<<"(7)\n";} ~B1(){cout<<"(2)\n";} ~D1(){cout<<"(8)\n";} }; }; class B2 { class D2:public D1,public B3 { public: public: B2(){cout<<"(3)\n";} D2(){cout<<"(9)\n";} ~B2(){cout<<"(4)\n";} ~D2(){cout<<"(10)\n";} }; }; class B3 { public: B3(){cout<<"(5)\n";} ~B3(){cout<<"(6)\n";} }; void main(){ D1 ob1; D2 ob2; }

Care mesaj se va scrie? a) (1),(3),(7),(3),(5),(7),(9),(10),(6),(8),(4),(2),(2),(3),(2),(2); b) (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(9),(7),(2),(3),(2),(2); c) (1),(3),(7),(1),(3),(7),(5),(9),(10),(6),(8),(4),(2),(8),(4),(2); d) (1),(3),(5),(7),(9),(2),(4),(6),(6),(8),(8),(10),(2),(2),(4),(2); e) (1),(3),(7),(1),(3),(7),(9),(5),(10),(6),(4),(8),(2),(8),(4),(2); 22. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect. #include <iostream.h>
template <class tip> class cls { tip z;

82

public: cls(tip i) { z=i; } tip operator-(cls); }; template <class tip> tip cls<tip>::operator-(cls<tip> a) { return z-a.z; } template <class tip> tip dif(tip x, tip y) { return x-y; } int dif(int x, int y) { return x>=y?x-y:y-x; } int main() { cls<int> i=3; cls<float> j=4; cout<<dif(i,j); return 0; }

23. Descriei pe scurt cum putei prelua o dat prin incluziune i a doua oar prin motenire o clas numar ntr-o clas lista care descrie liste nevide de dimensiune variabil de elemente de tip numar. 24. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect.
#include<iostream.h> class cls { static int x; public: cls(int i=25) { x=i; } friend int& f(cls); }; int cls::x=-13; int& f(cls c) { return c.x; } int main() { cls d(15); cout<<f(d); return 0; }

25. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect.
#include<iostream.h> class cls { int v,nr; public: cls(int i) { nr=i; v=new int[i]; } friend int& operator[](int); friend ostream& operator<<(ostream&,cls); }; int& operator[](cls& x, int i) { return x.v[i]; } ostream& operator<<(ostream& o, cls x) { for(int i=0;i<x.nr;i++) cout<<x.v[i]<< ; return o; } int main() { cls x(10);

83

x[5]=7; cout<<x; return 0; }

26. Descriei pe scurt metoda de identificare a tipului n timpul rulrii (RTTI). 27. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect.
#include<iostream.h> class cls { static int i; int j; public: cls(int x=7) { j=x; } static int imp(int k){ cls a; return i+k+a.j; } }; int cls::i; int main() { int k=5; cout<<cls::imp(k); return 0; }

28. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect. #include<iostream.h>
class cls { int x; public: cls(int i=32) { x=i; } int f() const; }; int cls::f() const { return x++; } void main() { const cls d(-15); cout<<d.f(); }

29. Spunei dac o variabil constant poate fi transmis ca parametru al unei funcii i dac da, n ce situaii. Justificai. 30. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz pentru o valoare ntreag citit egal cu 7, n caz negativ spunei de ce nu este corect.
#include <iostream.h> float f(float f) { if (f) throw f; return f/2; } int main() { int x; try { cout<<Da-mi un numar intreg: ; cin>>x; if (x) f(x); else throw x;

84

cout<<Numarul <<x<< e bun!<<endl; } catch (int i) { cout<<Numarul <<i<< nu e bun!<<endl; } return 0; }

31. Spuneti dac programul de mai jos este corect. n caz afirmativ, spunei ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include<iostream.h> class B { int x; public: B(int i=2):x(i){} int get_x() const { return x; } }; class D: public B { int *y; public: D(int i=2):B(i){ y=new int[i]; for(int j=0; j<i; j++) y[j]=1; } D(D& a){ y=new int[a.get_x()]; for(int i=0;i<a.get_x();i++) y[i]=a[i]; } int& operator[](int i) { return y[i]; } }; ostream& operator<<(ostream& o, const D& a) { for(int i=0;i<a.get_x();i++) o<<a[i]; return o; } int main() { D ob(5); cout<<ob; return 0; }

32. Descrieti trei metode de proiectare diferite prin care elementele unei clase se pot regsi n dublu exemplar, sub diverse forme, n definitia altei clase. 33. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spunei de ce nu este corect.
#include<iostream.h> class B { int x; public: B(int i=10) { x=i; } int get_x() { return x; } }; class D: public B { public: D(int i):B(i) {} D operator+(const D& a) {return x+a.x; } }; int main() { D ob1(7), ob2(-12); cout<<(ob1+ob2).get_x(); return 0; }

34. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include<iostream.h> class B { public: int x; B(int i=16) { x=i; }

85

B f(B ob) { return x+ob.x; } }; class D: public B { public: D(int i=25) { x=i; } B f(B ob) { return x+ob.x+1; } void afisare(){ cout<<x; } }; int main() { B *p1=new D, *p2=new B, *p3=new B(p1->f(*p2)); cout<<p3->x; return 0; }

35. Spuneti ce este obiectul implicit al unei metode si descrieti pe scurt propriettile pe care le cunoasteti despre acesta. 36. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include<iostream.h> class cls { int *v,nr; public: cls(int i) { nr=i; v=new int[i]; for (int j=1; j<nr; j++) v[j]=0; } int size() { return nr; } int& operator[](int i) { return *(v+i); } }; int main() { cls x(10); x[4]=-15; for (int i=0; i<x.size(); i++) cout<<x[i]; return 0; }

37. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include<iostream.h> class cls { int x; public: cls(int i=-20) { x=i; } const int& f(){ return x; } }; int main() { cls a(14); int b=a.f()++; cout<<b; return 0; }

38. Descriei pe scurt mostenirea virtual si scopul n care este folosit. 39. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include<iostream.h> class B { static int x; int i; public: B() { x++; i=1; } ~B() { x--; }

86

static int get_x() { return x; } int get_i() { return i; } }; int B::x; class D: public B { public: D() { x++; } ~D() { x--; } }; int f(B *q) { return (q->get_i())+1; } int main() { B *p=new B; cout<<f(p); delete p; p=new D; cout<<f(p); delete p; cout<<D::get_x(); return 0; }

40. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include<iostream.h> class B { int x; public: B(int i=17) { x=i; } int get_x() { return x; } operator int() { return x; } }; class D: public B { public: D(int i=-16):B(i) {} }; int main() { D a; cout<<27+a; return 0; }

41. Enumerai 3 metode de implementare a polimorfismului de compilare. 42. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include<iostream.h> class cls { static int x; public: cls (int i=1) { x=i; } cls f(cls a) { return x+a.x; } static int g() { return f()/2; } }; int cls::x=7; int main() { cls ob; cout<<cls::g(); return 0; }

87

43. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include<iostream.h> class cls { int *v,nr; public: cls(int i=0) { nr=i; v=new int[i]; for (int j=0; j<size(); j++) v[j]=3*j; } ~cls() { delete[] v; } int size() { return nr; } int& operator[](int i) { return v[i]; } cls operator+(cls); }; cls cls::operator+(cls y) { cls x(size()); for (int i=0; i<size(); i++) x[i]=v[i]+y[i]; return x; } int main() { cls x(10), y=x, z; x[3]=y[6]=-15; z=x+y; for (int i=0; i<x.size(); i++) cout<<z[i]; return 0; }

44. Descrieti pe scurt mecanismul de tratare a exceptiilor. 45. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include<iostream.h> class B { int i; public: B() { i=1; } int get_i() { return i; } }; class D: public B { int j; public: D() { j=2; } int get_j() {return j; } }; int main() { B *p; int x=0; if (x) p=new B; else p=new D; if (typeid(p).name()=="D*") cout<<((D*)p)->get_j(); return 0; }

46. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include <iostream.h> class cls { int x;

88

public: cls(int i) { x=i; } int set_x(int i) { int y=x; x=i; return y; } int get_x(){ return x; } }; int main() { cls *p=new cls[10]; int i=0; for(;i<10;i++) p[i].set_x(i); for(i=0;i<10;i++) cout<<p[i].get_x(i); return 0; }

47. Descrieti pe scurt diferenta dintre un pointer si o referint. 48. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz negativ spuneti de ce nu este corect.
#include <iostream.h> template<class T> int f(T x, T y) { return x+y; } int f(int x, int y) { return x-y; } int main() { int a=5; float b=8.6; cout<<f(a,b); return 0; }

49. Spunei pe scurt prin ce se caracterizeaz un cmp static al unei clase. 50 . Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ spunei de ce nu este corect. #include<iostream.h>
class cls1 { public: int cls1() { a=7; class cls2 { public: int cls2(int i) { cls2(cls1& x) int main() { cls1 x; cout<<x.a; cls2 y(x); cout<<y.b; return 0; } a; } }; b; b=i; } { b=x.a; } };

89

Bibliografie 1. Oprea M., Programare orientat pe obiecte - Exemple n limbajul C++, Editura Matrix Rom. 2. Dr. Kris Jamasa, Totul despre C si C++, Editura Teora. 3. Ion Smeureanu , Programare orientat pe obiecte in Limbajul C++, Editura CISON, Bucuresti 2005. 4.Liviu Negrescu, Limbajul C++, Editura ALBASTRA , Cluj 2000. 5.Luminita Duta, Programarea calculatoarelor in limbajul C++ , Editura Cetatea de Scaun 2006.

90

You might also like