Sunteți pe pagina 1din 166

INTRODUCERE Limbajul C++ se consider att un limbaj de programare intermediar de nivel sczut datorit apropierii sale de limbajele de asamblare

e cu posibiliti de manipulare a biilor, apel de sistem de operare, operaii cu memoria, etc., ct i un limbaj de nivel nalt cu proprieti caracteristice limbajelor din aceast categorie ce permite de a crea aplicaii n baza principiilor programrii orientate pe obiect. ndrumarul prezint lucrrile de laborator pe care studenii trebuie s le execute la disciplina Programarea n limbajul de programare C++. Pentru fiecare lucrare de laborator este indicat tema i scopul lucrrii, consideraiile teoretice necesare, ntrebri pentru verificarea cunotinelor, temele pentru acas i de laborator. Aceste compartimente mpreun cu bibliografia vor fi utilizate de studeni pentru executarea lucrrilor de laborator. Pentru a fi admis la ndeplinirea lucrrii de laborator, fiecare student trebuie s ndeplineasc urmtoarele condiii: s execute tema pentru acas, care const din descrierea algoritmului de rezolvare a problemelor lucrrilor de laborator, s rspund la ntrebrile de control puse de ctre profesor. n perioada efecturii lucrrilor de laborator studentul compileaz i execut programele corespunztoare problemelor indicate de ctre profesor n conformitate cu ndrumarul de laborator, efectueaz analiza datelor de intrare i a rezultantelor obinute, colecteaz datele pentru darea de seam a lucrrii. 3

Lucrarea de laborator se consider executat dup ce studentul demonstreaz profesorului funcionarea corect a programelor la calculator. Pentru fiecare lucrare studentul pregtete darea de seam pe care o susine n faa profesorului. Darea de seam pentru fiecare lucrare de laborator include: foaia de titlu, tema, scopul lucrrii, coninutul problemei propuse spre rezolvare, descrierea algoritmului de rezolvare a problemei propuse, listingul programului, datele obinute n urma executrii lucrrii respective, bibliografia utilizat. Temele de laborator pentru studeni se indic n conformitate cu urmtorul tabel: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 2 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 3 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 4 4 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 5 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 6 1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1 4 7 1 2 3 4 5 6 7 8 9 10 11 12 13 14

15

1 5

1 5

1 5

1 5

1 5

1 15 5

Pe vertical este numrul de ordine al studentului n registru, iar pe orizontal este numrul de ordine al lucrrii de laborator propuse pentru executare.

CUPRINS Introducere .......................................................................... Lucrarea de laborator nr. 1 Lucrarea de laborator nr. 2 Lucrarea de laborator nr. 3 Lucrarea de laborator nr. 4 Lucrarea de laborator nr. 5 Lucrarea de laborator nr. 6 Lucrarea de laborator nr. 7 ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ ................................................ 3 6 61 81 10 7 11 4 13 3 14 8

Bibliografie ......................................................................... 15 3

Lucrarea de laborator nr. 1


Tema: Reprezentarea tipurilor de date ale limbajului C++ n memoria calculatorului. Operatorii limbajului C++. Construciile elementare ale limbajului C++ (instruciunile for, while, do-while, if-else, switch-break, goto). Tipuri de date recursive, operaii asupra listelor, arborilor. Construirea i elaborarea programelor recursive. Fiierele. Scopul lucrrii: familiarizarea studenilor cu reprezentarea tipurilor de date ale limbajului C++ n memoria calculatorului, operatorii limbajului C++, construciile elementare ale limbajului C++ (instruciunile for, while, do-while, if-else, switch-break, goto), tipuri de date recursive, operaii asupra listelor, arborilor, construirea i elaborarea programelor recursive, lucrul cu fiierele. Consideraiile teoretice necesare: Tipurile simple i structurate ale limbajului C+ +. Identificatorii limbajului C++ snt formai cu ajutorul caracterelor alfanumerice i caracterul de subliniere _. Primul caracter al unui identificator nu poate fi o cifr. Pi //legal mesaj //legal maxx //legal x3 //legal 3x //ILEGAL n cadrul mulimii identificatorilor posibili, remarcm o clas aparte, reprezentnd cuvintelecheie. Cele mai frecvente cuvintele-cheie ale limbajul C++snt auto delete float interrupt register template break do for long return this 7

case double friend near short typedef char else goto new signed union class enum huge operator sizeof unsigned const export if private static virtual continue extern inline protected struct void default far int public switch while Cuvintele-cheie nu trebuie utilizai ca nume de variabile. Declararea variabilelor trebuie efectuat nainte de a fi folosite, la nceputul programului sau chiar n funcie de contextul problemei n interiorul programului nemijlocit nainte de utilizare, cnd apare necesitarea introducerii variabilei. 0 declaraie specific un tip i este urmat de o list de una sau mai multe variabile de acel tip, ca n exemplul de mai jos: int i,n; char c, linie[80]; Domeniu de aciune a variabilelor. Variabilele pot fi iniializate n momentul declaraiei lor. Dac numele este urmat de semnul egal i de o constant, aceasta servete la iniializare, ca n urmtoarele exemple: char backslash = '\\'; int i = 0; float eps = 1.0e-5; Dac variabila este extern sau static, iniializarea are loc o singur dat, nainte ca programul s-i nceap execuia. Variabilele automate, iniializate explicit, snt iniializate la fiecare apel al funciei n care snt coninute. Variabilele automate pentru care nu exist o iniializare explicit au valoare nedefinit. Variabilele externe i statice se iniializeaz implicit cu zero, dar este un bun stil de programare acela de a efectua iniializarea lor n 8

orice caz. Fiecare variabil i constant posed un tip, care determin dimensiunea spaiului necesar memorrii lor. Tipurile datelor se pot divide n dou categorii: tipuri fundamentale i tipuri derivate sau structurate. Tipurile fundamentale ale limbajului C++ snt char reprezentnd tipul caracter pe 1 octet, int ntreg pe 2 octei long ntreg pe 4 octei nsemnnd un numr real pe 4 octei float, double, ataat unui numr real pe 8 octei Aceste tipuri admit diferite variante, numite tipuri de baz de date. Tipurile enumerabile snt introduse prin sintaxa nume {membrul,membru2, . . . ) varl,var2, . . . ; De exemplu, enum CULORI {ROU , VERDE, ALBASTRU } culoarea_punct, culoare_linie; CULORI culoare_cerc, culoare_fond; definete tipul de dat CULORI i declar variabilele culoarea_punct i culoare_linie urmate de declarrile a nc dou variabile culoare_cerc i culoare_fond de tipul enumerabil. Membrii unui tip enumerabil trebuie s fie numai de tip ntreg. Valoarea fiecruia este obinut prin incrementarea cu 1 a valorii membrului anterior, primul membru avnd, implicit, valoarea 0. Iniializarea unui membru cu o valoare oarecare, avndu-se n vedere c doi membri ai aceluiai tip nu pot avea aceeai valoare. Valorile membrilor urmtori se stabilesc conform regulilor menionate. Exemple de tipuri de date enumerabile: enum ANOTIMP (IARNA=1, PRIMVARA, VARA, TOAMNA) ; 9

ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff enum BOOLEAN {fals, adevrat) condiie; enum DIRECIE {SUS, JOS, DREAPTA, fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff STNGA=5}; Putem defini tipuri enumerabile fr a specifica numele acestora. Procednd astfel, putem grupa un set de constante fr a denumi acea mulime, de exemplu, enum {bine, foarte_bine, cel_mai_bine}; Utilizarea variabilelor de tip enumerabil. Limbajul C++ permite atribuiri de tipul condiie=0; while (! condiie) { cout <<Utilizarea variabilei enumerabile; condiie=true; } Este bine ca astfel de atribuiri s fie nsoii de conversia de tip corespunztoare. condiie=false; condiie=(enum BOOLEAN) 0; Enumerrile, definite n interiorul structurilor limbajului C++, nu snt vizibile n afara acestora. Tipurile structurate snt obinute de la tipurile de baz. Tipurile derivate acceptate de limbajul C++ snt: pointeri, referine, tablouri, structuri, uniuni i clase. Pointerul este o variabil care conine adresa unei alte variabile de orice tip. Pentru a defini un pointer, vom specifica tipul datei a crei adres urmeaz s o memoreze. int *ip; // pointer ctre un ntreg char **s; // pointer la un pointer pe caractere. S considerm o variabil de tip int i i un pointer pi ctre un ntreg. int i=15, j; int *pi=NULL; pi=&i; 10

*pi=20; // i=20; Deoarece operatorul adres & furnizeaz adresa unei variabile, instruciunea pi=&i asigneaz variabilei pi adresa lui i.(de exemplu, adresa 1000). Un alt operator unar ce nsoete clasa pointerilor este * care furnizeaz coninutul locaiei de memorie de pe adresa indicat de ctre pointer, de exemplu, *pi=i; // adic 15; Dac j este un alt int, atunci j=*pi asigneaz lui j coninutul locaiei indicate de pi. Are loc urmtoarea echivalen: j=*pi; adic j=i; Pointerii pot aprea n expresii. De exemplu, dac pi conine adresa lui i, atunci *pi poate aprea n orice context n care ar putea aprea i, cum ar fi j=*pi+l; // adic j=i+l; printf("%d\n",*pi); d=sqrt((double)*pi); n expresii ca j=*pi+1; operatorii unari * i & snt prioritari fa de cei aritmetici, astfel, aceast expresie adun 1 i asigneaz valoarea obinut lui j ori de cte ori pointerul pi avanseaz. Referiri prin pointeri pot aprea i n membrul stng al atribuirilor. Dac pi conine adresa lui i, atunci *pi=0 l pune pe i ca 0, iar *pi+=1 l incrementeaz pe i, ca i (*pi)++. n ultimul exemplu parantezele snt necesare, fr ele se incrementeaz pi n loc s incrementeze ceea ce indic pi, deoarece operatorii unari * i ++ snt evaluai de la dreapta spre stnga. De exemplu, void main() { int *pi, i=10; float *pf, f=12.5; double *pd, d=0.001; char *pc, c=a; *pi=i; *pf=f; *pd=d; *pc=c; 11

printf(pi=%p, pf=%p, pd=%p, pc=%p, pi, pf, pd, pc); printf(*pi=%i, *pf=%f, *pd=%e, *pc=%c, *pi, *pf, *pd, *pc); printf(pi++ =%p, pf++ =%p, pd++ =%p, pc++=%p, pi++, pf++, pd++, pc++); printf((*pi)++ =%p, (*pf)++ =%p, (*pd)++ =%p, (*pc)++ = %p, (*pi)++ , (*pf)++, (*pd)++, (*pc) ++); } Rezultatul ndeplinirii programului: pi=8F1C, pf=0758, pd=074C, pc=1330*pi=10, *pf=12.500000, *pd=1.000000e-03, *pc=a pi++ =8F1C, pf++ =0758, pd++ =074C, pc++=1330(*pi)++ =1B57, (*pf)++ =0000, (*pd)++ =0000, (*pc)++ = 0000 Deoarece pointerii snt variabile, ei pot fi manevrai ca orice alt variabil. Dac pj este un alt pointer la int, atunci pj=pi; copiaz coninutul lui pi n pj, adic pj va indica la variabila adresa creia este indicat n pi, astfel c pj se va modifica odat cu pi. Pointerii pot conine adrese ctre elemente fr tip, cu void. Putem atribui unui pointer void valoarea unui pointer non-void fr a fi necesar o operaie de conversie de tip typecast. char *cp; // pointer ctre un caracter void *vp; // pointer ctre void vp=cp; // legal - pointerul la caracter depus n //pointerul ctre void cp=vp; // ILEGAL - lipsete conversia de tip cp=(char*) vp; // legal - pointerul ctre void depus n pointerul ctre caracter cu conversie de tip. Referina prezint o legtur cu o variabil, coninnd o adres. Spre deosebire de pointeri, n a cror declarare se utilizeaz simbolul *, pentru a defini o referin, vom folosi simbolul &. 12

int i; // declararea unui ntreg int *p=&i; // definirea unui pointer la i int &r=i; // definirea unei referine la i Att p, ct i r acioneaz asupra lui i. i=55; // aciune asupra lui i *p=13; // aciune asupra lui i r=20; // aciune asupra lui i. Exist ns o diferen major ntre p i r, nu numai n modul de apelare, ci i datorit faptului c p poate, la un moment dat, s fie n legtur cu o alt variabil, a crei locaie de memorie o va conine, diferit de cea a lui i, n timp ce r nu-i poate schimba referina, acesta nefiind altceva dect o redenumire a variabilei i. n ceea ce privete utilizarea referinelor, va trebui s inem cont de urmtoarele restricii: referinele trebuie iniializate chiar n momentul declarrii lor, odat fiind iniializate, referinelor nu li se pot schimba locaiile la care se refer, nu snt permise referine la referine i pointeri ctre referine, dar putem avea o referin la un pointer. Referinele pot fi utilizate drept constante, pot fi iniializate cu constante, funcii sau chiar structuri. Tablourile, din rndul crora fac parte vectorii i matricele, snt tipuri de date foarte apropiate pointerilor i referinelor. Vom vedea c orice operaie, care poate fi rezolvat prin indexarea tablourilor, poate fi rezolvat i cu ajutorul pointerilor. Astfel, declaraia char linie[80]; definete linie ca fiind un ir de 80 de caractere i, n acelai timp, linie va constitui un pointer la caracter. Dac pc este un pointer la un caracter, declarat prin char *pc; 13

atunci atribuirea pc=&1inie[0]; face ca pc s indice primul element al tabloului linie (de indice zero). Aceasta nseamn c pc conine adresa lui linie[0]. Acum atribuirea c=*pc; va copia coninutul lui linie[0] n c. Dac pc indic un element al lui linie, atunci, prin definiie, pc+1 indic elementul urmtor i, n general, pc-i indic cu i elemente naintea elementului indicat de pc, iar pc+i cu i elemente dup acelai element. Dac pc indic elementul linie[0], *(pc+1) indic coninutul lui linie[1], pc+i este adresa lui linie[i], iar *(pc+i) este coninutul lui linie[i]. Observm c operatorul de indexare [], de forma E1[E2], este identic cu*((E1)+(E2)). Aceste remarci snt adevrate indiferent de tipul variabilelor din tabloul linie. Definiia adunrii unitii la un pointer i, prin extensie, toat aritmetica pointerilor const, de fapt, n calcularea dimensiunii memoriei ocupate de obiectul indicat. Astfel, n pc+i i este nmulit cu lungimea obiectelor pe care le indic pc, nainte de a fi adunat la pc. Corespondena ntre indexare i aritmetica pointerilor este foarte strns. Referina la un tablou este convertit de ctre compilator ntr-un pointer spre nceputul tabloului. Numele acestui tablou este o expresie de tip pointer. Evalund elementul linie[i], limbajul C++ l convertete n *(linie+i), cele dou forme fiind echivalente. Aplicnd operatorul & ambilor termeni ai acestei echivalene, rezult c linie[i] este identic cu linie+i, unde linie+i fiind adresa elementului i din tabloul linie. Dac pc este un pointer, el poate fi utilizat n expresii cu un indice pc[i] fiind identic cu *(pc+i). Un pointer este o variabil. Deci, pc=linie; // i 14

pc++; snt operaii permise. Numele unui tablou este o constant i nu o variabil, construcii de tipul linie++ fiind interzise. Singurele operaii permise a fi efectuate asupra numelor tablourilor, n afara celor de indexare, snt cele care pot aciona asupra constantelor Aritmetica adreselor pentru pointeri i tablouri constituie unul din punctele forte ale limbajului C++. Se garanteaz c nici un pointer care conine adresa unei date nu va conine valoarea zero, valoare rezervat semnalelor de eveniment anormal. Aceast valoare este atribuit constantei simbolice NULL pentru a indica mai clar c aceasta este o valoare special pentru un pointer. n general, ntregii nu pot fi asignai pointerilor, zero fiind un caz special. Exist situaii n care pointerii pot fi separai. Dac p i q indic elemente ale aceluiai tablou, operatorii <, >, =, etc. lucreaz conform regulilor cunoscute. p<q este adevrat, de exemplu, n cazul n care p indic un element anterior elementului pe care l indic q. Relaiile == i != snt i ele permise. Orice pointer poate fi testat cu NULL, dar nu exist nici o ans n a compara pointeri situai n tablouri diferite. Este valabil i operaia de scdere a pointerilor. Astfel, dac p i q indic elementele aceluiai tablou, p-q este numrul de elemente dintre p i q. O funcie, deosebit de util n lucrul cu iruri de caractere, este strlen(), care returneaz lungimea irului de caractere transmis ca parametru. int strlen(char *s) { char *p=s; while (*p!='\0') p++; return p-s; } Prin declarare p este iniializat cu s i indic primul 15

caracter din s. n cadrul ciclului while este examinat coninutul irului de caractere, indirect, prin intermediul pointerului p, caracter dup caracter, pn cnd se ntlnete '\0'', acesta din urm semnificnd sfritul irului. Dac while ar testa doar dac expresia este zero, este posibil omiterea testului explicit, astfel de cicluri fiind deseori scrise sub forma while (*p) p++; Deoarece p indic irul de caractere, p++ face ca p s avanseze de fiecare dat la caracterul urmtor, iar p-s d numrul de caractere parcurse (lungimea irului). Aritmetica pointerilor este consistent: dac am fi lucrat cu float, care ocup mai mult memorie dect char, i dac p ar fi fost un pointer la float, p++ ar fi avansat la urmtorul float. Toate manipulrile de pointeri iau automat n considerare lungimea obiectului referit. S implementm, de exemplu, o funcie de comparare a dou iruri de caractere. Funcia strcmp(s, t) compar irurile de caractere s i t i returneaz valoare negativ, nul sau pozitiv, n funcie de relaia dintre s i t (care poate fi s<t, s=t sau s>t). Valoarea returnat este obinut prin scderea caracterului de pe prima poziie, unde s difer de t. Pentru claritatea problemei, vom prezenta dou variante, una utiliznd tablourile, iar cea de a doua utiliznd pointerii. Varianta cu tablourile: strcmp(char s[], char t[]) { int i=0; while (s[i]==t[i]) if (s[i++]=='\0') return 0; return s[i]-t[i];} Varianta cu pointerii: strcmp(char *s, char *t) { for(;*s==*t;s++,t++) if(*s=='\0') return(0); 16

return (*s-*t);} Dac ++ i - - snt folosii ca operatori prefixai, pot aprea alte combinaii de *, ++ i - -, dei mai puin frecvente. De exemplu: *++p incrementeaz pe p nainte de a aduce caracterul spre care indic p. *- - p decrementeaz pe p n aceleai condiii. Alte operaii, n afara celor menionate deja (adunarea sau scderea unui pointer cu ntreg, scderea sau compararea a doi pointeri), snt ilegale. Nu este permis adunarea, mprirea, deplasarea logic sau adunarea unui float sau double la pointer. Tablourile multidimensionale pot fi definite cu ajutorul tablourilor de tablouri, de exemplu.: char ecran [25][80]; excepie fcnd tablourile de referine, acestea din urm nefiind permise datorit faptului c nu snt permii pointerii la referine. Dac E este un tablou n-dimensional de dimensiuni i, j, ..., k, atunci apariiile lui E n expresii snt convertite n pointer la un tablou n-1-dimensional de dimensiuni j, ..., k. Dac la acesta se aplic explicit sau implicit (prin indexare) operatorul *, rezultatul este tabloul n-1-dimensional indicat de pointer, care, la rndul su, este convertit imediat n pointer. Tablourile snt memorate pe linii i, deci, ultimii indici, de la stnga la dreapta, variaz mai repede dect primii. Prima dimensiune a unui tablou se folosete numai pentru a determina spaiul ocupat de acesta, ea nefiind luat n consideraie dect la determinarea unui element de indici dai. Este permis omiterea primei dimensiuni a unui tablou, dac tabloul este extern, alocarea fcndu-se n cadrul altui modul sau cnd se efectueaz iniializarea tabloului n declaraie, n acest caz fiind determinat dimensiunea din numrul de elemente iniializate. 17

Iniializarea tablourilor poate avea loc chiar n cadrul declarrii acestora int point[2]={10,19}; char mesaj1[6]={'S1,'a','l','u','t','\0'}; char mesaj2[6]="Salut"; Observm c irurile de caractere se comport oarecum ciudat. Att mesaj1, ct i mesaj2 snt iruri de 6 caractere avnd drept terminator de ir caracterul nul. Diferena ntre cele dou iruri nu se afl n coninutul lor, ci n cadrul iniializrii lor. n cazul iniializrii prin acolade, { }, caracterul nul nu este subneles, prezena acestuia rmnnd la latitudinea noastr, n schimb, adoptnd o iniializare prin ghilimele, , va trebui s dimensionm corespunztor irului de caractere, innd cont de prezena terminatorului de ir. n exemplul mesaj2 avem 5 litere plus caracterul nul, fiind necesare 6 locaii n vederea memorrii cuvntului Salut. Dimensionarea tablourilor se realizeaz n concordan cu necesitile aplicaiei. Exist posibilitatea iniializrilor pariale, care nu utilizeaz ntreg spaiu rezervat. n cazul irurilor de caractere, restul spaiului rmas neutilizat va conine numai caracterul nul. n restul situaiilor, coninutul tabloului fiind aleator, se recomand iniializarea acestuia n cadrul unui ciclu. Tablouri de pointeri. Pointerii snt ei nii variabile, de aceea ei snt utilizai n tablouri de pointeri. Pentru exemplificare, vom considera un program care sorteaz un set de linii de text n ordine alfabetic. Cu toate c algoritmul de sortare este unul comun, deosebirea dintre sortarea unui tablou de numere i a unuia de iruri de caractere const n aceea c liniile de text de lungimi diferite nu pot fi comparate sau deplasate printr-o singur operaie. Avem nevoie de o 18

reprezentare a datelor care s se poat face eficient i potrivit regulilor de gestionare a liniilor de text de lungimi diferite. Introducem noiunea de tablou de pointeri. Dac liniile de sortare snt memorate cap la cap ntr-un ir de caractere, atunci fiecare linie poate fi accesat printrun pointer la primul su caracter. Pointerii nii pot fi memorai ntr-un tablou. Dou linii pot fi comparate prin transmiterea pointerilor respectivi lui strcmp(). Cnd dou linii neordonate trebuie inversate, se inverseaz pointerii lor n tabelul de pointeri, nu nsi liniile. Acest mod de lucru elimin cuplul de probleme legate de gestionarea memoriei i poate deplasa liniile. Procesul de sortare const din trei etape: - citirea tuturor liniilor la intrare, - sortarea liniilor, - tiprirea liniilor n ordine. mprim programul n funcii care efectueaz aceste trei etape. Funcia de intrare trebuie s colecteze i s salveze caracterele din fiecare linie i s construiasc un tablou de pointeri pe linii. Va trebui s numere liniile la intrare. Aceast informaie este necesar pentru sortare i tiprire. Deoarece funcia de intrare poate opera doar cu un numr finit de linii, ea va returna o valoare, cum ar fi -1, n cazul n care se vor prezenta mai multe linii. Funcia de ieire trebuie doar s tipreasc liniile n ordinea n care apar n tabloul de pointeri. #include <stdio.h> #include <string.h> #include <conio.h> #define LINII 100 #define MAXLEN 1000 int citeste_linii(char *s[]) 19

{ printf("Introdu un text (maxlen=1000)\n"); printf("Pentru introducerea unei linii noi se utilizeaz tasta ENTER.\n"); printf("Sfritul textului se va marca prin '*'. \n\n"); char c; int i=0, j=0; while((c=getchar())!='*') { if (c=='\n') { s[i][j]='\0'; i++;j=0; } else s[i][j++]=c; } return i+1; } void scrie_linii (char *linieptr[],int maxlinii) { for( int i=0; i<maxlinii;i++) printf("%s\n", linieptr[i]); } void sortare_linii(char *v[],int n) { char *temp; for(int k=n/2;k>0;k/=2) for(int i=k;i<n;i++) for(int j=i-k;j>=0;j-=k) { if (strcmp(v[j],v[j+k])<=0) break; temp=v[j]; v[j]=v[j+k]; v[j+k]=temp; } } void main() { clrscr(); char *linieptr[LINII]; int nlinii; if ((nlinii=citeste_linii(linieptr))>=0) { printf("\n Textul pn la sortare:\n"); scrie_linii(linieptr,nlinii); sortare_linii(linieptr,nlinii); printf("\n Textul dup sortare \n"); scrie_linii(linieptr,nlinii); 20

} else printf("Input prea mare pentru sortare \n"); } Rezultatul ndeplinirii programului: Introdu un text (maxlen=1000) Pentru introducerea unei linii noi se utilizeaz tasta ENTER. Sfrsitul textului se va marca prin '*'. ashdgasghddgjahsdgjaghdjhagsdhgdhjgdh* Textul pn la sortare: ashdgasghddgjahsdgjaghdjhagsdhgdhjgdh va marca prin '*'. Textul dup sortare ashdgasghddgjahsdgjaghdjhagsdhgdhjgdh va marca prin '*'. Declararea variabilei linieptr: char *linieptr[LINII]; arat c linieptr este un tablou de LINII elemente, fiecare element fiind un pointer la char. linieptr[i] este un pointer la caractere, iar *linieptr[i] acceseaz un caracter. Dac linieptr este el nsui un tablou care este transmis lui scrie_linii(), el poate fi tratat ca un pointer, iar funcia poate fi scris. void scrie_linii(char *linieptr[],int nlinii) { while (- - nlinii>=0) printf("%s\n",*linieptr++); } *linieptr adreseaz iniial prima linie, iar, cu fiecare incrementare, el avanseaz linia urmtoare pn cnd nlinii se epuizeaz. Sortarea are loc n cadrul funciei sortare_linii(). Dac orice element individual din v este un pointer la caractere, temp va fi un astfel de pointer, nct cei doi pot fi copiai unul n altul. Fiind date declaraiile int a[10][10]; int *b[10]; utilizrile lui a i b pot fi similare, n sensul c a[5][5] i 21

b[5][5] snt referine legale ale aceluiai int. Toate cele 100 celule de memorie ale tabloului a trebuie alocate, iar gsirea fiecrui element se face prin calculul obinuit al indicelui. Pentru tabloul b, prin declararea sa, se aloc 10 pointeri, fiecare dintre acetia urmnd s indice un tablou de ntregi. Presupunnd c fiecare indic la 10 elemente din tablou, vom obine 100 celule de memorie rezervate, plus cele 10 celule pentru pointeri. Astfel, tabloul de pointeri utilizeaz mai mult spaiu i poate cere un mod explicit de iniializare. Dar exist dou avantaje: accesarea unui element se face indirect prin intermediul unui pointer, n loc s se fac prin nmulire i adunare (cum este n cazul tabloului multidimensional), iar liniile tabloului pot fi de lungimi diferite. Aceasta nseamn c nu orice element al lui b este constrns s indice la un vector de 10 elemente, unii pot indica la cte 2 elemente, alii la cte 20 de elemente sau chiar la nici unul. Structura este o colecie de elemente de tipuri diferite i care pot fi referii att separat, ct i mpreun. Definirea unei structuri se realizeaz cu ajutorul cuvntului-cheie struct. Ea are urmtoarea sintax: struct [tip structur] { tip_1 element_1; tip_ n element_n } obiect_ de_ tip_ structur; unde tip structur descrie organizarea structurii, tip1,..., tipn indic tipul elementelor structurii, element1, ..., element sunt numele elementelor structurii, obiect_ de_ tip_ structur este una sau o list de variabile pentru care se aloc memorie. De exemplu, struct punct { float x,y;} p; S-a definit o structur p ca fiind de tip punct, punctul fiind compus din dou elemente x i y reale. Asupra 22

componentelor unei structuri putem aciona prin intermediul operatorului de apartenen, ., de exemplu: p.x=10; p.y=30; Exist posibilitatea efecturii operaiilor cu ntreaga structur, atribuirea fiind, de exemplu : p={10,30); 0 declaraie de structur care nu este urmat de o list de variabile nu produce alocarea memoriei, ci descrie organizarea structurii., de exemplu: typedef struct { char name[25]; int id, age; char prp; } student; Definirea unei structuri permite determinarea unui nou tip de date. n continuare definind pointeri la aceast structur, tablouri, ale cror elemente snt de tipul acestei structuri, i elemente de acest tip pot fi definite noi structuri de date mai compuse. Un alt aspect al utilitii structurilor l constituie tratarea tablourilor de structuri, de exemplu: punct hexagon[6]; punct octogon[8]; Accesul ctre membrii componeni ai fiecrui element al vectorului se realizeaz prin combinarea accesului indexat, caracteristic tablourilor, cu cel utilizat n cazul structurilor: hexagon[i].x=10; n cazul definirii unui pointer la o structur, accesul la componentele acelei structuri se va efectua prin expresii de forma punct *pptr; pptr->x=10; // Echivalent cu p.x=10; (*pptr) .y=30; // Echivalent cu p.y=30; Parantezele au rolul de a indica ordinea n care acioneaz cei doi operatori * i ., prioritar fiind *. Unele elemente ale unei structuri pot fi cmpuri de bii. Un cmp de bii este o configuraie de bii adiaceni, ce 23

apar ntr-un element de tip int. Cmpurile snt declarate de tip unsigned, iar numele cmpului este urmat de dou puncte : i un numr ce reprezint numrul de bii ocupai de cmpul respectiv: unsigned nume_cmp:nr_bii; Cmpurile pot fi accesate ca orice alt element de structur. Orice cmp trebuie s aib toi biii n interiorul unei zone de tip int (nu poate avea bii n dou cuvinte diferite). Ordinea de alocare a memoriei pentru cmpuri este dependent de sistem. Unele sisteme fac alocarea de la stnga la dreapta, iar altele invers. Nu se pot utiliza tablouri de cmpuri. Cmpurile nu au adres i nu li se poate aplica operatorul de adresare &. Un caz special de structuri l constituie union. Acestea snt structuri alternative pentru care dimensiunea spaiului necesar memorrii lor este egal cu cea mai mare dimensiune necesar memorrii unei componente a acelei structuri. De exemplu, variabila este de tipul union un_tip { int uint; float ufloat; char uchar; punct upunct; // upunct este de tipul structurii punct } variabila; Toate componentele uniunii ocup aceeai zon n cadrul memoriei. Spre deosebire de structuri, n uniune este accesibil o singur component a unei uniuni. Uniunea se definete n aceeai manier ca i structurile, cuvntul-cheie utilizat fiind union. Operatorii Operatori i expresii. Aciunile desfurate n cadrul oricrui program, n marea lor majoritate, se efectueaz prin expresiile formate prin combinaii de date i operatori. Limbajul C++ posed toi operatorii limbajului C i completeaz aceast list cu operatori 24

proprii. Din lista operatorilor disponibili ai limbajului C++ indicm operatorii caracteristici lui new pentru alocarea memoriei, delete pentru eliberarea memoriei alocate cu operatorul new, :: operatorul de scop sau de rezoliie. n funcie de numrul de operanzi, operatorii se pot clasifica n trei categorii: operatori unari, binari i ternari. Operatori unari. Formarea expresiilor n care intervin operatorii unari se produce de la dreapta la stnga. Operatorul de indirectare: * se poate aplica unei expresii de tip pointer (*expresie) i are drept rezultat o valoare (lvalue sau adres) care se refer la obiectul indicat de pointer. Operatorul de adresare: & poate fi aplicat unei valori (&lvalue) i are ca rezultat un pointer la obiectul definit de lvalue i are acelai tip ca i tipul lvalue. Operatorul unar minus: - se aplic unei expresii (expresie) n vederea inversrii semnului acesteia. Operatorul negaie logic: ! se poate aplica unei expresii aritmetice sau unui pointer (! expresie) i are ca rezultat 1, dac valoarea operandului este 0, i 0, n caz contrar, tipul rezultatului fiind int. Operatorul negaie pe bii: ~ se aplic unei expresii de tip ntreg (~expresie) i transform 0 n 1 i 1 n 0 n toi biii rezultai dup conversiile uzuale. Operatorul de incrementare: ++ incrementeaz cu 1 valoarea operandului. Operatorul de decrementare: - - decrementeaz cu 1 valoarea operandului. Operatorul ++, ca i operatorul - -, poate fi utilizat att ca prefix, ct i ca sufix. n cazul utilizrii lor ca prefix, nti se acioneaz cu operatorul asupra valorii operandului i apoi se utilizeaz noua valoare a acestuia. n cazul utilizrii lor ca sufix, nti se utilizeaz valoarea acestuia, apoi se acioneaz cu 25

operatorul asupra valorii operandului. Conversia unei expresii (typecast): este de tipul (tip) expresie sau (expresia) i produce conversia valorii expresiei la tipul specificat. Operatorul dimensiune: sizeof este de tipul sizeof (expresie) sau sizeof (tip) i ne indic dimensiunea n octei a operandului, determinat din declaraiile elementelor ce apar n expresie. Operatorul de alocare a memoriei: new apare sub forma pointer_la_nume = new nume [ iniializator] i ncearc s creeze un obiect nume prin alocarea unui numr egal cu sizeof(nume) de octei n memoria heap, adresa acestuia fiind returnat. n cazul n care alocarea nu este efectuat cu succes, se returneaz valoarea NULL Operatorul de eliberare a memoriei: delete are sintax de forma delete pointer_la_nume i elibereaz memoria alocat ncepnd de la adresa coninut de pointer_la_nume. Operatorul virgul: , produce expresii de forma expresie, expresie; El efectueaz evaluarea expresiilor de la stnga la dreapta i are ca rezultat i tip valoarea i tipul ultimei expresii. Gruparea cu paranteze este permis i produce o singur valoare. De exemplu: void main() {int s; for(int i=0,s=0;i<10,i++) s+=I; cout<< Suma este de <<s<<endl; } Operatori binari Operatorii aritmetici: +, -, *, / acioneaz respectnd regulile binecunoscute de calculare a expresiilor. Trebuie fcut o observaie asupra operatorului de mprire /. n cazul n care ambii operanzi snt ntregi, rezultatul
26

26

este ntreg (prin trunchierea rezultatului real). Operatorul modulo: % furnizeaz restul mpririi primului operand la cel de al doilea. De exemplu, un numr este par, dac este divizibil cu 2. Deci if (x%2==0) cout "x este par"; else cout "x este impar"; Operatorul de deplasare la stnga: are ca rezultat deplasarea ctre stnga a valorii operandului stng cu un numr de bii egal cu valoarea operandului drept, biii eliberai astfel fiind completai cu valoarea 0. Operatorul de deplasare la dreapta: acioneaz n mod similar cu precedentul, singurul element care difer fa de operatorul anterior fiind sensul deplasrii. De exemplu, funcia definit mai jos Biti (x,4,3) returneaz 3 bii din poziiile 4,3 i 2, aliniai la dreapta. Biti (unsigned x,unsigned p,unsigned n) {return (x(p+1-n))&~ (~0n) ; } Operatorii de comparaie: <, <=, >, >=, = =(egal), !=(neegal) au ca rezultat o valoare de tip int care este 0 n cazul n care condiia nu este ndeplinit i 1 n caz contrar. Pointerii pot fi comparai numai pe aceeai structur de date, iar ultimii doi operatori permit compararea pointerului cu NULL, care corespunde adresei vide. Operatorii logici binari pe bii: & (i), | (sau),^ (sau exclusiv) furnizeaz un rezultat de tip int (0 pentru valoarea false i 1 pentru valoarea true). De exemplu, funcia numr_biti() controleaz numrul de bii pe 1 dintr-un argument ntreg numr_biti (unsigned n) { int b; for (b=0; n!=0; n=1) if (n&O1) b++; return b; /} 27

Operatorii logici binari: && (i), || (or). Pentru ambii operatori se efectueaz evaluri de la stnga spre dreapta pn la prima expresie de valoare 0 (pentru &&) sau, respectiv, nenul (pentru || ), cnd valoarea ntregii expresii devine 0 i, respectiv, 1. Operatorii de atribuire: op= unde op face parte din mulimea { +, -, *, /, %, , , &, ^, |} se grupeaz de la dreapta la stnga, tipul expresiei de atribuire este tipul operandului stng, iar rezultatul aciunii operatorului se regsete tot n acest operand. Orice expresie de tipul x op= y este echivalent cu x =x op y. Operatori ternari Operatorul condiional: (condiie) ? : produce expresii de forma (expr1) ? expr2 : expr3, n care se evalueaz exp1. n cazul n care aceasta este nenul, se returneaz valoarea expresiei expr2, n caz contrar, se returneaz valoarea lui expr3. De exemplu, ciclul urmtor tiprete N elemente ale unui tablou, 10 pe linie, cu fiecare coloan separat printr-un blanc i cu fiecare linie (inclusiv ultima) terminat cu un singur caracter \n linie nou: for (i=0;i<N;i++) printf("%6d %c",a[i],(i%10==9||i==N-1)?-\n':' ); Acest exemplu poate fi scris prin intermediul instruciunii if n felul urmtor: for (i=0;i<N;i++) if,(i%10==9||i==N-1) printf("%6d %c",a[i],-\n'); else printf("%6d %c",a[i],' ); Instruciuni Expresiile snt utilizate n scrierea instruciunilor. O instruciune este o expresie care se ncheie cu punct i virgul ;". Instruciunile pot fi scrise pe mai multe linii program, spaiile 28

nesemnificative fiind ignorate. Pe o linie de program putem scrie multe instruciuni. Instruciunile pot aprea n diferite forme: de atribuiri, de declaraii, instruciuni condiionale, de ciclare, de salt, instruciuni compuse. Instruciunea compus (blocul de instruciuni) grupeaz declaraii i instruciuni n vederea utilizrii unui bloc de instruciuni echivalent cu o instruciune compus. Forma general este: {lista_declaraii lista_instruciuni} Instruciunea condiional if, if-else are una din formele: if (expr) instruciune; if (expr) instruciune_l; else instruciune_2; Instruciunea if evalueaz expresia expr. n cazul n care se obine o valoare nenul, se trece la executarea instruciune_1, iar dac aceast valoare este nul i exist instruciune_2, se va executa instruciune_2. n cazul absenei variantei else se va trece la execuia instruciunii imediat urmtoare instruciunii if. Instruciunea de ciclu condiionat anterior while este while (expr) instruciune; Att timp ct valoarea expresiei expr este nenul, se execut instruciune. De exemplu, i=0; while (i<n) a[i++]=0.0; Evaluarea expresiei are loc naintea execuiei instruciunii, fapt pentru care aceast instruciune este din clasa ciclurilor cu precondiie. Din acest motiv este posibil ca corpul ciclului s nu se execute nici mcar o dat, dac condiia ciclului este fals. Execuia programului va trece la instruciunea imediat urmtoare instruciunii de ciclu. Instruciunea de ciclu condiionat 29

posterior do-while are forma do instruciune while (expr); instruciune se va executa pn ce valoarea expresiei expr devine fals. Spre deosebire de instruciunea while, n ciclul do-while evaluarea expresiei are loc dup fiecare executare a corpului ciclului. Datorit acestui fapt instruciune se va executa cel puin o singur dat, iar instruciunea do se ncadreaz n categoria ciclurilor cu postcondiie. De exemplu: i=0; do { a[i++]=0.0; } while (i<n); Ciclul do-while este folosit mai puin dect ciclul for. Cu toate acestea, prezena sa se impune n cazurile n care este necesar executarea corpului unui ciclu cel puin o dat, urmnd ca ulterior s se execute n funcie de ndeplinirea condiiei finale, indicate n contextul ciclului while. Instruciunea de ciclu aritmetic for are urmtoarea form general for (expr_1; expr_2; expr_3) instruciune; i este echivalent cu urmtoarea succesiune de instruciuni: expr_1; while (expr_2) ( instruciune; expr_3; ) Oricare dintre cele trei expresii poate lipsi, absena expresiei expr_2 fiind nlocuit, implicit, cu valoarea 1. De exemplu, for (int i=0; i<=n; i++) a[i]=0.0; for ( int k=0, number_of_nums=0, number_of_chars=0; k<strlen(text); k++) { cout text[k] \n; 30

if (is_num(text[k])) number_of_nums++; if (is_alpha(text[k])) number_of_chars++; } Ciclul for este util de folosit atunci cnd exist o simpl iniializare i reiniializare, deoarece se pstreaz instruciunile de control al ciclului mpreun. Instruciunea switch face parte din categoria instruciunilor de selectare. Transferul controlului se va efectua la una din variantele posibile, n funcie de valoarea unei expresii de control. Sintaxa instruciunii este switch (expr) instruciune; unde instruciune este o instruciune compus, n care fiecare instruciune individual trebuie etichetat cu o etichet de forma case expresie_constanta: unde expresie_constanta trebuie s fie de tip int i nu pot fi dou etichete egale n aceeai instruciune switch. Cel mult o instruciune poate fi etichetat cu default: La execuia unei instruciuni switch se evalueaz expresia expr i se compar valoarea obinut cu fiecare constant ce apare n etichetele asociate instruciunii. Dac se gsete o astfel de constant, controlul este dat instruciunii ce urmeaz ei, n caz contrar, controlul fiind transferat la instruciunea de dup eticheta default, dac aceasta exist, sau instruciunii imediat urmtoare instruciunii switch. Pentru a se meniona sfritul unei instruciuni ataate unui caz, se va utiliza una dintre instruciunile goto, break sau return. La nceputul unei instruciuni switch pot aprea declaraii, dar nu se vor efectua iniializri ale variabilelor de tip auto sau register. De exemplu, switch (text[k]) { case A : numr_caractere++; break; 31

case B: numr_caractere++; break; // se vor completa toate cazurile posibile case 'Z' : numr_caractere++; break; case ' a : numr_caractere++; break; // se vor completa toate cazurile posibile case z: numr_caractere++; break; case 0: :numr cifre++; // se vor completa toate cazurile posibile case '9':numr cifre++; } Instruciunea break are forma break; Are ca efect terminarea execuiei unui ciclu de tip while, do-while, for sau switch, controlul fiind transferat primei instruciuni din corpul blocului cel mai interior. Instruciunea continue are forma continue; Are drept efect trecerea controlului urmtorului ciclu ntr-o instruciune de tip while sau for n care apare i nu are nici un efect dac nu apare n corpul unor astfel de instruciuni. Cnd este ntlnit, ea se trece la urmtoarea iteraie a ciclului (while, for, do-while). n cazul lui while i do-while, aceasta nseamn c partea de control se execut imediat. n cazul ciclului for, controlul va trece la faza de reiniializare. De obicei, instruciunea continue nu se va aplica instruciunii switch. Ca exemplu, fragmentul urmtor sumeaz numai elementele pozitive dintr-un tablou a, n care valorile negative snt omise. int s; for (int i=0,s=0; i<N; i++) { if (a[i]<0) continue; //sare indicii elementelor negative s+=a[I]; } Instruciunea return admite urmtoarele dou forme return; sau return (expr); cea din urm fiind echivalent cu urmtoarea 32

return expr; Efectul instruciunii return este trecerea controlului la funcia care a apelat funcia respectiv fr transmiterea unei valori n prima variant sau cu transmiterea unei valori n ultimele -dou variante. Instruciunea goto i etichete. Limbajul C++ ofer instruciunea goto pentru ramificare. Formal, instruciunea goto nu este necesar i uor se poate scrie programe fr ea. Cu toate acestea, exist cteva situaii n care goto i poate gsi locul. Cea mai obinuit folosire este aceea de a abandona prelucrarea n anumite structuri puternic imbricate, de exemplu, de a iei afar din dou cicluri deodat, instruciunea break nu poate fi folosit, deoarece ea prsete numai ciclul cel mai din interior. Astfel: for(...) for(...) ( ... if(dezastru) goto error;} error:; O posibilitate de cutare a primului element negativ ntr-un tablou bidimensional ar fi: for (i=0; i<N; i++) for(j=0; j<M; j++) if (v[i][j]<0) goto found; found: // s-a gsit n poziia i, j Programul cu un goto poate fi scris ntotdeauna fr goto, chiar dac preul pentru aceasta este o variabil suplimentar sau nite controluri repetate. De exemplu, cutarea n tablou devine: found=0; for (i=0; i<N && found; i++) for (j=0; j<M && found; j++) found = v[i][j]<0; 32 if (found) { .. } // a fost gsit la i-1, j-1 else {..} // nu s-a gsit 33

Instruciunea vid are forma ; i este utilizat pentru a evita existena unei etichete chiar n faa unei acolade de nchidere a unui bloc sau n cazul n care corpul unui ciclu nu conine nici o instruciune. Tipuri de date recursive, operaii asupra listelor, arborilor. Listele simplu i dublu lnuite, arborii snt formate din elemente definite de structuri cu autoreferire. Ele snt consecutiviti de elemente de acelai tip, numrul crora se schimb dinamic n procesul de executare a programului. Lista liniar F, care const din elemente D1, D2,...,Dn, grafic se poate reprezenta n modul urmtor: D1 D2 D3 ... Dn Asupra elementelor listelor se pot efectua urmtoarele operaii: - de cutare a elementului dup criteriul dat; - de determinare a primului element n lista liniar; - insertarea unui element nou nainte sau dup o component indicat a listei liniare; - eliminarea unui element din list; - sortarea componentelor listei. Metodele de stocare a listelor liniare se divid n metode consecutive i stocare lnuit. Elementele listei liniare, utilizate de metodele consecutive, se aloc ntr-un tablou d de dimensiune fix, de exemplu, 100, i lungimea listei este indicat de variabila l, adic se declar float d[100]; int l; Dimensiunea 100 mrginete dimensiunea maximal a listei liniare. Lista F n tabloul d se formeaz n modul urmtor: 34

d[0]=7; d[1]=10; l=2; Lista obinut se pstreaz n memorie n conformitate cu schema: l: 2 d 7 10 ... : [0] [1] [2] [3] [98] [99] Pentru organizarea elementelor n form de list simplu lnuit, se utilizeaz structurile care snt legate cte o component n lan, nceputul creia (prima structur) este indicat de pointerul dl. Structura care definete elementul listei conine n afar de componenta informaional i un pointer la urmtorul element din list. Descrierea acestui tip de structur cu autoreferire i pointerul n cauz se face n modul urmtor: typedef struct nod // structura cu autoreferire {float val; // valoarea componentei informaionale struct nod *urm ; // pointerul la urmtorul element din lan } DL; DL *p; // pointerul la elementul curent DL *prim; // pointerul la nceputul listei Pentru alocarea memoriei elementelor listei n C++, se utilizeaz operatorul de alocare: new care apare sub forma pointer_la_nume = new nume [ iniializator]; care ncearc s creeze un obiect nume prin alocarea unui numr egal cu sizeof(nume) de octei n memoria heap, adresa acestuia este returnat i asignat variabilei pointer_la_nume. n cazul n care alocarea nu este efectuat cu succes, se returneaz valoarea NULL. Operatorul de eliberare delete este apelat printr-o 35

instruciune de forma delete pointer_la_nume ; elibereaz memoria alocat ncepnd cu adresa coninut de pointer_la_nume. De exemplu, p=new(DL); p->val=10; p->n=NULL; dl=new(DL)); dl->val=7; dl->n=p; n ultimul element al listei pointerul la elementul vecin are valoarea NULL. Lista are urmtoarea form:

Operaii asupra listelor simplu lnuite Fiecare element al listei simplu lnuite reprezint o structur alctuit din dou componente: val folosit pentru componenta informaional i p pentru pointer la urmtorul element din lista lnuit. Pointerul dl indic adresa de alocare pentru primul element al listei. Pentru toate operaiile asupra listei se va utiliza urmtoarea descriere a structurii elementelor liste: typedef struct nod { float val; struct nod * urm; } NOD; int i,j; NOD * prim, * r, * p; Pentru executarea operaiilor pot fi utilizate urmtoarele fragmente de program: 1) formarea listei simplu lnuite: float x=5; int n=1; 36

p=new(nod); r=p; p->val=x; p->urm=NULL; prim=p; while (p->val !=0) { p=new(nod); n++; p->val=x-1.0*n; r->urm=p; p->urm=NULL; r=p; } 2) tiparul elementului j: r=prim;j=2; while(r!=NULL && j<n-1) { if (r==NULL) printf("\n nu este elementul %d ",j); else printf("\n elementul %d este egal cu %f ",j++,r->val); r=r->urm; } 3) tiparul ambilor vecini ai elementului determinat de pointerul p :

p=prim; if((r=p->urm)==NULL) printf("\n nu are vecin din dreapta"); else printf("\n vecinul din dreapta este %f", r->val); if(prim==p) printf("\n nu are vecin din stnga" ); else { r=prim; while( r->urm!=p ) r=r->urm; printf("\n vecinul de stnga este %f", r->val); } 4) eliminarea elementului care este succesorul elementului n cauz, la care indic pointerul

p=prim; 37

if ((r=p->urm)==NULL) printf("\n nu este succesorul "); p->urm=r->urm; delete(r->urm); 5) insertarea noului element cu valoarea newval=100 dup elementul determinat de pointerul p:

r=new(NOD); r->urm=p->urm; r->val=100; p->urm=r; Organizarea listelor dublu lnuite Lista dublu lnuit este o list n care fiecare element conine doi pointeri: unul la precedentul element, altul la succesorul element din list. Lista dublu lnuit n program se poate determina cu ajutorul urmtoarelor descrieri: typedef struct ndd { float val; // valoarea informaional a componentei struct ndd * succesor; // pointer la succesorul element al //listei n struct ndd *precedent; // pointer la precedentul element al //listei m } NDD; NDD * prim, * p, * r; Interpretarea grafic a listei F=< 2,5,7,1 > ca list dublu lnuit este urmtoarea:

Insertarea noului element cu valoarea newval dup elementul determinat de pointerul p, se efectueaz de operatorii r=new(NDD); r->val=newval; 38

r->succesor=p->succesor; (p->succesor)->precedent=r; p->=r; Eliminarea elementului urmat de elementul la care indic pointerul p se efectueaz n modul urmtor: p->succesor=r; p->succesor=(p->succesor)->succesor; ( (p->succesor)->succesor )->precedent=p; delete r; Lista liniar este ciclic, dac ultimul element al listei indic la primul element, iar pointerul dl indic la ultimul element al listei. Schema listei ciclice pentru lista F=< 2,5,7,1 > este urmtoarea:

La rezolvarea problemelor pot aprea diferite tipuri de liste lnuite. Stiv i coad n funcie de metoda de acces la elementele listei liniare, pot fi cercetate urmtoarele tipuri de liste liniare: stive, cozi i cozi de tip vagon. Stiva este o consecutivitate de elemente de acelai tip variabile scalare, tablouri, structuri sau uniuni. Stiva reprezint o structur dinamic, numrul de elemente a creia variaz. Dac stiva n-are elemente, ea este vid. Asupra elementelor stivei pot fi efectuate urmtoarele operaii: - verificarea dac stiva este vid, - includerea unui element nou n vrful stivei; - eliminarea elementului din vrful stivei; - accesarea elementului din vrful stivei, dac stiva nu este vid. Astfel, operaia de includere i eliminare a 39

elementului, de asemenea, accesarea elementului are loc numai asupra elementului din vrful stivei. Coada este o list liniar n care elementele listei se elimin din capul listei i elementele noi se includ prin coada listei. Coad de tip vagon este o list liniar n care includerea i eliminarea elementelor din list se efectueaz din ambele capete (vrful i sfritul) ale listei. Stiva i coada se organizeaz att static prin intermediul tabloului, ct i dinamic prin list (simplu sau dublu lnuit). Vom cerceta cum se utilizeaz lista n form de stiv pentru implementarea calculrii expresiei aritmetice n form invers polonez. n astfel de mod de prezentare a expresiei operaiile se nregistreaz n ordinea executrii lor, iar operanzii se afl nemijlocit n faa operaiei. De exemplu, expresia (6+8)*5-6/2 n forma invers polonez are forma: 6 8 + 5*62/Utiliznd noiunea de stiv, expresia aritmetic n form invers polonez se execut print-o singur trecere de examinare a expresiei. Fiecare numr se introduce n stiv, iar operaia se execut asupra urmtoarelor dou elemente din vrful stivei, nlocuindu-le cu rezultatul operaiei efectuate. Dinamica schimbrilor din stiv va fi urmtoarea: S = < >; <6>; <6,8>; <14>; <14,5>; <70>; <70,6>; <70,6,2>; <70,3>; <67>. Mai jos este descris funcia eval, care calculeaz valoarea expresiei indicate n tabloul m n form de expresie invers polonez, m[i]>0 indic numrul nenegativ, iar valoarea m[i]<0 - operaia. n calitate de coduri pentru operaiile de adunare, scdere, nmulire i mprire se aleg numerele: -1, -2, -3, -4. 40

Pentru organizarea stivei se utilizeaz tabloul interior stack. Parametrii funciei snt tabloul de intrare m i lungimea sa l. float eval (float *m, int l) { int p,n; float stack[50],c; for(int i=0; i < l ;i++) if ((n=m[i])<0) { c=st[p--]; switch(n) { case -1: stack[p]+=c; break; case -2: stack[p]-=c; break; case -3: stack[p]*=c; break; case -4: stack[p]/=c; } } else stack[++p]=n; return(stack[p]); } Arbori Arborii snt structuri de date dinamice, cu autoreferire. Prin arbore se nelege o mulime finit i nevid de elemente (noduri): A={A1, A2,..., An}, n>0 cu urmtoarele proprieti: exist un nod i numai unul care se numete rdcina arborelui, celelalte noduri formeaz submulimi ale lui A, care formeaz fiecare cte un arbore, arborii respectivi se numesc subarbori ai rdcinii. ntr-un arbore exist noduri crora nu le corespund subarbori. Astfel de noduri se numesc terminale. n multe aplicaii se utilizeaz noiunea de arbori binari. Dac mulimea de elemente a arborelui binar este vid, se consider c arborele const numai din rdcin. Dac mulimea de elemente este nevid, arborele binar se divide n dou submulimi: 41

subarborele drept i cel de stnga. Arborele binar este ordonat, deoarece n fiecare nod subarborele stng se consider c precede subarborele drept. Un nod al unui arbore binar poate s aib numai un descendent: subarborele drept sau subarborele stng. De exemplu, un nod al unui arbore binar poate fi o structur care poate fi definit n felul urmtor: typedef struct tnod { int nr, int f; //declaraii struct tnod *st; // este pointerul spre subarborele stng al //nodului curent struct tnod *dr; // este pointerul spre subarborele drept al //nodului curent } TNOD; Asupra arborilor binari pot fi definite urmtoarele operaii: afiarea componentelor informaionale ale nodului, specificarea criteriului de determinare a poziiei n care s se inserteze n arbore nodul curent; determinarea echivalenei a doi arbori; insertarea unui nod terminal ntr-un arbore binar; accesarea unui nod al arborelui, parcurgerea unui arbore; tergerea unui arbore. Afiarea componentelor informaionale ale nodului se poate de efectuat prin funcia: void prelucrare (TNOD *p) {printf(numrul = %d apariii= %d \n, p->nr, p->f);} Criteriul de determinare a poziiei, n care s se inserteze n arbore nodul curent, se definete de funcia: int criteriu(TNOD *p, *q) { if (q->nr < p -> nr ) return 1; // insertarea nodului curent 42

//n subarborele stng al nodului spre care indic //pointerul p if (q->nr > p-> nr ) return 1; // insertarea nodului curent //n subarborele drept al nodului spre care indic //pointerul p } Insertarea unui nod terminal ntr-un arbore binar poate fi efectuat prin urmtoarea funcie: TNOD* insert_nod() { TNOD *parb, *p, *q; int n=sizeof(TNOD); if (parb ==0) { parb=p; return p; } int i; q=parb; for(;;) if ((i=criteriu(q,p)) <0) {q->st=p; return p; } else { q=q->st; continue; } if (i>0) if (q->dr ==0) {q->dr=p; return p;} else {q=q->dr; continue; } return eq(q,p); } } if(p==0) { printf(eroare: memorie insuficient\n); exit(1);} elibnod(p); return 0; } Accesarea unui nod al unui arbore poate fi realizat prin urmtoarea funcie: TNOD * cauta (TNOD *p) {TNOD *parb, *q; if (parb==0) return 0; 43

int i; for (q=parb;q;) if ((i=criteriu(q,parb))==0) return q; else if(I<0) q=q->st; else q=q->dr; return 0; } Parcurgerea unui arbore poate fi efectuat n trei modaliti: n preordine; n inordine; n postordine. Parcurgerea n preordine presupune accesul la rdcin i apoi parcurgerea celor doi subarbori ai si: mai nti subarborele stng, apoi cel drept. void preord (TNOD *p) { if (p!=0) { prelucrare(p); preord(p->st); preord(p->dr); } } Parcurgerea n inordine presupune parcurgerea mai nti a subarborelui stng, apoi accesul la rdcin i n continuare se parcurge subarborele drept. void inord (TNOD *p) { if (p!=0) {inord(p->st); prelucrare(p); inord(p->dr);} } Parcurgerea n postordine presupune parcurgerea mai nti a subarborelui stng, apoi a arborelui drept i, n final, accesul la rdcina arborelui. void postord (TNOD *p) { if (p!=0) { postord(p->st); postord(p->dr); prelucrare(p); } } tergerea unui arbore poate fi efectuat de urmtoarea funcie: void elib_nod(TNOD *p) { delete(p); } void sterge_arbore (TNOD *p) { if (p!=0) 44

{ postord(p->st); postord(p->dr); elibnod(p); } } Recursivitatea ca metod de programare Recursivitatea presupune o repetare. Ea const n apelarea unei funcii de ctre ea nsi. Funcia se numete recursiv dac n momentul executrii sale funcia se apeleaz pe ea nsi, sau indirect, printr-o succesivitate de apeluri ale altor funcii. Funcie este nemijlocit recursiv dac ea se apeleaz din corpul aceleiai funcii. De exemplu: int a() {.....a().....} Funcia este indirect recursiv dac se efectueaz apel recursiv prin intermediul unei succesiviti de apeluri ale altor funcii. Toate funciile componente ale acestei succesiviti de apeluri se socot recursive. De exemplu, a(){.....b().....} b(){.....c().....} c(){.....a().....} . Funciile a,b,c snt recursive, deoarece la apelul unei din funcii are loc apelul altor funcii inclusiv i pe ea nsi. Execuia algoritmului recursiv presupune crearea unui numr (finit) de copii ale algoritmului, care corespund diferitelor valori ale unei variabile. n construirea algoritmului recursiv este inclus o condiie de terminare a apelrii recursive de o expresie; care prin apelri succesive valoarea ei crete pn la o valoare ce satisface condiia de finalizare a recursivitii. La executarea programului cu funcii recursive se creeaz copii ale acestora, fiecare din ele corespunznd unei valori a expresiei de recursie. Att timp ct expresia recursiei se calculeaz 45

pn cnd crete pn la o valoare ce satisface condiia de finalizare a recursivitii, se spune c are loc recursia nainte. Cnd expresia atinge valoarea soluiei recursiei, se execut copiile create, astfel nct se obine soluia problemei. n acest caz are loc recursia napoi. Executarea programelor cu funcii recursive necesit mult memorie i mult timp de calcul, cu o complexitate mai mare dect cele nerecursive. Recursivitatea ca metod de programare este mai eficient, codul programelor cu funcii recursive este mai compact i mai uor de neles. n limbajul C++ funciile pot s se autoapeleze. Exemplul clasic de funcie recursiv este calcularea factorialului numrului N! = 1*2*3*...*N. Vom numi aceast funcie factorial(). long factorial(int n) {return((n==1)?1: n*factorial(n-1) ); } Apelul funciei recursive creeaz noi copii ale variabilelor locale i ale parametrilor pentru clasa de memorie auto i register, valorile lor din apelurile precedente se pstreaz. Pentru fiecare moment snt accesibile numai valorile apelului curent. Variabilele declarate cu clasa de memorie static nu necesit crearea noilor copii. Valorile lor snt accesibile n orice moment de executare a programului. n corpul funciei recursive este necesar de indicat condiia de ieire din procesul recursiv, n caz contrar sistemul de calcul poate intra n impas. De exemplu, funcia de tiprire unui numr (ca un ir de caractere): poate fi apelat de ea nsi, adic s fie recursiv void print_cifre(int n) { int i; if (n<0) { putchar(-); n=-n; } if ((i=n/10)!=0) print_cifre(i); putchar(n%10+0); } 46

Programul de mai jos calculeaz funcia Akkerman cu utilizarea funciei recursive ackr i funciei auxiliare smacc: // calculul recursiv al funciei kkerman # include <stdio.h> int smacc( int n,int x ) // funcie auxiliar int ackr( int n, int x, int y) // funcie recursiv void main () // funcia n care se apeleaz funcia { int x,y,n,t; int ackr(int, int, int); scanf("%d %d %d",&n,&x,&y); t=ackr(n,x,y); printf("%d",t); } int smacc( int n,int x ) // funcie auxiliar { switch (n ) { case 0: return(x+1); case 1: return (x); case 2: return (0); case 3: return (1); default: return (2); } } int ackr( int n, int x, int y) // funcie recursiv { int z; int smacc( int,int); if(n==0 || y==0) z=smacc(n,x); else { z=ackr(n,x,y-1); // apeluri recursive ackr(...) z=ackr(n-1,z,x); } return z; } Rezultatul ndeplinirii programului: 146 // datele iniiale 10 Fiierele // rezultatul obinut input/output ale limbajul 47

C++.

Deschiderea i nchiderea fiierelor. Citirea i scrierea n fiiere. Limbajul C++ include n sine funciile standard input/output ale limbajului C de prelucrare a fiierelor la nivelul jos, inferior i superior. Funciile de prelucrare a fiierelor de nivel inferior pot fi utilizate prin includerea fiierelor io.h, fcntl.h i stat.h. Pentru deschiderea unui fiier se utilizeaz funcia open care are urmtoarea form sintactic: df = open(); unde df este variabil de tip int (descriptorul de fiier). Funcia open are urmtorii parametri: se indic calea de acces la fiier i modalitatea de accesare a componentelor fiierului. Modalitatea de accesare a componentelor se indic prin una din urmtoarele constante: O_RDONLY fiierul se deschide numai pentru citirea componentelor lui O_WRONLY fiierul se deschide numai pentru nregistrarea componentelor lui O_RDWR fiierul se deschide pentru citirea i nregistrarea componentelor lui O_APPEND fiierul se deschide pentru adugarea componentelor noi la sfritul lui O_BINARY fiierul se prelucreaz binar O_TEXT fiierul se prelucreaz textual Pentru a crea un fiier, se utilizeaz funcia creat cu urmtorii parametri: calea de acces la fiierul creat i modalitatea de utilizare a fiierului. Al doilea parametru se indic de una din urmtoarele constante: fiierul va fi creat numai pentru S_IREAD citire 48

fiierul va fi creat numai pentru nregistrare S_IEXE fiierul va fi creat numai pentru executare Citirea dintr-un fiier se efectueaz prin funcia read indicndu-se urmtorii parametri: read(df, buf, lung) unde df este descriptor de fiier; buf pointer spre zona de memorie n care se va pstra nregistrarea citit din fiier; lung lungimea n octei a nregistrrii citite. nregistrarea n fiier se efectueaz prin funcia write. Aceast funcie are aceiai parametri ca i funcia read. Poziionarea ntr-un fiier se efectueaz prin funcia fseek(df, deplasare, origine). Aceast funcie are urmtorii parametri: df descriptorul fiierului, deplasarea indic numrul de octei pentru a deplasa capul de citire sau scriere al discului, origine are urmtoarele valori pentru efectuarea deplasrii: 0 fa de nceputul fiierului, 1- fa de poziia curent a capului de citire sau nregistrare, 2 fa de sfritul fiierului. nchiderea fiierului se efectueaz de funcia close (df). Exemplu de utilizare ale acestor funcii: char nfis[]=fisier1.dat; int df; char *p; df=open(nfis,O_RDONLY); read(df,p,80); close(df); Prototipurile funciilor de prelucrare a fiierelor de nivel superior pot fi utilizate prin includerea fiierului stdio.h. Fiierele input/output standard se efectueaz prin intermediul funciilor scanf i printf, gets, getc, getch i S_IWRITE 49

puts, putc, respective. Funciile getc(), getch() citesc cte un caracter din fiierul standard input . Funciile scanf i fscanf, printf i fprintf permit citirea, respectiv, afiarea uneia sau a mai multor valori la intrarea standard sau dintr-un fiier, respectiv, ieirea standard sau nregistrare ntr-un fiier. Prototipurile acestor funcii se afl n biblioteca stdio.h. Principiul de utilizare al funciei printf const n asocierea unei liste, care conine indicaii de formatare, dat sub forma unui ir de caractere, o list de variabile. Ambele funcii utilizeaz specificaiile de scriere sau citire plasate ntr-o constant de tip ir de caractere, urmat de o list de argumente. Funcia de afiare printf utilizeaz ca argumente nume de variabile, iar funcia de citire scanf utilizeaz drept argumente adrese de variabile. De exemplu, #include<stdio.h> void main() {printf("intreg:%6i \n real: %9.3f ",316,144.82) ; int z; printf("Introdu valoarea z:"); scanf("%d",&z); printf("%6d",z); } Rezultatul ndeplinirii programului: intreg: 316 real: 144.820 Introdu valoarea z:500 Valoarea z: 500 Lista este parcurs de la stnga la dreapta. Fiecare semn este asociat cu caracterul care l urmeaz i este interpretat drept caracter de control. Caracterele de control utilizate snt: 50

\n avans la nceput de linie nou; \r poziionare la nceputul liniei curente; \t tabulator; \a emite un semnal sonor . Fiecare semn % este interpretat ca nceputul descrierii caracteristicilor de tiprire a unei valori. Cele mau utilizate semne snt urmtoarele: Semnul Descrierea %d, un ntreg zecimal este ateptat la intrare; %i argumentul corespunztor trebuie s fie un pointer la ntreg; %o un ntreg octal este ateptat la intrare; argumentul corespunztor trebuie s fie un pointer la ntreg; %x un ntreg hexazecimal este ateptat la intrare;argumentul corespunztor trebuie s fie un pointer la ntreg; %h un ntreg short este ateptat la intrare; argumentul trebuie s fie un pointer la un ntreg short; %u un ntreg fr semn zecimal este ateptat la intrare; argumentul s fie pointer la ntreg; %f un numr n virgul flotant este ateptat; argumentul corespunztor trebuie s fie un pointer la un cmp float. Caracterul de conversie e este *f. Formatul prezentat la intrare pentru un float este alctuit dintr-un semn opional; %e un numr n virgul flotant este ateptat; argumentul corespunztor trebuie s fie un pointer la un cmp double. Caracterul de conversie e este *e. Formatul prezentat la intrare pentru un double este alctuit dintr-un semn opional, un ir de numere care pot s conin i un punct zecimal i un cmp 51

de exponent care este format din E sau e, urmat de un ntreg cu semn; %c un singur caracter este ateptat la intrare; argumentul corespunztor trebuie s fie un pointer la caracter. n acest caz, ignorarea caracterelor albe este suprimat; pentru a citi urmtorul caracter, altul dect caracterele albe se va utiliza %1s; un ir de caractere este ateptat; %s argumentul corespunztor trebuie s fie un pointer al unui tablou de caractere, destul de mare pentru a ncpea irul i un terminator \0, care va fi adugat. Caracterele de conversie d, u, i ,o, i x pot fi precedate de litera l, pentru a indica un pointer la long, mai degrab dect la int, care apare n lista de argumente. Similar, litera l naintea lui e sau f indic un pointer la double n lista de argumente. De exemplu: int i; float x; char nume[50]; scanf (%d%f%s,&i,&x,nume) ; cu linia de intrare 25 244.32E-1 Mircea va asigna lui i valoarea 25, lui x valoarea 244.32E-1, iar lui nume valoarea Mircea. Cele trei cmpuri de la intrare pot fi separate de oricte spaii, taburi i caractere de linie nou. Apelarea int i; float x; char nume[50]; scanf (%2d%4.2f%2s,&i,&x,nume) ; cu linia de intrare 25 244.32E-1 Mircea va asigna 25 lui i, 44.32 lui x, iar nume va obine 52

valoarea Mi. Cele mai utilizate secvene asociate valorilor de tip ntreg snt %ssNX sau %ssNU, unde s este semnul + dac se dorete afiarea explicit a semnului, - arat c se va face o aliniere la stnga. N este un numr care arat pe cte poziii se va face afiarea. De exemplu, #include<stdio.h> void main() { printf("% -+5i",3); } programul indicat va afia valoarea +3 prin aliniere la stnga n conformitate cu specificaiile date, astfel semnul - cere alinierea la stnga, n cmpul afectat valorii, semnul + cere afiarea explicit a semnului, cifra 5 arat c afiarea se va face pe 5 poziii, simbolul i arat c va fi afiat o valoare de tip ntreg. Valorile de tip real pot fi tiprite utiliznd secvene asociate de forma %ssNMf sau %sSN.tte, n care simbolul M semnific precizia cu care vor fi reprezentate numerele (numrul de cifre dup punctul zecimal). Toate caracterele care nu aparin secvenelor de control snt afiate i snt tratate ca iruri de caractere. De exemplu, #include<stdio.h> void main () { char * p="abracadabra"; char ch=B; printf("%s %c ", p, ch); } programul afieaz irul de caractere adresat prin intermediul pointerului p i valoarea variabilei ch. Funcia scanf se utilizeaz la iniializarea unor variabile. Funciile scanf i printf snt utilizate mpreun. Prin funcia printf se afieaz un mesaj care adesea este un comentariu legat de valoarea care urmeaz s fie 53

introdus. De exemplu, n programul de mai jos am afiat mesajul numr real, dup care urmeaz apelul funciei scanf care ateapt introducerea unei valori de tip real sau ntreg: #include<stdio.h> void main() { float a; int i; printf ("Introdu un numr real: ");//utilizatorul va introduce, //la scanf("%f", &a); //tastatura, un numr urmat de Enter. printf("\n Introdu un numr intreg: "); //la fel se procedeaz //n cazul variabilei i scanf ("%i,", &i); // afiarea textului "numr intreg" printf("\n Numr ntreg: %6i \n Numr real: %9.3f", i, a); } // va fi urmat de introducerea, la tastatur, a numrului //dorit. Rezultatul ndeplinirii programului: Introdu un numr real: 3.14 Introdu un numr intreg: 20 Numr ntreg: 20 Numr real: 3.140 Funciile de scriere i citire anterioare pot fi folosite i n cazul fiierelor. Biblioteca stdio.h, specific limbajului C, conine definiia unui tip de date FILE. Accesul la un fiier se face printr-un pointer de tip FILE. Etapele care trebuie s fie parcurse snt definirea unui pointer de tip FILE i asocierea unui fiier fizic. Pointerul va primi drept valoare adresa unei variabile de tip FILE, obinut prin intermediul funciei fopen(). Aceast funcie are dou argumente: un ir de caractere care conine numele fiierului fizic 54

recunoscut de sistemul de operare i un alt ir de caractere care conine indicaii relativ la modul de utilizare al fiierului. Ultimul parametru poate conine caracterul pentru fiiere deschise pentru citire, R pentru fiiere deschise pentru creare sau W scriere, pentru fiiere de tip text sau T pentru fiiere binare B n exemplul de mai jos am creat un fiier prin intermediul unui pointer de tipul FILE i o iniializare prin funcia fopen(). Pointerul p conine o valoare de tip FILE fumizat de funcia fopen(), care deschide pentru nregistrare fiierul disk.dat. Am scris n acest fiier dou valori n acelai mod cum am fcut afiarea la ieirea standard. #include<stdio .h> void main() { FILE *p; p = fopen("disk.dat","wt"); fprintf(p,"%6i\n%9.3f", 29, 2.71); fclose (p) ; } Rezultatul ndeplinirii programului: s-a format fiierul disc.dat (pentru nregistrare) cu urmtorul coninut: 29 2.710 ntrebrile pentru verificarea cunotinelor: 1. Enumerai tipurile de date fundamentale n limbajul C++? 2. Pentru ce se utilizeaz variabilele n limbajul C++? 3. Prin ce se deosebesc variabilele descrise ca extern de cele descrise ca static? 55

4. Prin ce se deosebete o referin de un pointer? 5. De ce nu este permis definirea unui vector de referine? 6. Prin ce se deosebete noiunile union i struct? 7. Care snt operaiile aritmetice admise asupra pointerilor? 8. Definii un tablou tridimensional, elementele crora snt de tip point. 9. Dai exemple de utilizare a operatorilor unari, binari i ternari. 10. Prin ce se deosebete operatorii de incrementare i decrementare prefixate de cele sufixate? Exemple de expresii. 11. Explicai aciunea instruciunilor de ciclu. Exemple de utilizare. 12. De ce fiecare variant a instruciunii switch se poate termina cu instruciunea break? Exemple de utilizare a instruciunii switch cu i fr instruciunea break. 13. Corectai programul de mai jos care efectueaz sortarea parial a listei simplu lnuite, dup sortare pointerul v indic la elementul k1.

NOD *v; float k1; k1=prim->val; r=prim; while( r->urm!=NULL ) { v=r->urm; if (v->val; v=v->urm; v->n=prim; prim=v; } else r=v; } 56

14. Alctuii un program care organizeaz o list ciclic Fi (1<I) dintr-o consecutivitate de numere ntregi B1, B2,..., Bn din intervalul de la 1 pn la 9999, sortate cresctor. Indicaie: La rezolvarea acestei probleme avem o list sortat cresctor Fi. La introducerea unui nou element Bi+1, acest element se inserteaz la locul su n lista Fi. Pentru insertarea elementului nou n lista Fi, s se cerceteze trei cazuri: - lista Fi este vid, - elementul se inserteaz la nceputul listei, - elementul se inserteaz la sfritul listei. 15. Cercetai programul de mai jos typedef struct str1 { float val; struct str1 *n; } NOD; void main() { NOD *arrange(void); NOD *p; p=arrange(); while(p!=NULL) { cout<< p->val<<endl; p=p->n; } } NOD *arrange() // formarea listei sortate { NOD *dl, *r, *p, *v; // dl - nceputul listei, p,v pointeri la //dou elemente vecine, float in=1; // r fixeaz pointerul la elementul curent //care conine valoarea n char *is; dl=new(NOD); dl->val=0; // primul element dl->n=r=new(NOD); r->val=10000; r->n=NULL; // ultimul element 57

while(1) { cin>> is; if(* is=='q') break; in=atof(is); r=new(NOD); r->val=in; p=dl; v=p->n; while(v->valn); } r->n=v; p->n=r; } return(dl); } 16. Inversai consecutivitatea de simboluri introduse. De exemplu, s-a introdus consecutivitatea ABcEr-1, consecutivitatea inversat va fi 1-rEcBA. Utilizai noiunea de list simplu lnuit. 17. Cercetai operaiile efectuate asupra stivei n programul de mai jos: typedef struct st // declararea tipului STACK { char ch; struct st *ps; } STACK; main() { STACK *p,*q; char a; p=NULL; do // completarea stivei { a=getch(); q=new(STR1); q->ps=p; p=q; q->ch=a; } while(a!='.'); do // tiparul stivei 58

{ p=q->ps; delete (q); q=p; cout<< p->ch; } while(p->ps!=NULL); } 18. Scriei un program n care numerele din baza 10 din lista dublu lnuit dat se nlocuiesc cu cele din baza 2. 19. Scriei un program care ar efectua urmtoarele operaii. Se creeaz o list dintr-un ir de numere ntregi, care se termin cu zero. Din list se terg mai nti elementele negative, apoi numerele pare. 20. Scriei un program care din trei cozi se selecteaz o coad nou mai nti numerele negative, zerourile, apoi numerele pozitive. 21. Analizai ce efectueaz programul de mai jos: typedef struct nod { float val; struct nod *n; } NOD; int index (NOD *x[100]) { NOD *p; int i,j=0; float inp; for (i=0; i<100; i++) x[i]=NULL; cin>> inp; while (inp!=0) { j++; p=new(NOD); i=inp%100+1; p->val=inp; p->n=x[i]; x[i]=p; cin>>inp; } return j; } 59

Valoarea returnat de funcia index va fi numrul de elemente cercetate din list. 22. Scriei un program care s extrag n lista L2 elementele cu numere impare din lista L1. Lista L1 conine numere ntregi. 23. Cum se utilizeaz funciile standard de citire a valorilor variabilelor de la tastatur i din fiier? 24. Cum se utilizeaz funciile standard de tipar la ecran i nregistrare ntr-un fiier a valorilor variabilelor? 25. Care snt caracterele de control i specificare a tiparului variabilelor la ecran? Temele pentru acas: 1. Care dintre exemplele de declaraie i/sau iniializare snt corecte: int r; int 25; int p=25; int i; int r=i; r=25; const int j=25; int r=j; 2. Scriei o versiune de funcie cu pointeri pentru strcat() concatenare a dou iruri de caractere. 3. Scriei variante de programe cu pointeri care ar efectua funciile citete_linie(), reverse_linie(). 4. Care dintre exemplele de declaraie i/sau iniializare snt corecte: int &r; int &r=25; int *p=25; int i; 64 int &r=i; r=25; | 60

const int j=25; int &r=j; 5. Scriei un program pentru a numra biii de la dreapta spre stnga pentru un numr dat. 6. Scriei un program care rotete ntregul n la dreapta cu b poziii. 7. Scriei un program care inverseaz cei n bii ai si care ncep de la poziia p, lsndu-i pe ceilali neschimbai. 8. Scriei un program care convertete literele mari n litere mici utiliznd o expresie condiional. 9. Scriei un program care convertete numrul ntreg n n baza zecimal ntr-un ir de caractere. 10. Scriei un program care convertete ntregii fr semn n ntr-o reprezentare binar s. 11. Scriei un program care convertete un ntreg ntr-un numr hexazecimal. 12. Scriei un program de convertire a unui ir de caractere ntr-un ntreg. 13. Scriei un program care inverseaz un ir de caractere s. 14. Scriei un program pentru a numra biii de la dreapta spre stnga pentru fiecare numr citit dintr-un fiier. 15. Scriei un program care rotete un ntreg n (citit dintr-un fiier) la dreapta cu b poziii. b este un numr aleator de la 1 la 16. 16. Scriei un program care n fiecare numr citit dintr-un fiier inverseaz cei n bii ai si care ncep de la poziia p, lsndu-i pe ceilali neschimbai, p ia valoare de la 1 la 10. 17. Scriei un program care convertete literele mari n litere mici dintr-un fiier textual. 18. Scriei un program care convertete numrul ntreg n n baza zecimal, citit dintr-un fiier ntr61

un ir de caractere. 19. Scriei un program de convertire a fiecrui element dintr-o stiv ntr-un ir de caractere. Temele pentru lucrri de laborator: I. 1. Scriei un program care compar dou stive date. 2. Scriei un program care genereaz o mie de seturi de cinci numere aleatoare cuprinse ntre 1 i 40, n final afind frecvena cu care a fost generat fiecare numr. 3. Scriei un program pentru a numra biii de la dreapta spre stnga pentru un numr introdus de la tastatur. II. 1. Scriei un program care calculeaz numrul de elemente dintr-o list simplu lnuit care snt mai mici ca valoarea medie aritmetic a tuturor elementelor acestei liste. 2. Scriei un program care efectueaz nmulirea cifrelor unui numr dat. 3. Scriei un program care convertete numrul ntreg n n baza zecimal ntr-un ir de caractere. 1. Scriei un program care determin un numr obinuit din inversul cifrelor numrului dat. 2. Scriei un program care permite crearea unui arbore binar i traversarea lui n inordine, preordine, postordine. 3. Scriei un program care convertete ntregii fr semn dintr-o list dublu lnuit n reprezentare binar. 62

III.

IV. 1. Scriei un program care din 100 de numere aleatoare se determin numrul maximal i cel minimal. S se determine diferena dintre numrul maximal i cel minimal determinat 2. Scriei un program care convertete ntregii fr semn n selectai dintr-un fiier ntr-o reprezentare binar. 3. Scriei un program care convertete un ntreg ntrun numr hexazecimal. V. 1. Scriei un program care determin numrul de ordine a numrului minimal dintr-o consecutivitate de numere aleatoare. Cantitatea de numere aleatoare ale consecutivitii este aleatoare (N = 1, ,100). Valorile numerelor aleatoare ale consecutivitii snt din intervalul 0 100000. 2. Scriei un program de convertire a unui ntreg ntrun ir de caractere. 3. Scriei un program cu o funcie recursiv care calculeaz cel mai mare divizor comun al elementelor dintr-o consecutivitate. VI. 1. Scriei un program care determin numrul maximal i cel minimal ntr-o list circular de 100 de numere aleatoare. S se determine consecutivitatea de elemente ce se afl ntre numerele maximal i cel minimal determinate. 2. Scriei un program care calculeaz suma cifrelor pentru fiecare numr din consecutivitatea de 100 de numere aleatoare. 3. Scriei un program care inverseaz un ir de caractere s. VII. 1. Scriei un program care formeaz o list nou 63

simplu lnuit dup urmtoarea legitate: din trei liste simplu lnuite mai nti se selecteaz numerele divizibile la 3, 5 i 7, apoi cele pozitive pare. 2. Scriei un program care genereaz un fiier al cror valori ale elementelor snt cuprinse ntre 1 i 100. S se determine frecvena cu care a fost generat fiecare element n fiierul creat. 3. Scriei un program care inverseaz cei n bii ai elementelor unei liste simplu lnuit care ncep de pe poziia p, lsndu-i pe ceilali neschimbai. VIII. 1. Scriei un program care atribuie unei liste simplu lnuite elementele altei liste n ordine invers. 2. Scriei un program cu funcie recursiv care calculeaz cel mai mare divizor comun dintr-un ir de numere date. 3. Scriei un program care din dou fiiere ordonate descresctor se unesc n unul nou pstrndu-i-se ordinea descresctoare de sortare. IX. 1. Scriei un program care determin cte numere din consecutivitatea de 100 de numere aleatoare snt mai mari ca vecinii si. 2. Scriei un program care nlocuiesc numerele din baza 10 din consecutivitatea dat cu cele din baza 2. 3. Scriei un program care decide dac o valoare x aparine unei liste dublu lnuite v. Elementele lui v trebuie s fie n ordine cresctoare. Se tiprete numrul elementului din list (un numr ntre 0 i n-1), dac x apare n v, i 1, dac nu apare. X. 1. Scriei un program care va tipri n ordine invers subconsecutivitatea de numere dintre valoarea minim i maxim ale unei liste simplu lnuit. 64

2. Scriei un program care determin un numr obinuit din inversul cifrelor numrului dat. 3. Utiliznd funcia recursiv, scriei un program care convertete un ntreg citit dintr-un fiier n numere hexazecimale. XI. 1. Scriei un program care formeaz o list dublu lnuit nou din cea dat dup urmtoarea legitate: elementele listei noi se obine din inversul cifrelor numrului din lista dat. 2. S se scrie un program care ar conine dou funcii: una - recursiv, a doua - nerecursiv pentru numrarea elementelor unei liste. 3. Scriei un program care inverseaz fiecare element de tip ir de caractere dintr-o list simplu lnuit. XII. 1. Scriei un program care convertete literele mari n litere mici utiliznd din elementele unei stive. 2. S se scrie un program care din lista L1 ce conine numere ntregi s se extrag n lista L2 elementele cu numere impare din lista L1. 3. Scriei un program care convertete ntregii fr semn dintr-o list simplu lnuit n, ntr-o reprezentare binar. XIII. 1. Scriei un program care calculeaz suma cifrelor pentru fiecare numr din consecutivitatea de 100 de numere aleatoare. 2. Scriei un program care rotete fiecare element al listei dublu lnuite n la dreapta cu b poziii. 3. Scriei un program care atribuie unui fiier elementele altui fiier n ordine invers. XIV. 1. Scriei un program care creeaz o list circular a 65

cror valori ale elementelor snt cuprinse ntre 1 i 100. S se determine frecvena cu care a fost generat fiecare element al listei create. 2. Scriei un program care calculeaz suma cifrelor pentru fiecare numr din consecutivitatea de 100 de numere aleatoare. 3. Scriei un program care convertete fiecare element al listei dublu lnuite ntr-un numr hexazecimal. XV. 1. Scriei un program care determin cte numere ale unei cozi de 100 de numere aleatoare snt mai mari ca vecinii si. 2. Scriei un program care formeaz un fiier nou din cel dat dup urmtoarea legitate: elementele fiierului nou se obine din inversul cifrelor numrului din fiierul dat. 3. Alctuii un program care ar efectua urmtoarele operaii asupra listei dublu lnuite: iniializarea listei; cutarea elementului dup criteriul dat; insertarea unui element nou nainte sau dup o component indicat a listei; eliminarea unui element din list; sortarea componentelor listei.

66

Tema: Clase (constructori, destructori).Funcii i clase prietene. Scopul lucrrii: familiarizarea studenilor cu noiunea de clase, utilizarea constructorilor, destructorilor, cu funcii i clase prietene. Consideraiile teoretice necesare: Clase Sintaxa simplificat a declarrii unei clase este urmtoarea: class NumeClas {... declaraii variabile membri... declaraii funcii membri... } Din definirea clasei se poate observa c clasa este asemntoare cu o structur. Ea are n componena sa membri att de tip variabil, ct i de tip funcie. Pentru datele din interiorul clasei se utilizeaz, de regul, termenul de date membri, iar pentru funcii denumirea de funcii sau metode. O clas permite incapsularea n interiorul sau a datelor i a codului. Pentru a putea utiliza efectiv un tip de date (n cazul de fa o clas), trebuie sa definim o variabil de acel tip. ntr-un mod similar declaraiei int i; putem scrie: NumeClas variabil Vom considera c variabil este un obiect. Exprimarea uzual este c un obiect este instanierea unei clase. O clas este compus din dou pri: declaraia i implementarea ei. Declaraia clasei prezint membrii clasei. Membrii clasei snt variabile de 67

Lucrarea de laborator nr. 2

instaniere i funcii membri indicate prin prototipul lor (tipul returnat, numele funciei, lista de parametri). Implementarea funciilor membri are loc prin implementarea clasei. Gradul de accesibilitate la elementele componente ale clasei este indicat prin cuvintele: private sau protected elementele clasei snt accesate numai prin intermediul funciilor membri sau prietene friend, public toate elementele snt disponibile n exteriorul clasei. De exemplu: #include<iostream.h> class myclass // se declar un nou tip de date myclass {private: int a; // componenta int a se declar implicit n //zona private public: // funciile membri declarate mai joc snt din //zona public void set_a (int num); // prin intermediul acestor funcii se // acceseaz componenta a int get_a (); }; void myclass::set_a(int num) { a=num;} // aceast funcie atribuie valoare componentei a int myclass::get_a(){return a; } // aceast funcie furnizeaz valoarea componentei a void main () // funcia de baz a programului { myclass ob1, ob2; // se declar dou obiecte ob1 i ob2 //de tipul myclass ob1.set_a (10); // pentru obiectul ob1 se atribuie //valoare componentei a egal cu 10 ob2.set_a (99); // pentru obiectul ob2 se atribuie //valoare componentei a egal cu 99 cout << ob1.get_a ()<< \n; // pentru obiectul ob1 68

se //furnizeaz valoarea componentei a //care apoi se tiprete la ecran cout << ob2.get_a () << \n; // pentru obiectul ob2 se //furnizeaz valoarea componentei a //care apoi se tiprete la ecran } Rezultatul ndepliniri programului: 10 99 Funciile set_a i get_a, pentru setarea i furnizarea valorii pentru componenta a, nu snt necesare,dac componenta a va fi inclus n zona public,. Componenta a va fi explicit accesat i i se va iniializa sau atribui valoare. Exemplul de mai sus se va modifica n felul urmtor: #include<iostream.h> class myclass // se declar un nou tip de date myclass {public: int a; // componenta int a se declar explicit n //zona public // funciile membri declarate mai sus nu snt necesare }; void main () { myclass ob1, ob2; // se declar dou obiecte ob1 i ob2 //de tipul myclass ob1.a =10; // pentru obiectul ob1 se iniializeaz //valoarea componentei a n mod //explicit cu valoarea 10 ob2.a = 99; // pentru obiectul ob2 se iniializeaz 69

//valoarea componentei a n mod // explicit cu valoarea 99 cout << ob1.a << \n; // pentru obiectul ob1 // se tiprete valoarea componentei a cout << ob2.a << \n; // pentru obiectul ob2 // se tiprete componentei a } Rezultatul ndepliniri programului: 10 99 Constructorii snt un tip special de funcie membru, avnd acelai nume ca i numele clasei, nu returneaz rezultat i snt apelai automat la instanierea unei clase, fie ea static sau dinamic. Ei au scopul de a atribui valori iniiale elementelor membri, dar pot efectua i unele operaii, cum ar fi, alocarea dinamic de memorie, deschiderea unui fiier .a. De exemplu: class persoana { private: char nume[40]; long int telefon; public: persoana() {nume=\0; telefon =0;}; //constructorul iniializeaz valori nule elementelor membri persoana(char*p, long int t) {strcpy(nume,p); telefon=t;} //constructor iniializeaz valori concrete pentru elementele //membri ale clasei persoana(char* nume) {return nume; }; //aceste funcii atribuie valori pentru elementele membri nume persoana(long int telefon) {return telefon;}; //i telefon 70

persoana persoana_ input (char *n, long int t=0) {persoana p; strcpy(p.nume,n); p.telefon=t; return p; }; }; Apelul constructorului se efectueaz n momentul declarrii unui obiect. Dac declarm o variabil de tipul persoana, fie persoana p = persoana (Vasilina, 743567); sau persoana p (Vasilina, 743567); constructorul va iniializa elementele membri nume i telefon ale clase persoana respectiv cu valorile Vasilina i 743567. Dac se va declara un obiect de tipul persoana fr date iniiale, constructorul va completa elementele membri nume cu stringul vid \0 i telefon cu valoarea 0. Destructorii dezactiveaz toate funciile unui obiect, l distruge i snt apelai automat la eliminarea unui obiect, la ncheierea timpului de via n cazul static, sau la apelul unui delete n cazul dinamic. De regul, destructorii snt utilizai n cazul, cnd constructorii efectueaz alocri dinamice de memorie. Destructorul are acelai nume ca i constructorul, fiind precedat de semnul ~. De exemplu: #include<iostream.h> #include<string.h> #include<stdlib.h> #define Size 255 class strtype { private: char *p; int len; public: strtype() // constructorul { p=new char; 71

if (!p){cout << Eroare la alocarea memoriei \n; exit(1);} *p=\0; len=0; }; ~strtype() {cout << Eliberarea memoriei\n; delete p; } // destructorul void set (char*ptr) { if (strlen(ptr)> Size ) cout<<Stringul conine mai mult de 255 de caractere \n; strcpy(p,ptr); len=strlen(p);}; void show() { cout << p << - lungimea << len << \n;} }; void main() { strtype s1,s2; s1.set (Test); s2.set(Program C++); s1.show(); s2.show(); } Rezultatul ndeplinirii programului: Test- lungimea 4 Program C++- lungimea 11 Eliberarea memoriei Eliberarea memoriei Destructorii obiectelor membri snt apelai, dup ce destructorul obiectului principal a fost executat. Dac obiectul membru este compus din alte obiecte, atunci se va proceda la executarea destructorilor obiectelor incluse. Destructorii obiectelor membri snt apelai n ordine invers, n care acetea apar n declaraia clasei. Din punct de vedere cronologic, constructorul este apelat dup alocarea memoriei necesare, deci n faza final a crerii obiectului, iar destructorul naintea eliberrii memoriei aferente, deci n faza iniial a distrugerii sale. Constructorii i destructorii se declar i se 72

definesc similar cu celelalte funcii membri, dar prezint o serie de caracteristici specifice: numele lor coincide cu numele clasei creia ii aparin; destructorii se disting de constructori prin faptul c numele lor este precedat de caracterul nu pot returna nici un rezultat nu se pot utiliza pointeri ctre constructori sau destructori constructorii pot avea parametri, destructorii insa nu. Un constructor fr parametri poart denumirea de constructor implicit. n care o clasa nu dispune de constructori sau destructori, compilatorul de C++ genereaz automat un constructor, respectiv destructor, implicit. Membrii unei clase Accesarea membrilor unei clase se face n felul urmtor: obiect.VariabiMembru = valoare; pentru accesul la o variabil membru, i obiect.FuncieMembru(); pentru apelarea unei funcii membri. Pentru exemplificare s consideram o implementare a noiunii de punct. Ca variabile membri avem nevoie doar de coordonatele x i y care definesc poziia n spaiu a unui punct. Am mai declarat o funcie care calculeaz aria dreptunghiului avnd punctele (0, 0) i (x, y). class Point {unsigned x, y; unsigned long Arie() {return x * y;}; unsigned GetX(); unsigned GetY(); void SetX(unsigned X); void SetY(unsigned Y); 73

}; unsigned Point::GetX() {return x;} unsigned Point::GetY(){return y; } void Point::SetX(unsigned X){ x = X; } void Point::SetY(unsigned Y) { y = Y; } Am folosit un operator nou, specific C++, ::, numit operator de rezoluie, numit i operator de acces sau de domeniu. El permite accesul la un identificator dintr-un bloc n care acesta nu este vizibil datorit unei alte declaraii locale. Un exemplu de folosire este urmtorul: char *sir = "variabil global"; void funcie() { char *sir = "variabil local"; printf("%s\n", ::sir); // afieaz variabila global printf("%s\n", sir); // afieaz variabila local } Pentru definiiile funciilor membri aflate n afara declaraiei clasei este necesar specificarea numelui clasei urmat de acest operator, indicnd faptul c funcia are acelai domeniu cu declaraia clasei respective i este membru a ei, dei este definit n afara declaraiei. Cuvntul-cheie this. Toate funciile membri ale unei clase primesc un parametru ascuns, pointer-ul this, care reprezint adresa obiectului n cauz. Acesta poate fi utilizat n cadrul funciilor membri. De exemplu: unsigned long Point::Arie() {return this->x * this->y; } Crearea i distrugerea obiectelor. S considerm urmtorul program C++: void main() {Point p; } n momentul definirii variabilei p, va fi alocat automat spaiul de memorie necesar, acesta fiind eliberat la terminarea programului. n exemplul de mai sus, variabila p este 74

de tip static. n continuare vom modifica acest program pentru a folosi o variabil dinamic (pointer). void main() { Point *p; p = new Point; p->x = 5; p->y = 10; printf("Aria = %d\n", p->Aria()); delete p; } Operatorul new este folosit pentru alocarea memoriei, iar sintaxa acestuia este: variabila = new tip; variabila = new tip(valoare_iniial); variabila = new tip[n]; Prima variant aloc spaiu pentru variabil dar nu o iniializeaz, a doua variant ii aloc spaiu i o iniializeaz cu valoarea specificat, a treia aloc un tablou de dimensiune n. Acest operator furnizeaz ca rezultat un pointer coninnd adresa zonei de memorie alocate, n caz de succes, sau un pointer cu valoarea NULL (practic 0) cnd alocarea nu a reuit. Eliminarea unei variabile dinamice i eliberarea zonei de memorie aferente se realizeaz cu ajutorul operatorului delete. Sintaxa acestuia este: delete variabil; Dei aceti doi operatori ofer metode flexibile de gestionare a obiectelor, exist situaii n care aceasta nu rezolv toate problemele. De aceea, pentru crearea i distrugerea obiectelor n C++ se folosesc nite funcii membri speciale, numite constructori i destructori, despre care s-a menionat mai sus. S completm n continuare clasa Point cu un constructor i un destructor: Point::Point() // constructor implicit { x = 0; y = 0; } 75

Point::Point(unsigned X, unsigned Y) { x = X; y = Y; } Point::~Point() { } Ai remarcat cu aceast ocazie modul de marcare a comentariilor n C++: tot ce se afl dup caracterul // este considerat comentariu. Definiii de forma Point p; //sau Point *p = new Point(); duc la apelarea constructorului implicit. O ntrebare care poate apare este motivul pentru care am realizat funciile GetX(), GetY(), SetX(), SetY(), cnd puteam utiliza direct variabilele membri x i y. Deoarece una din regulile programrii C++ este de a proteja variabilele membri, acestea pot fi accesate numai prin intermediul unor funcii, care au rolul de metode de prelucrare a datelor incapsulate n interiorul clasei. Funcii i Clase friend. Conceptul friend permite abateri controlate de la ideea proieciei datelor prin incapsulare. Mecanismul de friend (sau prietenie) a aprut datorita imposibilitii ca o metod s fie membru a mai multor clase. Funciile prietene snt funcii care nu snt metode ale unei clase, dar care au totui acces la membrii privai ai acesteia. Orice funcie poate fi prieten a unei clase, indiferent de natura acesteia. Sintaxa declarrii unei funcii prietene n cadrul declaraiei unei clase este urmtoarea: friend NumeFuncie De exemplu: class Point { friend unsigned long Calcul(unsigned X, unsigned Y); public: friend unsigned long AltClas::Calcul(unsigned 76

X, ... }; unsigned long Calcul(unsigned X, unsigned Y) {return X * Y / 2; } unsigned long AltClas::Calcul(unsigned X, unsigned Y) { ... } Funcii membri ca prietene. Orice funcie membru nu poate fi prieten aceleiai clase, dar, posibil, s fie prietena altei clase. Astfel, funciile friend constituie o punte de legtur ntre clase. Exist dou moduri de a face ca o funcie membru a unei clase s fie prietena altei clase. Prima variant este specificarea funciei membru a unei clase, ca fiind prieten altei clase. Deci, n cea de-a doua clas, vom declara funcia membru n prima clas ca fiind de tip friend. class B; class A {.. void Al (B &x) ; .. ); class B { friend void A::A1(B &x); ); A doua variant este declararea unei clase prieten, astfel, c toate funciile sale membri, snt, de fapt, prietene clasei n care un obiect de tipul primei clase este declarat friend. class B; class A { void Al (B &x) ; ); class B { friend A; 77 unsigned Y);

}; Indiferent de metod, se impune predeclararea clasei, care va fi prieten sau va conine funcii, care snt prietene unei alte clase. Clasele prietene snt clase care au acces la membrii privai ai unei clase. Sintaxa declarrii unei clase prietene este: friend class NumeClasPrieten De exemplu: class PrimaClas { ... }; class ADouaClas { ... friend class PrimaClas; }; Clasa PrimaClas are acces la membrii privai ai clasei ADouaClas. Relaia de prietenie nu este tranzitiv. Daca o clasa A este prietena a clasei B, i clasa B este prieten a unei clase C, aceasta nu nseamn ca A este prieten clasei C. De asemenea, proprietatea de prietenie nu se motenete n clasele derivate. Clase prietene snt utile n situaia n care avem nevoie de clase, care s comunice ntre ele deseori, acestea aflndu-se pe acelai nivel ierarhic. Pentru exemplificare, presupunem c vom implementa o stiv de caractere ca o list simplu nlnuit. Vom utiliza dou clase, una ataat nodurilor din list i una stivei propriu-zise. #include<conio.h> #include<stdio.h> class stiva; class nod { private: 78

friend stiva; nod(int d, nod *n); int data; nod *anterior; }; class stiva { private: nod *virf; public: stiva () { virf=NULL; } ~stiva() { delete virf; } void push (int c); int pop (); }; nod::nod (int d, nod *n) {data=d; anterior=n;} void stiva::push (int i) {nod *n=new nod(i, virf); virf=n; } int stiva::pop () { nod *t=virf; if (virf) { virf=virf->anterior; int c= t->data; delete t; return c; } return -1; } void main() { int c; stiva cs; printf("Introdu un sir de caractere, ce se termina in *"); while ((c=getch ())!='*') { cs.push (c); putch(c); } putch(c); while ((c=cs.pop ())!=-1) { putch (c); } 79

c='\n'; putch(c); } Rezultatul ndeplinirii programului: Introdu un sir de caractere, ce se termina in *ertyuioppoiu*uiop poiuytre Prototipul unei funcii friend cu o clas se afl, de regul, n cadrul clasei respective. Funcia friend nu este membru a acestei clase. Indiferent de poziia declaraiei unei asemenea funcii, n cadrul declaraiei clasei funcia va fi public. De exemplu: class punct { private: int x, y; public: punct (int xi, int yi) {x=xi; y=yi; }; friend int compara (punct &a, punct &b); }; int compara (punct &a, punct &b) { //returneaz <0, dac a este mai aproape de origine // >0, dac b este mai aproape de origine // =0, dac a i b snt egal deprtate. return a. x*a. x+a. y*a. y-b. x*b. x-b. y*b. y; } void main() { punct p (14,17), q(57,30); if(compara(p,q)<0) printf("p este mai apropiat de origine\n"); else printf (q este mai apropiat de origine. \n") ; } Orice funcie friend a unei clase poate fi transformat ntr-o funcie membru a acelei clase, renunndu-se ns la gradul de prietenie. Exemplul de mai sus se modific n felul urmtor: 80

#include<stdio.h> class punct { private: int x,y; public: punct (int xi, int yi) { x=xi; y=yi; } int compara (punct &b); }; int punct:: compara (punct &b) { return x*x+y*y-b.x*b.x-b.y*b.y; } void main () { punct p(14,17), q(57,30); if (p.compara (q)<0)printf ("p este mal apropiat de origine\n"); else printf ("q este mai apropiat de origine.\n"); } Rezultatul ndeplinirii programului: p este mal apropiat de origine ntrebrile pentru verificarea cunotinelor: 1. Ce se va afia la ecran? #include <iostream.h> int a=1,b=2, c=43; class numere { public: int a,b; void Actiune(int b) }; void numere:: Actiune(int b) { a=14; b=56; c=3; } void main () {numere doua_numere; doua_numere.Actiune(56); } 2. Care este deosebirea dintre constructor i un destructor al 8 unei clase? Cum snt activai constructorii i destructorii? 81

2. Ce se numesc clase prietene? 3. Ce se numesc funcii prietene? Temele pentru acas: 1. Introducei i lansai n execuie urmtorul program. Ce rezultat furnizeaz? #include <stdio.h> class numr {public: int n; numr(int i) { printf( "num() %d \n",n); }; -numr() { printf("-num() %d \n",n); }; }; class numere { public: numr a,b,c; numere(int x, int y, int z); -numere(); }; numere::numere(int x, int y, int z) : c(z), a(x), b(y) { printf("A fost apelat un constructor \n"); } numere::~numere {printf("A fost apelat un destructor\n"); } void main () {numere bon(1, 2, 3);) 2. Introducei i lansai n execuie urmtorul program. Ce rezultat se afieaz? #include. <stdio.h> class 0_clasa {public: o_clasa () { printf("Apel constructor\n"); }: ~o_clasa() { printf("Apel destructor \n"); } }; class alta_clasa2 ( public: 82

alta_clasa *ac; }; void main() { alta_clasa acc;} 3. Scriei un program n C echivalent cu urmtorul program C++. Creai funcii n limbajul C echivalente constructorului i destructorului i verificai apelul lor la momentul oportun. #include <stdio.h> class calculator { public: float memorie; calculator () ; ~calculator (); float aduna (float f); }; calculator::calculator () { printf("S-a pornit calculatorul\n") ; memorie=0 .0; } calculator::~calculator(){printf("S-a oprit calculatorul\n"); } float calculator::aduna (float f) (memorie+=f; return memorie; } void main () {calculator c; c.aduna(lO.O); c.aduna(30.0); c.aduna(2.0); printf ("Memoria este %f\n" ,c.memorie) ; } 4. Se va compila corect urmtoarea clas? class num { public: int data; num(int i) { data=i; ) int set (int i) { return data=i; } }; 5. Descriei funciile de modificare a stringului 83

(copiere, includere a unui substring, concatenare i eliminare a unui substring) pentru clasa: class sir { private: char continut[80]; int lungime; public: sir (); char *contine() (return continut;} }; Temele pentru lucrri de laborator: 1. S se scrie un program care s defineasc un nou tip de variabile - tipul complex i s construiasc funcii adecvate pentru operaiile de baz cu acest tip de date (adunare, scdere, nmulire, calculul modulului numrului complex). 2. S se scrie un program care ar evalua o funcie, a crei expresie analitic se introduce de la terminalul calculatorului ca un ir de caractere. 3. S se scrie un program n care se definete o clas stiv elementele creia snt de un tip abstract de date cu urmtoarele funcii: empty(Q) care cur stiva Q, if_is_empty(Q) care verific dac stiva Q este vid, in_query(Q,x) care adaug elementul x n stiva Q, out_query(Q,x) care scoate elementul x din stiva Q, error (k) care indic eroarea cu numrul k (k=1 dac stiva este suprancrcat, k=2 dac stiva este vid). 4. Scriei un program care determin pentru o clas list simplu lnuit cu urmtoarele funcii: member(x,L) care determin apartenena elementului x listei L; 84

5. 6. 7. 8.

equal(L1,L2) care determin echivalena a dou liste L1 i L2; print(L) care tiprete toate elementele listei L; readlist(L,fin) care citete elementele din fiierul fin n lista L. Scriei un program care determin o clas coad n care snt determinate urmtoarele funcii: empty(Q) care cur coada Q, if_is_empty(Q) care verific dac coada Q este vid, in_query(Q,x) care adaug elementul x n coada Q, out_query(Q,x) care scoate elementul x din coada Q, error (k) care indic eroarea cu numrul k (k=1 dac coada este suprancrcat, k=2 dac coada este vid). Scriei un program care la o singur trecere prin fiierul fin fr utilizare a fiierelor suplimentare va tipri elementele fiierului fin n urmtoarea ordine: toate elementele mai mici ca valoarea a, elementele din segmentul [a,b], restul elementelor pstrnd aceeai ordine din fiierul fin. (a<b, a i b snt cunoscute i snt de acelai tip ca i elementele fiierului fin). Scriei un program care determin o clas stiv n care snt determinate urmtoarele funcii: empty(S) care cur stiva S, if_is_empty(S) care verific dac stiva S este vid, pop(S,x) care adaug elementul x n stiva S, push(S,x) care scoate elementul x din stiva S, error (k) care indic eroarea cu numrul k (k=1 dac stiva este suprancrcat, k=2 dac stiva este vid). Scriei un program care efectueaz asupra elementelor fin - fiierului de tip text - urmtoarele operaii: tiprete inversat coninutul fiecrui cuvnt al liniei 85

fiierului fin, sorteaz lexicografic att cuvintele din fiierul fin, ct i cuvintele inversate, determin cuvintele simetrice din fiierul fin. 9. Scriei un program care determin clasa arbore. Asupra elementelor arborelui s se determine urmtoarele operaii: repeat (A,x) care determin numrul de repetri ale elementului x n arborele A, media(A) care calculeaz media aritmetic a elementelor de tip ntreg sau real. Dac elementele snt de alt tip, media (A) =0, dac elementele arborelui A snt de tip ntreg sau real, valorile negative ale elementelor se nlocuiesc cu valorile lor absolute, dac elementele arborelui A snt de tip ntreg sau real valorile maximale i minimale se schimb cu locurile, dac elementele arborelui A snt de tip string, elementele de lungime minimal se schimb cu locurile cu elementele de lungime maximal. 10. Scriei un program care determin pentru o clas list dublu lnuit urmtoarele funcii: member(x, L) care determin apartenena elementului x listei L, equal(L1, L2) care determin echivalena a dou liste L1 i L2, min_max(L, x, y) care determin elementele cu valoare minimal x i cu valoare maximal y din lista L, sum(L) care determin suma elementelor din lista L, substract(L) care determin diferena elementelor din lista L, multiply(L) care determin produsul elementelor din lista L, 86

sum(L,x,y) care determin suma elementelor din lista L n intervalul elementelor cu valorile x i y, n caz contrar sum(L,x,z)=0, print(L) care tiprete toate elementele listei L, readlist(L,fin) care creeaz lista L din elementele fiierului fin. 11. Scriei un program care efectueaz urmtoarelor operaii asupra unitilor bneti (de exemplu, lei, bani): adunarea, nmulirea cu un coeficient, scderea, mprirea la un coeficient, valoarea numeric a unitilor bneti s se tipreasc cu cuvinte. 12. Scriei un program care efectueaz urmtoarele operaii asupra numerelor fracionare: transformarea unui numr fracionar compus ntro fracie supraunitar, adunarea numerelor fracionare, nmulirea numerelor fracionare, scderea numerelor fracionare, mprirea numerelor fracionare. 13. Scriei un program care efectueaz urmtoarele operaii asupra unitilor de lungime (de exemplu, metri, centimetri, milimetri): adunarea, nmulirea cu un coeficient, scderea, mprirea la un coeficient, valoarea numeric a unitilor de lungime s se tipreasc cu cuvinte. 14. Scriei un program care efectueaz urmtoarele operaii asupra unitilor de greutate (de exemplu, tone, kilograme, grame): 87

adunarea, nmulirea cu un coeficient, scderea, mprirea la un coeficient, valoarea numeric a unitilor de greutate s se tipreasc cu cuvinte. 15. Scriei un program care efectueaz urmtoarele operaii asupra unitilor de timp (de exemplu, anul, luna, ziua, ceasul, minutul, secunda): adunarea, nmulirea cu un coeficient, scderea, mprirea la un coeficient, valoarea numeric a unitilor de timp s se tipreasc cu cuvinte.

Tema: Clase derivate, funcii virtuale, suprancrcarea funciilor i operatorilor. Scopul lucrrii: familiarizarea studenilor cu noiunile de clase derivate, funcii virtuale i redefinite, operatori suprancrcai, obiecte Consideraiile teoretice necesare: Clase derivate. Motenirea este o relaie ntre clase, caracterizat prin trecerea atributelor de la o clas, de baz, la alta, derivat. Clasele derivate posed toate caracteristicile clasei de baz. Ele pot fi mbogite att structural, ct i funcional. Totodat se observ o ierarhizare datorit faptului c exist posibilitatea ca o clas derivat s aib mai multe 88

Lucrarea de laborator nr. 3

clase de baz, ordinea importanei nivelelor rmnnd aceeai. Noiunea de derivare este o abstractizare a noiunii de motenire. O clas care adaug proprieti noi la o clas deja existent vom spune ca este derivata clasei de baz. Clasa derivat motenete toate datele i funciile membri ale clasei de baz; ea poate aduga noi date la cele existente i poate suprascrie sau aduga funcii membri. Clasa de baz nu este afectat n nici un fel n urma acestui proces de derivare i, ca urmare, nu trebuie recompilat. Declaraia i codul obiect snt suficiente pentru crearea clasei derivate, ceea ce permite reutilizarea i adaptarea uoar a codului deja existent, chiar dac fiierul surs nu este disponibil. Astfel, nu este necesar ca programatorul unei clase derivate s cunoasc modul de implementare a funciilor membri din componenta clasei de baz. n funcie de necesiti, derivarea claselor va fi un proces cu durat variabil. n acest sens, se prefer conceperea unor clase de baz simple, n locul unora dezvoltate. Dintr-o clas de baz pot fi derivate mai multe clase i fiecare clas derivat poate servi mai departe ca baz pentru alte clase derivate. Se poate astfel realiza o ierarhie de clase, care s modeleze adecvat sisteme complexe. Pornind de la clase simple i generale, fiecare nivel al ierarhiei acumuleaz caracteristicile claselor "printe" i le adaug un anumit grad de specializare. O clas poate s moteneasc simultan proprietile mai multor clase, procedur numit motenire multipl. Construirea ierarhiei de clase reprezint activitatea fundamental n realizarea unei aplicaii orientate obiect. Sintaxa simplificat a derivrii este: 89

class NumeClasDerivat : NumeClasaDeBaz n continuare vom deriva din clasa Point o clas specializat, GraphicPoint, care va "ti" s deseneze punctul pe ecran: class GraphicPoint : public Point {unsigned color; GraphicPoint(unsigned X, unsigned Y, unsigned Color); ~GraphicPoint(); void Draw(); void SetX(unsigned X); void SetY(unsigned Y); }; GraphicPoint::GraphicPoint(unsigned X, unsigned Y, unsigned Color) : Point(X, Y) {color = Color; } GraphicPoint::~GraphicPoint() {} GraphicPoint::Draw() { // apelarea funciilor grafice pentru desenarea punctului } GraphicPoint::SetX(unsignedX) {Point::SetX(); // funcia SetX() este membru a clasei de baz Draw(); } GraphicPoint::SetY(unsigned Y) {Point::SetY(); Draw(); } n exemplul de mai sus s-a adugat o variabil nou fa de clasa Point, color, pentru a putea memora culoarea cu care se face desenarea punctului. De asemenea, s-a suprascris constructorul i destructorul clasei printe. n constructorul derivat sa apelat constructorul original folosind construcia: 90

ClasaDerivat::ClasaDerivat() : ClasaDeBaz() n clasa GraphicPoint s-a adugat o funcie membru nou, Draw(), care deseneaz punctul pe ecran. Am suprascris funciile SetX() i SetY(), apelnd n ambele funciile originale, utiliznd sintaxa: ClasaDeBaz::FuncieMembru() apelnd apoi funcie de desenare, Draw(). Regulile de funcionare ale constructorilor i destructorilor, descrise n lucrarea de laborator nr. 2, rmn valabile i n cazul claselor derivate, inndu-se cont de urmtoarele observaii privind ordinea de apelare a acestora: la instanierea clasei derivate, se apeleaz mai nti constructorul clasei de baz, apoi se apeleaz propriul constructor. la distrugerea unui obiect al unei clase derivate, este apelat mai nti propriul destructor, i apoi destructorul clasei de baz (n ordine invers crerii obiectului). Controlul accesului la clase Limbajul C++ permite controlul accesului la membrii claselor. n acest scop s-au creat trei specificatori de control al accesului: public, membrul poate fi accesat de orice funcie din domeniul declaraiei clasei; private, membrul este accesibil numai funciilor membri i prietene ale clasei; protected, similar cu private, ns accesul se extinde i la funciile membri i prietene ale claselor derivate. O funcie membru a unei clase are acces la toi membrii clasei, indiferent de specificatorul de acces. Aa dar, sintaxa declaraiei unei clase derivate, 91

incluznd controlul accesului, este: class NumeClasDerivat : SpecificatorAcces NumeClasaDeBaz unde SpecificatorAcces poate fi public sau private. Accesul Atributul Modificator motenit de Accesul din din clasa clasa de acces exterior de baz derivat
private protected public private protected public private private private public public public inaccesibil private private inaccesibil protected public inaccesibil inaccesibil inaccesibil inaccesibil inaccesibil accesibil

Pentru a oferi clasei derivate acces la un membru al clasei de baz, acesta trebuie declarat protected sau public. Elementele declarate cu specificatorul public n clasa de baz snt accesibile elementelor n clasa derivat. Elementele declarate cu specificatorul private sau protected n clasa de baz nu snt accesibile n mod direct elementelor din clasa derivat, ele pot fi accesate doar prin unele funcii declarate special (care returneaz valorile elementelor din clasa de baz descrise cu specificatorul private sau protected). Pentru respectarea principiului incapsulrii datelor, datele membri pentru care se ofer acces claselor derivate se declar n clasa de baz cu atributul protected. De asemenea, pentru a conserva dreptul de acces n urma derivrii, se utilizeaz derivarea public. Accesul poate fi stopat pe orice nivel al ierarhiei de clase printr-o derivare cu specificatorul de acces private. Stabilirea atributelor de acces ale membrilor unei clase, precum i ale derivrilor, dezvoltarea ierarhiei de clase, trebuie s se fac astfel ca s nu afecteze incapsularea datelor. 92

S cercetm exemplul urmtor, completat cu specificatori de acces: class Point { protected: unsigned x, y; public: Point(); Point(unsigned X, unsigned Y); ~Point(); unsigned long Arie(); unsigned GetX(); unsigned GetY(); void SetX(unsigned X); void SetY(unsigned Y); }; class GraphicPoint : public Point {unsigned color; public: GraphicPoint(unsigned X, unsigned Y, unsigned Color); ~GraphicPoint(); void Draw(); void SetX(unsigned X); void SetY(unsigned Y); }; Variabilele membri x i y snt declarate protected, aa nct vor fi vizibile i vor avea acelai atribut n clasa GraphicPoint (dei nu snt utilizate). n mod normal, x i y ar trebui sa fie declarai private, ntruct nu snt utilizai dect n interiorul clasei Point. Funciile din GraphicPoint nu acceseaz aceti doi membri direct, ci prin intermediul metodelor publice de accesare a lor oferite de clasa Point. Implicit, dac nu este utilizat nici un specificator de acces, membrii snt considerai private. void main() {Point *p; 93

p = new Point; p->x = 5; // operaie imposibil: x este membru privat p->y = 8; // operaie imposibil: y este membru privat p->SetX(5); // corect: acces la variabila x prin intermediul //funciei SetX() p->SetY(8); // corect: acces la variabila x prin intermediul //funciei SetY() printf("Aria = %d\n", p->Aria()); delete p; } Deci din exteriorul unei clase nu pot fi accesate datele membri private sau protected. Atunci cnd funciile unei clase de baz snt rescrise ntr-una derivat, spunem c aceste clase snt polimorfe, vom avea o singur denumire i mai multe aciuni, adic, o singur interfa cu metode multiple. Aceast noiune legat de derivare este cea de suprancrcare sau suprascriere a funciilor membri. Ea se refer la redefinirea unor funcii a clasei de baz n clasa derivat. Funciile din clasa printe snt n continuare accesibile n clasa derivat. S vedem cum arat obiectele polimorfe. Pentru aceasta, determinm clasele de baz Punct i cea derivat Cerc i funcia membru Aria(), ce va avea ca efect determinarea ariei obiectului respectiv: #include <stdio.h> class Punct { float x,y; public: void Incarc_punct(float xi,float yi) {x=xi;y=yi;}; virtual float Aria() { return 0.0; }; }; const float pi=3.14159; class Cerc : public Punct {float raza; 94

public: void Incarc_raza (float r) { raza=r; } float Aria(){ return pi*raza*raza; } ; // funcie redefinit }; void main() { Punct p; float a=p.Aria(); printf("Aria unui punct este: %5.2f\n",a); Cerc c; c.Incarc_raza(3.65637); a=c.Aria(); printf("Aria cercului este: %5.2f\n",a); } Rezultatul ndeplinirii programului: Aria unui punct este: 0.00 Aria cercului este: 42.00 Clasa Cerc este derivat de la clasa Punct. Cerc i Punct snt obiecte din aceeai categorie, snt polimorfe. Pentru a obine obiecte polimorfe, va trebui s construim o ierarhie de clase i apoi s redefinim funciile, aparinnd clasei de baz n clasele derivate. Aceast operaie poate fi realizat n dou moduri: rescriind funciile respective, efectund, deci o nou implementare a acestora, utiliznd funciile virtuale. S vedem cum are loc rescrierea funciilor. Este evident c funcia membru Aria() a clasei Punct este motenit de clasa Cerc, dar nu convine din punctul de vedere al valorii returnate. Astfel, s-a impus reimplementarea acesteia. Punct *p; Cerc c; p=&c; float aria=p->Aria (); n instruciunea n care este apelat funcia Aria() 95

este apelat funcia Cerc::Aria(), p indic spre obiectul c de tipul Cerc. Ataarea codului unei funcii la numele su poate fi efectuat static sau dinamic. Acest proces poart denumirea de identificare a funciilor, existnd, deci dou metode de identificare: static i dinamic. Identificarea static are loc la un apel normal de funcie. Compilatorul preia numele funciei, argumentele sale i, n cazul n care aceasta este membru, numele clasei obiectului. Identificarea dinamic se va realiza n momentul execuiei, funcia nefiind identificat. Cum? O posibilitate ar fi de a utiliza pointeri la funcii, astfel, c doar n momentul rulrii vom ti funcia, spre care a indicat acel pointer. Vom defini un pointer n interiorul clasei Punct, ce va indica spre funcia Aria(). Reinem c acest pointer va fi motenit de ctre Cerc. Dup crearea unui obiect de tip Punct sau Cerc, vom iniializa pointerul spre funcia corect. O a doua posibilitate este aceea de a utiliza funciile virtuale. Funciile virtuale snt utilizate pentru reimplementarea unor funcii membri ale unor clase de baz, astfel, nct aceast redefinire de funcii s funcioneze n ambele situaii. const float pi=3.14159; class Cilindru : public Cerc { float inaltime; public: void Incarca_ inaltime(int h) {inaltime =h;}; float Aria() ; }; float Cilindru::Aria() {return 2*pi*raza*inaltime +2 *Cerc::Aria(); } n implementarea noii versiuni a funciei membri Cilindru::Aria() se utilizeaz versiunea anterioar 96

motenit de la clasa de baz Punct. Evident c Punct::Aria() nu va modifica rezultatul furnizat, aportul su la aceasta fiind nul, dar se sugereaz c este permis apelarea funciilor motenite pentru oricare nivel al ierarhiei, innd cont de nivelurile de protecie ( private, protected i public). n procesul de lucru cu clase derivate putem foarte uor grei, transformnd o funcie virtual n funcie redefinit datorit faptului c cele dou categorii se aseamn. Totui, exist cteva diferene dintre aceste dou categorii de funcii: funciile membri redefinite snt ataate obiectului, urmnd procedura static (la compilare), n timp ce funciile virtuale fac parte din cea de a doua categorie, legturile cu obiectele fiind realizate dinamic (n timpul execuiei); funciile membri redefinite pot avea liste diferite de parametri, n timp ce funciile virtuale trebuie s posede aceeai list de parametri. Dac, n cadrul ierarhiei de clase, funciile nu se comport exact cum vrem noi, va trebui s verificm toate funciile virtuale, pentru a ne asigura c, ntradevr, snt virtuale i nu redefinite. # include <stdio.h> class scade {public: virtual int executa (unsigned char c) { return --c; }; }; class aduna : public scade { public: int executa ( char c) { return ++c; }; }; void main() {scade *p=new aduna; int k=p->executa(43); 97

printf(k = %d \n,k); } Rezultatul ndeplinirii programului: k = 42 n acest exemplu programul va afia rspunsul 42. Versiunea clasei scade accept un argument unsigned char, iar versiunea clasei aduna un argument de tip char. Datorit acestei schimb de tip, a doua versiune a funciei executa() nu este virtual, ea este redefinit. Chiar dac prin intermediul lui p acionm asupra unui obiect aduna, apelnd funcia executa(), ne referim la versiunea scade. Deci programul va afia valoarea 42. O funcie operator are aceleai componente pe care le are orice funcie, include un nume, un tip returnat, argumente, corp i, eventual, apartenena la o clas. Exist trei elemente care trebuie stabilite la declararea operatorului, i anume, este operator unar sau binar, este postfixat sau prefixat ca poziie i este funcie membru sau nu domeniu de aciune. Funciile operator membri vor avea cu un argument mai puin dect cele non-membri. Apelul unui astfel de operator membru va fi p+=5; sau p. operator+=(5); Redefinirea operatorilor Limbajul C++ permite programatorilor s defineasc operatori pentru a lucra cu propriile clase. Sintaxa suprancrcrii unui operator este: operator Simbol unde Simbol este simbolul oricrui operator C++, exceptnd: . *- adresare la componenta prin pointer, ::operatorul de rezoluie, () ?:- operatorul condiional, operatorul sizeof, etc.. Aceast definire se face n cadrul clasei, ntocmai ca o funcie membru (vezi Tabelul 1.) 98

Exist dou variante de definire a operatorilor: ca funcie membru a clasei; ca funcie prieten a clasei. Tabelul 1. Tipul Simbolul Asociativitate Observaii operatorului operatorului Binar Unar Unar Unar Binar Binar Binar
() [] -> + - ~ * & (tip) ++ --

-> <<<-> -> <-

Se definesc ca funcii membri Nu se poate distinge ntre pre- i postfixare Poate fi supradefinit i pentru o clas

new, delete -> * / % + - & | && || << >> < <= > >= == != = += -= *= /= %= &= ^= |= <<= >>=

Se definesc ca funcii membri

, Binar -> Pentru exemplificare, vom extinde clasa Point cu utilizarea unor operatori. class Point {// ... Point& operator += (Point p); Point& operator -= (Point p); Point operator + (Point p); Point operator - (Point p); Point& operator = (Point p); int operator == (Point p); int operator != (Point p); 99

int operator < (Point p); int operator > (Point p); int operator <= (Point p); int operator >= (Point p); }; Point& Point::operator += (Point p) {x += p.x; y += p.y; return *this;} Point& Point::operator -= (Point p) {x -= p.x; y -= p.y; return *this;} Point Point::operator + (Point p) {return Point(x + p.x, y + p.y);} Point Point::operator - (Point p) {return Point(x -p.x, y -p.y);} int Point::operator == (Point p) {return x == p.x && y == p.y;} int Point::operator != (Point p) {return !(*this == p);} int Point::operator < (Point p) {return x < p.x && y < p.y;} int Point::operator > (Point p) {return x > p.x && y > p.y;} int Point::operator <= (Point p) {return x <= p.x && y <= p.y;} int Point::operator >= (Point p) {return x >=p.x && y >= p.y;} Am utilizat mai sus varianta cu funcii membri. Vom descrie implementarea operatorului + folosind cea de-a doua variant. class Point { // ... friend Point operator + (Point p1, Point p2); }; Point operator + (Point p1, Point p2) {return Point(p1.x + p2.x, p1.y + p2.y);} Definirea operatorilor ca funcii membri a unei 100

clase prezint o restricie major: primul operand este obligatoriu s fie de tipul clasa respectiv. n limbajul C++ supradefinirea operatorilor este supus unui set de restricii: nu este permis introducerea de noi simboluri de operatori; patru operatori nu pot fi redefinii (vezi mai sus); caracteristicile operatorilor nu pot fi schimbate: pluralitatea (nu se poate supradefini un operator unar ca operator binar sau invers), precedena i asociativitatea, prioritatea lor; funcia operator trebuie sa aib cel puin un parametru de tipul clasa cruia i este asociat operatorul supradefinit. Programatorul are libertatea de a alege natura operaiei realizate de un operator, ns este recomandat ca noua operaie s fie apropiat de semnificaia iniial. Redefinirea operatorului +=. Aciunea acestuia este aceea de a aduga o valoare unui numr de tip char, int, float sau double. Putem redefini acest operator, pentru a opera asupra obiectelor, de exemplu, de tip persoana. # include <stdio.h> # include <string.h> class persoana { private: char nume[40]; long int telefon; int virsta; public: persoana () {strcpy(nume,'\0');telefon=0; virsta=0;}; persoana(long int Telefon) { telefon= Telefon; }; persoana(char *Nume) { strcpy(nume,Nume); }; 101

persoana(int Virsta) { virsta= Virsta; }; persoana(char *n, long l) { strcpy(nume,n);telefon=l; }; void Citeste(char *n,long t=0,int v=0) { strcpy(nume,n); telefon=t; virsta=v;}; void Tipar(persoana s) {printf("\n nume %s, telefon %ld, virsta %d ",s.nume, s.telefon, s.virsta);}; int Seteaza_Virsta(int v) { return(virsta=v); }; }; void main() { persoana p; p.Citeste("Cristina",234567,p.Seteaza_Virsta(20)); p.Tipar(p); } Rezultatul ndeplinirii programului: nume Cristina, telefon 234567, virsta 20 Pentru aceasta, vom defini o funcie avnd numele operator +=() cu doi parametri. Aceti parametri snt cei doi operanzi ai operatorului +=. void operator +=(persoana &p, int v) { p.Seteaza_Virsta(p.Virsta()+v); } Utilizarea operatorului definit se realizeaz prin iniializarea persoana p('Mircea" , 0 , 9) ; i apelul p+=5; sau apelndu-l ca funcie operator+=(p,5); Bineneles, funcia operator poate fi membru a clasei asupra creia acioneaz class persoana { public: void operator+=(int v); }; void persoana::operator+=(int v) {Seteaza_Varsta(varsta+v); } Redefinirea operatorului =. Operatorul = este deja 102

predefinit n C++, pentru operanzi de tip clas. Dac nu este supradefinit, atribuirea se face membru cu membru n mod similar cu iniializarea obiectului efectuat de ctre compilator. n caz de o atribuire specific a clasei, operatorul = poate fi supradefinit. Point& Point::operator = (Point p) {x = p.x; y =p.y; return *this;} Redefinirea operatorului [].Operatorul de indexare [] se definete astfel: int &operator[](int) De exemplu Point Array_Point::operator []= (Point *p, int j) {return p[j];} Redefinirea operatorilor new i delete poate fi efectuat pentru a realiza operaii specializate de alocare/eliberare dinamic a memoriei. Funcia operator new trebuie sa primeasc un argument de tipul size_t care s precizeze dimensiunea n octei a obiectului alocat i s returneze un pointer de tip void coninnd adresa zonei alocate: void *operator new(size_t) cu meniunea c size_t este definit n stdlib.h. Chiar dac parametrul de tip size_t este obligatoriu, calculul dimensiunii obiectului n cauz i generarea sa se face de ctre compilator. Funcia operator delete trebuie sa primeasc ca prim parametru un pointer de tipul clasei n cauz sau void, coninnd adresa obiectului de distrus, i un al doilea parametru opional de tip size_t. Funcia nu ntoarce nici un rezultat. void operator delete(void *, size_t) Operatorii new i delete supradefinii pstreaz toate proprietile operatorilor new i delete standard. Redefinirea operatorilor unari poate fi efectuat utiliznd o funcie membru fr parametri sau o 103

funcie prieten cu un parametru de tipul clasei respective. Pentru operatorii ++ i -- dispare distincia ntre utilizarea ca prefix i cea ca postfix, de exemplu, intre x++ i ++x, respectiv, x-- i x. De exemplu: Point Point::operator++ () {x++; y++; return *this;} Conversii de tip definite de programator. n limbajul C+ + este definit un set de reguli de conversie pentru tipurile de baz de date. C++ permite definirea de reguli de conversie pentru clasele create de programator. Regulile astfel definite snt supuse unor restricii: ntr-un ir de conversii nu este admis dect o singur conversie definit de programator; se recurge la aceste conversii numai dup ce se verific existena altor soluii (de exemplu, pentru o atribuire, se verific mai nti suprancrcarea operatorului de atribuire i n lipsa acestuia se face conversia). Exista dou metode de a realiza conversii de tip. Suprancrcarea operatorului unar "cast". Sintaxa este: operator TipData() respectiv: operator (TipData) Operatorul "cast" este unar, aadar, are un singur parametru, adresa obiectului n cauz, i ntoarce un rezultat de tipul operatorului. Ca urmare, prin aceast metod se pot defini numai conversii dintrun tip clas ntr-un tip de baz sau un alt tip clas. n cazul conversiei dintr-un tip clas ntr-un alt 104

tip clas, funcia operator trebuie s aib acces la datele membri ale clasei de la care se face conversia, deci trebuie declarat prieten a clasei respective. Conversiile de tip folosind constructori const n definirea unui constructor ce primete ca parametru tipul de la care se face conversia. Constructorul ntoarce ntotdeauna ca rezultat un obiect de tipul clasei de care aparine, ca urmare, folosind aceast metod se pot realiza numai conversii dintr-un tip de baz sau un tip clas ntr-un tip clas. n cazul conversiei dintr-un tip clas ntr-un alt tip clas, constructorul trebuie s aib acces la datele membri ale clasei de la care se face conversia, deci trebuie declarat prieten a clasei respective. Constructorul de copiere. O situaie care poate aprea deseori este iniializarea unui obiect cu datele membri ale unui obiect de acelai tip. Exista totui situaii n care operatorul de atribuire nu poate fi utilizat, de exemplu, la transferul unui obiect ca parametru sau la crearea unei instane temporare a unei clase, cnd copierea membru cu membru nu este adecvat. Pentru a rezolva aceste situaii, n limbajul C++ a fost introdus un constructor special, numit constructorul de copiere. Sintaxa este: NumeClas::NumeClas (NumeClas &NumeObiectSurs) n continuare vom completa clasa Point cu un constructor de copiere: Point::Point(Point &p) {p.x = x; p.y = y;} n cazul n care clasa nu dispune de constructor de copiere, compilatorul genereaz automat un constructor de copiere care realizeaz copierea 105

membru cu membru. Clase abstracte. n limbajul C++ exist posibilitatea de a defini clase generale, care snt destinate crerii de noi clase prin derivare. Ele nu pot fi instaniate i utilizate ca atare. Acest gen de clase se numesc clase abstracte. Ele se constituie ca baz n cadrul elaborrii de ierarhii de clase, putnd fi folosite, de exemplu, pentru a impune anumite restricii n realizarea claselor derivate. n vederea construirii unor astfel de clase, s-a introdus conceptul de funcie virtual pur. O astfel de funcie este declarat n cadrul clasei, dar nu este definit. O clas care conine o funcie virtual pur este considerat abstract. Sintaxa definirii acestor funcii este: virtual TipData NumeFunctieMembru() = 0 Funciile virtuale pure trebuie definite n clasele derivate, altfel i acestea vor fi considerate abstracte. Membrii statici ai unei clase. n mod normal, datele membri ale unei clase snt alocate n cadrul fiecrui obiect. n C++, se pot defini date membri cu o comportare special, numite date statice. Acestea snt alocate o singur dat, existnd sub forma unei singuri copii, comun tuturor obiectelor de tipul clasei respective, iar crearea, iniializarea i accesul la aceste date snt independente de obiectele clasei. Sintaxa este: static DeclarareMembru Funciile membri statice efectueaz, de asemenea, operaii care nu snt asociate obiectelor individuale, ci ntregii clase. Funciile exterioare clasei pot accesa membrii statici ale acesteia astfel: NumeClas::NumeMembru Obiect::NumeMembru Funciile membri statice nu primesc ca parametru implicit adresa unui obiect, aadar, n 106

cadrul lor cuvntul-cheie this nu poate fi utilizat. De asemenea, membrii normali ai clasei nu pot fi referii dect specificnd numele unui obiect. ntrebrile pentru verificarea cunotinelor: 1. Care dintre instruciunile de mai jos snt legale? a: class sir {private: static char eos; char *continut; int dimensiune; public: sir(int sz); void copy(sir &s); }; b: char sir::eos=Ox1A; void sir::copy(sir 63) delete continut; continut = new char[3.dimensiune]; char *p=continut; char *q=s.continut; while <*qt=eos) c: *p++=*q++; *p=eos; void main() {sir s(80);} d: sir::eos=0; e: s.eos='$'; 2. Dup cum tim, atunci cnd redefinim funcii, listele de argumente trebuie s difere de la implementare la implementare. De ce acest lucru nu se impune atunci cnd redefinim funcia Aria() din cadrul claselor Punct i Cerc? 3. Dac redefinim operatorul+ ataat clasei persoana i facem aceast funcie membri, cte argumente 107

explicite va avea aceasta? 4. Scriei o funcie operator membru a clasei persoana pentru a rescrie operatorul ==. Exist mai multe variante ale acestei funcii? Temele pentru acas: 1. Ce va afia programul urmtor? #include<iostream.h> class Bunic { public : virtual void sfat() { cout<< Distreaz-te; } }; class Tata : public Bunic { public: void sfat() { cout "F-i leciile'\n"; } } class Fiu : public Tata { public: void sfat() { Bunic::sfat(); } }; void main() ( Fiu lon; lon.sfat(); } 2. Ce se va afia la ecran dup executarea programului urmtor? #include <iostream.h> class Mesaje_bune { public: virtual void act1() 108 { cout"Prinul vede prinesa\n"; act2(); } 108

void act2() { cout"Prinul o srut\n"; act3(); } virtual void act3() { cout"Prinesa se trezete\n"; act4(); } virtual void act4 () { cout"Si au trit fericii . . . \n" ; act5(); ) void act5() { cout "Sfrit '\n"; } }; class Mesaje rele : public Mesaje bune { public: void act3() { cout"Prinesa rmne eapn\ n" ;act4 () ; } void act4() { cout"Prinul fuge ngrozit\n";act5(); } void act5() { cout"Un sfrit, nefericit' \n" ; } }; void main () { char c; Mesaje bune *mes; cout"Care variant doriii s vedei (L/B) ?\n" ; cinc; if (p=='L') | | (c=='l)) mes=new Mesaje_bune; else mes=new Mesaje rele; mes->act1(); delete mes;} Temele pentru lucrri de laborator: 109

1. Scriei un program care ar defini clasa de baz num. n aceast clas determinai un numr ntreg i funcia shownum(). Creai dou clase derivate outhex i outoct care motenesc num. Funcia shownum() se va redefini n clasele derivate astfel ca ea s afieze la display respectiv, valorile din sistemele de numeraie hexazecimal i octal, s se efectueze toate operaiile aritmetice asupra numerelor din aceste sisteme de numeraie. 2. Scriei un program care care ar defini clasa de baz num. n aceast clas determinai un numr ntreg i funcia shownum(). Creai clasa derivat outbin care motenete num. Funcia shownum() se va redefini n clasa derivat astfel ca ea s afieze la display valorile din sistemul de numeraie binar, s se efectueze toate operaiile aritmetice i logice asupra numerelor binare. 3. Scriei un program care definete clasa de baz distance pentru a pstra n variabila de tipul double distana dintre dou puncte. n clasa distance s se creeze funcia virtual trav_time(), care afieaz valoarea timpului necesar pentru a parcurge distana msurat n mile, viteza fiind de 60 mile/or. n clasa derivat metric se va redefini funcia trav_time(), astfel ca ea s afieze timpul necesar pentru parcurgerea distanei msurat n kilometri, viteza fiind de 100 km/or. 4. Scriei un program care ar defini clasa de baz vector. n aceast clas determinai un vector de numere ntregi i funcia showvector(). Creai clasa derivat array care motenete vector. Funcia showvector() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile tabloului pe linii i pe coloane. S se calculeze suma elementelor tabloului, s se efectueze operaiile algebrice 110

5.

6.

7.

asupra a dou tablouri (adunarea, scderea, nmulirea). Scriei un program care ar defini clasa de baz punct. n aceast clas determinai un punct n plan i funcia showpoint(). Creai clasa derivat poligon care motenete punct. Funcia showpoint() se va redefini n clasa derivat, astfel ca ea s afieze la display punctele cu coordonatele date. S se determine poziia punctului fa de celelalte puncte din poligon, s se determine distana minim de la punctul dat pn la primul punct ce aparine poligonului. Scriei un program care ar defini clasa de baz string. n aceast clas determinai funcia showstring(). Creai clasa derivat newstring care motenete string. Funcia showstring() se va redefini n clasa derivat, astfel ca ea s afieze la display simbolurile prin codurile lor interioare. S se efectueze urmtoarele operaii asupra codurilor: s se determine spaiul pentru stringul dat, s se compare dou stringuri, s se extrag un substring din stringul dat, s se lichideze un substring din stringul dat din poziia dat, s se inverseze un string dat. s se caute un substring din stringul dat. Scriei un program care ar defini clasa de baz bit. n aceast clas determinai valorile logice true i false i funcia showbit(). Creai clasa derivat outbit care motenete bit. Funcia showbit() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile unui ir de bii. S se efectueze urmtoarele operaii asupra irului de bii: s se determine lungimea irului de bii, s se determine spaiul pentru irul de bii dat, 111

8.

9.

s se compare dou iruri de bii, s se extrag un subir de bii din irul de bii dat, s se lichideze un subir de bii din irul de bii dat din poziia dat, s se inverseze un ir de bii dat, s se caute un subir de bii din irul de bii dat. Scriei un program care ar defini clasa de baz set_bit. n aceast clas determinai funcia showset_bit(). Creai clasa derivat mulime care motenete set_bit. Funcia showset_bit() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile unei mulimi. S se efectueze urmtoarele operaii asupra mulimii: s se determine numrul de elemente a mulimii s se determine spaiul pentru mulimea dat, s se compare dou mulimi, s se extrag o submulime din mulimea dat, s se adauge un nou element la mulime, s se tearg un element din mulime, s se caute o submulime din mulimea dat. Scriei un program care ar defini clasa de baz int. n aceast clas determinai valorile ntregi i funcia showint(). Creai clasa derivat longint care motenete int. Funcia showint() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile unui numr de tip longint..S se efectueze urmtoarele operaii asupra tipului longint: s se determine numrul de cifre din numrul dat, s se compare dou numere longint, s se extrag numerele de tip int din numrul longint (partea inferioar i partea superioar), s se efectueze operaiile algebrice asupra numerelor date, s se inverseze un numr de tip longint (partea inferioar cu cea superioar). 112

10. Scriei un program care ar defini clasa de baz real. n aceast clas determinai valorile reale i funcia showreal(). Creai clasa derivat double care motenete real. Funcia showreal() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile unui numr de tip double. S se efectueze urmtoarele operaii asupra tipului double: s se determine numrul de cifre din partea ntreag a numrului dat, s se determine numrul de cifre din partea fracionar a numrului dat, s se compare dou numere double, s se transforme numrul dat ntr-un ir de caractere, s se efectueze operaiile algebrice asupra numerelor date. 11. Scriei un program care ar defini clasa de baz int n sistemul de numeraie p. n aceast clas determinai valorile ntregi i funcia showint(). Creai clasa derivat longint n sistemul de numeraie p care motenete int. Funcia showint() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile unui numr de tip int. n sistemul de numeraie p. S se efectueze urmtoarele operaii asupra tipului longint n sistemul de numeraie p: s se determine numrul de cifre din numrul dat, s se compare dou numere longint, s se extrag numerele de tip int din numrul longint (partea inferioar i partea superioar), s se efectueze operaiile algebrice asupra numerelor date, s se inverseze un numr de tip longint (partea inferioar cu cea superioar). 12. Scriei un program care ar defini clasa de baz real 113

n sistemul de numeraie p. n aceast clas determinai valorile reale n sistemul de numeraie p i funcia showreal(). Creai clasa derivat double n sistemul de numeraie p care motenete real n sistemul de numeraie p. Funcia showdouble() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile unui numr de tip double n sistemul de numeraie. S se efectueze urmtoarele operaii asupra tipului double n sistemul de numeraie p: s se determine numrul de cifre din partea ntreag a numrului dat, s se determine numrul de cifre din partea fracionar a numrului dat, s se compare dou numere double, s se transforme numrul dat ntr-un ir de caractere, s se efectueze operaiile algebrice asupra numerelor date. 13. Scriei un program care ar defini clasa de baz int. n aceast clas determinai valorile ntregi i funcia showint(). Creai clasa derivat fraction care motenete int. Funcia showfraction() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile unui numr fracionar. S se efectueze urmtoarele operaii asupra numerelor fracionare: transformarea unui numr fracionar mixt ntr-o fracie supraunitar, adunarea numerelor fracionare, nmulirea numerelor fracionare, scderea numerelor fracionare, mprirea numerelor fracionare. 14. Scriei un program care ar defini clasa de baz list. n aceast clas determinai valorile ntregi ale 114

listei i funcia showlist(). Creai clasa derivat stiva care motenete list. Funcia showlist() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile unei stive. S se efectueze urmtoarele operaii asupra stivei: empty(S) care cur stiva S, if_is_empty(S) care verific dac stiva S este vid, pop(S,x) care adaug elementul x n stiva S, push(S,x) care scoate elementul x din stiva S, error (k) care indic eroarea cu numrul k (k=1 dac stiva este suprancrcat, k=2 dac stiva este vid). 15. Scriei un program care ar defini clasa de baz int. n aceast clas determinai valorile ntregi i funcia showdate(). Creai clasa derivat date care motenete int. Funcia showdate() se va redefini n clasa derivat, astfel ca ea s afieze la display valorile unei date. S se efectueze urmtoarele operaii asupra datei: adunarea, nmulirea cu un coeficient, scderea, mprirea la un coeficient, valoarea datei calendaristice s se tipreasc cu cuvinte.

115

Tema: Clase derivate cu motenire multipl.

Lucrarea de laborator nr. 4.

Scopul lucrrii: familiarizarea studenilor cu clasele derivate cu motenire multipl. Consideraiile teoretice necesare: Motenirea multipl Limbajul C++ permite crearea de clase care motenesc proprietile mai multor clase de baz. Dintr-o clas de baz pot fi derivate mai multe clase i fiecare clas derivat poate servi mai departe ca baz pentru alte clase derivate. Se poate astfel realiza o ierarhie de clase, care s modeleze adecvat sisteme complexe. Pornind de la clase simple i generale, fiecare nivel al ierarhiei acumuleaz caracteristicile claselor "printe" i le adaug un anumit grad de specializare. O clas poate s moteneasc simultan proprietile mai multor clase, procedur numit motenire multipl. Construirea ierarhiei de clase reprezint activitatea fundamental de realizare a unei aplicaii orientate obiect. Motenirea multipl permite formarea unei ierarhiei de clase. Dac derivarea normal duce la construirea unei ierarhii de tip arbore, derivarea multipl va genera ierarhii de tip graf. Sintaxa complet pentru operaia de derivare este urmtoarea: class NumeClasDerivat : ListaClaseDeBaz unde ListaClaseDeBaz este: SpecificatorAcces NumeClasaDeBaz, ... Clase virtuale. Utilizarea motenirii multiple se poate complica odat cu creterea dimensiunii ierarhiei de clase. O situaie care poate apare este derivarea din dou clase de baz, Clasa1 i Clasa2, care 116

la rndul lor snt derivate dintr-o clas comun, ClasaDeBaz. n acest caz, noua clas, ClasaNou, va conine datele membri ale clasei ClasaDeBaz duplicate. Dac prezena acestor date duplicate este util, ele pot fi distinse evident cu ajutorul operatorului de rezoluie, ::. Totui, n cele mai multe cazuri, aceast duplicare nu este necesar i duce la consum inutil de memorie. De aceea, n C++ a fost creat un mecanism care va evita aceast situaie, prin intermediul conceptului de clas virtual. Sintaxa este: class NumeClasaDerivat : SpecificatorAcces virtual NumeClasaDeBaz Aceast declaraie nu afecteaz clasa n cauz, ci numai clasele derivate din aceasta. Astfel, clasele Clasa1 i Clasa2 considerate vor fi declarate virtuale. Declararea virtual a acestor clase va afecta definirea constructorului clasei ClasaNou, deoarece compilatorul nu poate hotr care date vor fi transferate ctre constructorul ClasaDeBaz, specificate de constructorii Clasa1 i Clasa2. Constructorul ClasaNou va trebui modificat astfel, nct s trimit datele pentru constructorul ClasaDeBaz. ntr-o ierarhie de clase derivate, constructorul clasei virtuale este ntotdeauna apelat primul. De exemplu, class A { }; class B1: virtual public A {} class B2: virtual public A {}; class C1: public B1, public B2 { }; Grafic acest exemplu se poate de prezentat n modul urmtor: class A 117

virtual class B1 class C

virtual class B2

ntrebrile pentru verificarea cunotinelor: 1. Ce se numete motenire multipl? Dai exemple. 2. Ce se numete clas virtual? Dai exemple. Temele pentru acas: 1. Ce se va afia la ecran dup rularea programului urmtor? #include <iostream.h> cassBl { public : Bl() { cout "Constructorul B1 \n";} ~B1() { cout "Destructorul B1 \n";} class B2 { int b; public : B2 ( ) { cout "Constructorul B2\n"; } ~B2 ( ) { cout "Destructorul B2 \n"; } }; // Motenirea a dou clase de baz KOHCTp ( cout " yKTOpa cout main () { C ob; return 0; class D : public Bl, public B2 { public: 118

D() { cout "Constructorul D \n";} ~D() { cout "Destructorul D\n";} }; void main() { D obj; } 2. Care va fi rezultatul lansrii programului de mai jos? Corectai greelile din program. #include <iostream.h> class A { int i; public : Al(int a=0) { i=a;cout "Constructorul A \n";} ~B1() { cout "Destructorul B1 \n"; class B { int j; public : B2 (int a=0 ) { j=a; cout "Constructorul B2\n"; } ~B2 ( ) { cout "Destructorul B2 \n"; } }; // Motenirea a dou clase de baz KOHCTp ( cout " yKTOpa cout main () { C ob; return 0; class C : public Bl, public B2 { int k, public: /* Scriei constructorul pentru clasa C astfel ca el s activeze constructorii claselor de baz A i B*/ c() { cout "Constructorul D \n"; 119

~C() { cout "Destructorul D\n"; }; void main() { C obj; } Temele pentru lucrri de laborator: 1. La un depozit a fost adus marf de export = {denumire, ara de exportare, cantitatea, preul, data livrrii}. i anume, procesor = {tip, viteza de lucru}, monitor = {tipul, diagonala}, wincester = {firma, dimensiune}. S se determine suma de bani necesar pentru achitarea mrfii aduse. Plata se efectueaz n lei innd cont de cursul valutar {valuta, data, vnzarea, cumprarea}. 2. S se efectueze urmtoarele operaii asupra figurilor geometrice: de rotire a figurii, de mutare pe ecran, de colorare, de decupare a unei pri din obiect, de a scrie text n figur. Figurile geometrice au urmtoarele structuri:. triunghi = {vrfl, vrf2, vrf3}, dreptunghi ={vrfl, vrf2}, cerc = {centru, raza}, elipsa = (centru, raza1, raza2}. 3. S se calculeze ct timp a trecut de la nceputul erei noastre. Structurile de baz snt deceniu = {veac, era}, timp = {ora, minutul, secunda}, data = {zi, luna, an}}. 4. S se efectueze operaii algebrice i conversii asupra urmtoarelor structuri: numr complex= {partea real, partea imaginar}, numr raional= {numrtor, numitor}, punct din plan = {coordonata x, coordonata y}. 5. S se efectueze operaii de conversie a urmtoarelor structuri: punct din scatiu = { coordonata x, coordonata y. coordonata z} i coordonate polare = {raza, unghiul}, unde unghi = {grad, minuta, secunda), 6. ntr-un ora a fost construit un centru de prestare a 120

serviciilor telefonice. Informaia despre ora are urmtoarea structur ora= {denumire, cod telefonic, cod potal}. La acest centru au fost conectai 100 de utilizatori cu adresa: {strada, numrul casei, scara, apartamentul}. Fiecare utilizator este abonat telefonic, pentru care se indic {numele, adresa, numrul telefonului, data montrii}. Informaia despre convorbirea telefonic a utilizatorului are urmtoarea structur: convorbire telefonic = {codul rii, numrul solicitatului, oraul, numrul solicitantului, numrul de minute (durata conversaiei)}. S se calculeze plata pentru achitarea serviciilor prestate acestor utilizatori dac ei au inut convorbiri telefonice cu alte orae. Plata pentru servicii se primete n diferite uniti valutare. cost = {lei., bani}, sau {ruble, copeici}, sau {dolari, ceni}.... 7. La competiii sportive de diferite probe sportive au participat persoane din mai multe ri. Denumirea rii are urmtoarea structur ar= {denumire, cod telefonic, cod potal}. Fiecare participant are greutatea = {kilogram, gram} i poate participa numai la una din probele sportive propuse. S se determine cele mai bune rezultate sportive obinute n diferite probe sportive. Rezultatele sportive pot fi estimate n diferite uniti, cum ar fi {minute, secunde, sutimi} {kilogram, gram}, {metri, centimetri}, .a. 8. O bibliotec s-a completat cu 100 de cri. Fiecare carte a fost pus la eviden cu urmtoarea structur: carte ={autor, denumire, anul de editare, locul de editare}. S se determine frecvena de utilizare a fiecrei cri. Cititorul este nregistrat la bibliotec dup urmtoarea structur: {strada, numrul casei, scara, apartamentul, numrul de telefon}. 9. n municipiul Chiinu sunt multe cinematografe 121

unde ruleaz filme. Filmele sunt nregistrate n baza de date cu urmtoarea structur: film = {denumire, ara, regizor, gen, anul filmrii}. Fiecare cinematograf este nregistrat cu urmtoarea structur = {denumire, telefon, adresa, numrul de locuri}. S se determine care gen de filme este mai solicitat de vizitatorii cinematografele din Chiinu. 10. Hotelurile din Chiinu presteaz servicii pentru mai multe persoane din mai multe ri. Pentru fiecare hotel n baza de date se va introduce urmtoarea structur {denumirea, adresa, numrul de telefon, numrul de stele}. Fiecare numr la hotel are structura: {numr , numele locatarului, data sosirii, data plecrii, costul}. Fiecare persoan este nregistrat la hotel: {ara, strada, numrul casei, scara, apartamentul, numrul de telefon}. S se determine din ce ar ne viziteaz mai mult. 11. La depozitele unor farmacii a fost adus marf de export = {denumire, tara de exportare, cantitatea, preul mrfii, data livrrii}. Medicamentele snt nregistrate n baza de date cu urm toarea structur: {denumire, tip, ambalaj, cantitate, cost}. Fiecare farmacie este pus la eviden cu urmtoarea structur farmacie={numr, telefon, adres, deschiderea, nchiderea farmaciei} . Medicul prescrie re et pacientului. Pacientul este nregistrat la policlinic dup urm toarea structur : {nume, adres, diagnostic, data mbolnvirii}. Pacientul dorete s cumpere aceste leacuri. S se afle adresele farmaciilor unde snt depozitate medicamentele solicitate. S se determine frecven a de solicitare a medicamentelor. 12. Se se afle ruta cea mai optimal (din punct de vedere 122

13.

14.

15.

16.

al costului, orei sosirii la destina timpului) dintre ie, dou orae. Comunicarea ntre aceste dou orae are loc cu autocarele ( autocar ={model, data fabricrii, punctul de destinaie, costul rutei ), cu trenul (mersul trenurilor ={num destina ora plec , r, ie, rii ora sosirii, categoria }), cu avionul ( ruta de avion = {numr, destinaia, ora decolrii, ora aterizrii} ). La Universitatea Tehnic din Moldova snt specialiti la care se nva obiecte ce in de informatic : specialitatea={facultatea, catedra, obiectul}, obiect ={denumire, an, tipul lec iei, num ore). S se r determine din punct de vedere geografic c studen i i din diferite jude e la facult au ore de informatic i (studentul = {numele, grupa, data i locul naterii, media }.) n Chiinu snt multe muzee. Informaia despre un muzeu poate fi introdus n urm toarea structur : muzeu ={ denumire, adresa, telefon, nceputul lucrului, sfritul lucrului} . Pentru diferite categorii de vizitatori (pensionari, studen oameni maturi, copii mici, i, elevi) costul biletului de intrare este diferit. S se determine frecven a de vizitate a muzeului innd cont de num rul de bilete de intrare vndute. Informaia despre bilet are urmtoarea structur bilet_de_intrare= {muzeu, tip_bilet, costul}. La aeroportul din Chiin la depozitul de lucruri u, pierdute, se afl bagaje uitate de pasageri. Fiecare bagaj este etichetat cu urm toarea informa ie: bagaj = {numr rut, stpn, data, greutate}. Pentru recuperarea bagajului uitat pasagerul trebuie s prezinte paaportul (buletinul de identitate). S se determine persoanele a c rei mai des uit ri bagajul n aeroportul din Chiin . u La o cas de vindere a imobilului se duce evidena caselor (casa.={adresa, telefon, num camere, r 123

cost, comodit i }} i a apartamentelor de vnzare (apartament.={adresa, telefon, num camere, etaj, r cost}). S se determine cele mai solicitate tipuri de vnzri efectuate de casa de vindere a imobilului.

Lucrarea de laborator nr. 5.


Tema: Fluxurile Input i Output standard i definite de utilizatori. Formatarea fluxurilor numerice i textuale. Fluxurile stringuri i de memorie. Scopul lucrrii: familiarizarea studenilor cu fluxurile input i output standard i definite de utilizatori, cu formatarea fluxurilor numerice i textuale, cu fluxurile stringuri i de memorie. Consideraiile teoretice necesare: Noiune de Fluxuri Limbajul C++ folosete conceptul abstract de stream (flux) pentru realizarea operaiilor input/output. Din punctul de vedere al limbajului C++, fluxul este o clas n sens obinuit. El ofer metode de scriere i citire a datelor independente de dispozitivul I/O. Operaiile de input/output se fac prin intermediul obiectelor de tip flux. Acestea pot fi clasificate n funcie de dispozitivul fizic asociat respectivei operaii. Din acest punct de vedere, limbajul C++ distinge trei categorii de fluxuri: input/output standard (de exemplu, tastatura i ecranul); input/output prin intermediul fiierelor; input/output n memorie. Fluxurile incapsuleaz (ascund) problemele specifice dispozitivului cu care se lucreaz, sub biblioteca standard iostream.h.. Biblioteca iostream.h. este scris nsi n limbajul C++ i este o bibliotec a claselor. 124

Alt avantaj al utilizrii fluxurilor se datoreaz implementrii bibliotecii iostream.h, care utilizeaz un sistem de zone tampon. Operaiile de intrare/ieire cu dispozitivele periferice snt consumatoare de timp. Informaiile trimise ctre un flux nu snt scrise imediat n dispozitivul n cauz, ci snt transferate ntr-o zon de memorie tampon, din care snt descrcate ctre dispozitiv n momentul umplerii acestei zone de memorie. In limbajul C++ fluxurile au fost implementate utiliznd clase, dup cum urmeaz: clasa streambuf gestioneaz zonele tampon, este clasa de baz pentru clasele de clasa ios fluxuri de intrare i de ieire. Clasa ios are ca variabil membru un obiect de tip streambuf, clasele istream snt derivate din ios, i ostream clasa iostream este derivat din istream i ostream i ofer metode pentru lucrul cu terminalul, ofer metode pentru operaii cu clasa fstream fiiere. .Obiecte standard. La lansarea n execuie a unui program C++, care include iostream.h, in mod automat compilatorul limbajului C++ creeaz i iniializeaz patru obiecte: gestioneaz intrarea de la intrarea standard cin (tastatura), cout gestioneaz ieirea ctre ieirea standard (ecranul), cerr gestioneaz ieirea ctre dispozitivul standard de eroare (ecranul), neutiliznd zone tampon, clog gestioneaz ieirea ctre dispozitivul standard de eroare (ecranul), utiliznd zone tampon 125

Expresia "cout " se utilizeaz pentru afiarea diverselor valori. De fapt, cout este un obiect de tip flux, i anume obiectul flux standard output. Operaia output (afiare) se realizeaz prin intermediul operatorului suprancrcat . Obiectul de tip flux standard input este cin, iar operaia input (citirea) se realizeaz prin intermediul operatorului suprancrcat . Obiectele cout i cin snt declarate n biblioteca iostream.h. Redirectri. Dispozitivele standard de intrare, ieire i eroare pot fi redirectate ctre alte dispozitive. Erorile snt, de obicei, redirectate ctre fiiere, iar intrarea i ieirea pot fi conduse ("piped") ctre fiiere utiliznd comenzi ale sistemului de operare (utilizarea ieirii unui program ca intrare pentru altul). Sintaxa pentru operaii de ieire, cout: cout << InformatieDeTrimisLaIesire; Respectiv pentru intrare, cin: cin >> NumeVariabil; De fapt, cin i cout snt nite obiecte definite global, care au suprancrcat operatorul >> respectiv << de mai multe ori, pentru fiecare tip de parametru n parte (int, char *, etc.): istream &operator >> (TipParametru &) De exemplu: #include <iostream.h> void main() {int IntegerNumber=50; cout << "IntegerNumber = "; cin >> IntegerNumber; cout<<"\nWhat you entered = "<<IntegerNumber<<endl; } Rezultatul ndeplinirii programului: IntegerNumber = 200 What you entered = 200 Acest scurt program citete de la intrarea standard o valoare ntreag, pe care o trimite apoi ctre ieirea 126

standard. Se observ posibilitatea de a utiliza simbolurile '\n', '\t', s.a.m.d (ca la printf, scanf, etc.). Utilizarea simbolului endl va fora golirea zonei tampon, adic trimiterea datelor imediat ctre ieire. Att operatorul >>, ct i << returneaz o referent ctre un obiect al clasei istream. Deoarece cin, respectiv cout, este i el un obiect istream, valoarea returnat de o operaie de citire/scriere din/n stream poate fi utilizat ca intrare/ieire pentru urmtoarea operaie de acelai fel. Operaia de intrare cin. Funcia cin.get() poate fi utilizat pentru a obine un singur caracter din intrare, apelnd-o fr nici un parametru, caz n care returneaz valoarea utilizat, sau ca referin la un caracter. get(); //fr parametri n aceast form, funcia ntoarce valoarea caracterului gsit. Spre deosebire de operatorul >>, funcia nu poate fi utilizat pentru a citi mai multe intrri, deoarece valoarea returnat este de tip ntreg, nu un obiect istream. Un exemplu de utilizare: #include <iostream.h> void main() {char c; while((c = cin.get()) != *) {cout << "c = " << c << endl;} } Rezultatul ndeplinirii programului: asdfgh* c=a c=s c=d c=f c=g c=h 127

Citirea de iruri de caractere utiliznd get(). Operatorul >> nu poate fi utilizat pentru a citi corect iruri de caractere de la intrare, deoarece spaiile snt interpretate ca separator ntre diverse valori de intrare. n astfel de cazuri trebuie folosit funcia get(). Sintaxa de utilizare a funciei get n acest caz este urmtoarea: cin.get(char *PointerLaSirulDeCaractere, int Lungime Maxim, char Sfrit); Primul parametru este un pointer la zona de memorie n care va fi depus irul de caractere. Al doilea parametru reprezint numrul maxim de caractere ce poate fi citit plus unu. Cel de-al treilea parametru este caracterul de ncheiere a citirii, care este opional (implicit considerat '\n'). n cazul n care caracterul de ncheiere este ntlnit nainte de a fi citit numrul maxim de caractere, acest caracter nu va fi extras din flux. Exist o funcie similar funciei get(), cu aceeai sintax, numit getline(). Funcionarea sa este identic cu get(), cu excepia faptului c acel ultim caracter menionat mai sus este i el extras din flux. Funcia cin.ignore() se utilizeaz pentru a trece peste un numr de caractere pn la ntlnirea unui anume caracter. Sintaxa sa este: cin.ignore(int NumrMaximDeCaractere, char Sfrit); Primul parametru reprezint numrul maxim de caractere ce vor fi ignorate, iar al doilea parametru caracterul care trebuie gsit. Funcia cin.peek() returneaz urmtorul caracter din flux, fr ns a-l extrage. Funcia cin.putback() insereaz n flux un caracter. cout . Funcii membri ale cout Funcia cout.flush()) determin trimiterea 128

ctre ieire a tuturor informaiilor aflate n zona de memorie tampon. Aceast funcie poate fi apelat i n forma cout << flush. Funcia cout.put()) scrie un caracter ctre ieire. Sintaxa sa este urmtoarea: cout.put(char Caracter); Deoarece aceast funcie returneaz o referin de tip ostream, pot fi utilizate apeluri succesive ale acesteia, ca n exemplul de mai jos: #include <iostream.h> void main() {cout.put('H').put('i').put('!').put('\n');} Funcia cout.write() are acelai rol ca i operatorul <<, cu excepia faptului c se poate specifica numrul maxim de caractere ce se doresc scrise. Sintaxa funciei cout.write() este: cout.write(char *SirDeCaractere, int CaractereDeScris); Formatarea ieirii Funcia cout.width() permite modificarea dimensiunii valorii trimise spre ieire, care implicit este considerat exact mrimea cmpului n cauz. Ea modific dimensiunea numai pentru urmtoarea operaie de ieire. Sintaxa este: cout.width(int Dimensiune); Funcie cout.fill() permite modificarea caracterului utilizat pentru umplerea eventualului spaiu liber creat prin utilizarea unei dimensiuni mai mari dect cea necesar ieirii, cu funcia cout.width(). Sintaxa acesteia este: cout.fill(char Caracter); Opiuni de formatare a ieirii. Pentru formatarea ieirii snt definite dou funcii membri ale cout, i anume: Funcia cout.setf() activeaz o opiune de formatare a ieirii, primit ca parametru: 129

cout.setf(ios::Opiune); unde Opiune poate fi: Showpos determin adugarea semnului plus (+) n faa valorilor numerice pozitive; left, right, schimb alinierea ieirii (la stnga. La internal dreapta, centreaz); dec, oct, hex schimb baza de numeraie pentru valori numerice; showbase determin adugarea identificatorului bazei de numeraie n faa valorilor numerice. Funcia cout.setw() modific dimensiunea ieirii, fiind similar funciei cout.width(). Sintaxa sa este: cout.setw(int Dimensiune); n continuare vom exemplifica utilizarea funciilor pentru formatarea ieirii: #include <iostream.h> #include <iomanip.h> void main() {int number = 783; cout << "Numr = " << number<<endl; cout.setf(ios::showbase); cout<<"Numr n sistem hexazecimal = "<<hex << number<<endl; cout.setf(ios::left); cout << "Numr n sistemul octal, aliniat la stnga = " << oct << number<<endl; } Rezultatul ndeplinirii programului: Numr = 783 Numr n sistem hexazecimal = 0x30f Numr n sistemul octal, aliniat la singa = 01417 Redefinirea operatorilor de intrare i ieire pentru fluxul standard. Operatorii de intrare i ieire pentru fluxul standard pot fi redefinii 130

pentru citirea i nregistrarea obiectului de tipul clasei definite de utilizator. Operatorul de intrare a fluxului standard pentru un obiect din clasa obj se redefinete n modul urmtor: istream & operator >> (istream &s, class obj) { //citirea componentelor din clasa obj return s; } Operatorul de ieire a fluxului standard pentru un obiect din clasa obj se redefinete n modul urmtor: ostream & operator<< (ostream &s, class obj) { // tiprirea componentelor din clasa obj return s; } De exemplu, vom redefini operatorii menionai pentru clasa persoana. # include <stdio.h> # include <iostream.h> # include <string.h> class persoana { private: char nume[40]; char prenume[40]; long int telefon; int virsta; public: persoana(char *Nume=NULL, char *n=NULL, int v=0, long int t=0) {strcpy(nume,Nume); strcpy(prenume,n); virsta=v; telefon= t; } friend istream & operator >> (istream &s, persoana &P); friend ostream & operator<< (ostream &s, persoana &P); void set(char* Nume, char *n, int v, long int t) {strcpy(nume,Nume); strcpy(prenume,n); virsta=v; telefon= t; }; }; 131

istream & operator >> (istream &s, persoana &P) { if((s >>P.nume) && (s >>P.prenume) && (s >>P.virsta) && (s >>P.telefon)) P.set(P.nume,P.prenume,P.virsta, P.telefon); return s; } ostream & operator << (ostream &s, persoana &P) {return(s<<P.nume<<' '<<P.prenume<<' '<<P.virsta<< ' '<<P.telefon);} void main() { persoana p; cout<<"Introdu datele despre persoana:"<<endl; cin >>p; cout<< p; } Rezultatele ndeplinirii programului: Introdu datele despre persoana: Bradu Maria 20 123456 Bradu Maria 20 123456 Operaii de intrare/ieire cu fiiere. n afar de fluxurile standard input/output se pot defini fluxuri ale utilizatorului definite prin: ifstream nume pentru un flux input (citire), ofstream nume pentru un flux output (scriere), nume flux utilizat i pentru citire i fstream pentru scriere n oricare din cazurile de mai sus. Numele va fi un obiect de tipul clasei corespunztoare. Lucrul cu fiierele se face prin intermediul clasei ifstream pentru citire, respectiv ofstream pentru scriere. Pentru a utiliza fiiere, aplicaiile trebuie s includ fstream.h. Clasele ofstream i ifstream snt derivate din clasa iostream, ca urmare toi operatorii i 132

toate funciile descrise mai sus snt motenite i de aceast clas. Definirea fluxurilor se poate face numai prin includerea n program a fiierului fstream.h. ifstream, ofstream i fstream snt clase. Declararea unui flux al utilizatorului este o declarare a unui obiect din clasa respectiv. Clasele pot conine att date, ct i metode (funcii). Operaiile input/output se fac prin intermediul unor funcii membri ale claselor respective. Sintaxele pentru constructorii acestor dou clase snt: ofstream Variabila(char *NumeFiier, ios::Mod); ifstream Variabila(char *NumeFiier); Funcia de deschidere a unui stream este funcia open. Fiind o funcie membru, ea va fi apelat numai prin intermediul unui obiect declarat de tipul uneia din clasele de mai sus. De exemplu: #include<fstream.h> void main() { ofstream scriere; scriere.open(disk.dat"); scriere.close(); } Programul de mai sus exemplific faptul, c nu exist nici o diferen ntre lucrul obinuit cu clase i definirea unui flux output. Astfel, variabila scriere a fost declarat ca fiind de tipul (clasa) ofstream. Definiia clasei ofstream poate fi gsit n fiierul fstream.h. Funcia membru open, apelat prin intermediul obiectului scriere, creeaz un fiier cu numele disk.dat. n continuare acesta este nchis prin apelul funciei membru close(), prin intermediul obiectului scriere. Execuia programului va determina crearea unui fiier, care, ns nu va conine nimic. Scrierea propriu-zis se face cu operatorul (suprancrcat) , iar citirea cu operatorul , la 133

fel ca n cazul fluxurilor standard input/output. Ideea de a utiliza noiunea abstract de flux s-a impus din dorina de a asigura independena operaiilor input/output fa de calculator sau de sistemul de operare. Productorii de compilatoare livreaz (mpreun cu compilatorul) biblioteci, care conin clase, funcii, variabile, care permit ca, n mare msur, aceast independen s fie asigurat. Funcia open are primul parametru de tip ir de caractere modificat prin modificatorul const. Acesta reprezint numele fiierului, care va fi asociat fluxului. Al doilea parametru specific modul de acces la fiierul asociat fluxului i poate avea urmtoarele valori: accesul se face prin adugare la ios:: app; sfritul fiierului, poziionarea se face la sfritul ios:: ate; fiierului, fiierul este interpretat ca fiier binar, ios:: binary; ios::in; se asociaz unui flux de intrare, ios::out; se asociaz unui flux de ieire, ios::nocreate; fiierul trebuie s existe deja, ios::noreplace; fiierul trebuie s nu existe, ios::trunc; distruge un fiier preexistent, avnd acelai nume. Aceti constructori au rolul de a deschide fiierul specificat ca parametru. Valorile de mai sus snt constante definite n clasa ios, definite n fiierul ifstream.h. Al treilea parametru este ignorat n cazul n care parametrul al doilea este ios :: nocreate. Caracteristicile de tip text i binary (binar) trebuie privite n legtur cu operatorii folosii n operaiile input/output. Operatorii, pe care i-am folosit pn acum ( i ), utilizeaz codificarea ASCII a datelor. De exemplu: 134

#include<fstream.h> void main() {ofstream s1; s1.open("dl.dat",ios::binary); s1<<"text \n"<<12<<"\n"<<4.2; s1.close(); ofstream s2("d2.dat"); s2<<"text \n"<<12<<"\n"<<4.2; s2.close (); } Rezultatul ndeplinirii programului: Coninutul fiierului d1.dat: text 12 4.2 Coninutul fiierului d2.dat: text 12 4.2 Funciile specializate pentru operaii input/output, care realizeaz copii ale memoriei, snt read i write. Funciile read i write. Funcia write scrie ntr-un fiier coninutul unei zone de memorie, care trebuie s fie adresat printrun pointer de tip caracter. 0 zon de memorie poate fi privit ca un ir de octei, indicat prin adres de nceput i lungime. De exemplu: #include<fstream.h> void main () {ofstream s1; int i; s1. open ("d1. dat", ios:: binary) ; s1.write((char *) &i, sizeof(i)) ; s1.write("\n",4) ; s1.close(); 135

ofstream s2; s2.open("d2.dat"); s2.write((char *) &i, sizeof(i)); s2. write (" \n", 4); s2. close (); } Funcia de conversie (char *) se utilizeaz pentru a converti adresele oricror obiecte la tipul adres de caracter. Astfel, se poate de interpretat orice adres din memorie ca fiind adresa unui ir de caractere. Operatorul sizeof se utilizeaz pentru determinarea lungimii acestei zone de memorie. Citirea unui fiier, utiliznd funcia read, se face respectnd aceleai principii ca n cazul funciei write. Diferena ntre fiierele de tip text i binary se pstreaz i n cazul utilizrii funciilor read i write i const n reprezentarea diferit a caracterelor de control. De exemplu: #include<fstream.h> void main() {char *p=''\n"; ofstream s1; s1.open("d1.dat",ios::binary); s1.write(p,sizeof(p)); s1.close(); ofstream s2; s2.open("d2.dat") ; s2,write(p,sizeof(p)); s2.close() ; } Reprezentarea datelor ntr-un fiier depinde exclusiv de funciile care se utilizeaz pentru nregistrarea (<< sau write) respectiv, citirea acestora (>> sau read). De cte ori citim un fiier, despre care tim cum a 136

fost creat, este bine s-l citim cu aceleai caracteristici i cu aceeai operatori. Cnd vrem s citim din fiier, despre care nu tim cum a fost creat, este bine s-l citim n mod binar, caracter cu caracter. De exemplu: #include<fstream.h> void main() {int i,j; float k; char p[10]; ofstream s1; s1.open("d1.dat") ; s1 122 "\n" 147; s1 "\n abcd\n" 9.3; s1.close(); ofstream s2; s2.open("d.dat",ios::binary); s2 i 3 p k; s2.close(); } Rezultatul ndeplinirii programului: n fiierul textual d1.dat s-a nscris urmtoarele 122 147 abcd 9.3 n fiierul binar s-a nscris urmtoarele: 102273BO39 _3.138909e-42 n alt exemplu: #include<fstream>.h> void main() { char p[20]; ifstream s; s.open("d.dat",ios::binary); s.read(p,19); 137

s.close(); p[19]=NULL; cout p; } fiierul creat poate fi citit n mod binar, utiliznd funcia read. n acest mod coninutul fiierului este considerat copia unei zone de memorie. Deoarece fiierul a fost creat printr-o codificare ASCII, coninutul acestuia se ia ca un ir de caractere. Instruciunea read copie coninutul fiierului n irul de caractere p. Ultima instruciune din programul de mai sus afieaz la ieirea standard coninutul vectorului p. Atribuirea explicit p[19]=NULL este obligatorie pentru a marca sfritul irului de caractere. Pentru a nchide aceste fiiere, trebuie apelat funcia membru close(). Fluxuri n memorie constituie o interfa ntre program i un dispozitiv fizic. Am utilizat fluxurile standard i operatorii , i, de asemenea, fluxurile asociate cu fiiere. Exist posibilitatea de definire a unor fluxuri n memorie. Acestea snt fiiere, care fizic snt localizate n memorie. Un flux n memorie este un ir de caractere, care are exact aceeai structur ca un fiier obinuit. Clasele, care definesc aceste fluxuri, snt: istrstream(input string stream); flux input, ostrstream(output string stream); flux output, strstream (string stream); flux input/output. Funcii de formatare. Clasele bazate pe fluxuri conin o serie de funcii, care ofer o mare flexibilitate a operaiilor de scriere cu formatare. Pentru datele numerice cele mai importante snt funciile, care permit tiprirea ntr-un cmp de lungime fix, alinierea (la stnga sau la dreapta) i precizia, cu care se face afiarea. Funcia width stabilete dimensiunea cmpului, 138

calculat n caractere, pe care se va face scrierea. Funcia are un singur parametru de tip ntreg i trebuie apelat prin intermediul unui obiect de tip flux. Astfel, obiectul de tip flux n memorie, declarat prin simbolul a, va avea funcia asociat a. width, prin care am cerut ca fiecare dat, s fie nregistrat ntrun cmp format din 8 caractere. Funcia setf are, de asemenea, un singur parametru n cazul dat ios::right, avnd semnificaia de aliniere la dreapta. Funcia precision are un singur parametru de tip ntreg, prin care se specific numrul de poziii, scrise dup punctul zecimal. De exemplu: #include<fstream.h> #include<strstream.h> void main () { float a[4][5]; int i, j; for ( i=0; i<4; i++) for ( j=0; j<5; j++) a[i][j]=(float) (i+2)/(j+1); char s[200]; ostrstream scrie(s,sizeof(s)); for ( i =0; i<4; i++) {for ( j=0; j<5; j++) {scrie.width(8); scrie.setf(ios::right); scrie.precision(2); scrie << a[i][j]; } scrie << "\n";}; s[164]=NULL; cout << s; } Rezultatul ndeplinirii programului: 139

2 1 0.67 0.5 0.4 3 1.5 1 0.75 0.6 4 2 1.33 1 0.8 5 2.5 1.67 1.25 1 n programul de mai sus parametrul funciei precision este 2, deci numerele vor fi scrise cu dou semne zecimale. Programul reprezint un bun exemplu, prin care putem scoate n eviden utilitatea fluxurilor de memorie. Rezultatul operaiilor de intrare/ieire poate fi testat prin intermediul a patru funcii membri: eof() verific dac s-a ajuns la sfritul fiierului; bad() verific dac s-a executat o operaie invalid; fail() verific dac ultima operaie a euat; good() verific dac toate cele trei rezultate precedente snt false. Funciile de formatare pot fi aplicate oricrui obiect de tip flux, fie c este un flux standard, un flux de tip fiier sau un flux n memorie. ntrebrile pentru verificarea cunotinelor: 1. Care snt tipurile de fluxuri standard definite de utilizator? 2. Scriei un exemplu de utilizare a funciilor read i write a coninutului unei zone de memorie. 3. Definii parametrii funciei membru open ale unei clase flux. 4. Cum se definesc fluxurile de memorie? 5. Care snt funciile de formatare pentru fluxuri? 140

Temele pentru acas: 1. Corectai greelile i lansai urmtoarele exemple la execuie. Ce rezultate vor aprea pe ecran? Exemplul 1: #include <iostream.h> define N 80 void main(0 { inc c; int cnt=0; charcnt=0; While(1) { c=cin.get(); if (c==EOF) break; if (c=/ && cin.peek() ==/) { cnt++; cin.ignore(n,\n); charcnt++=cin.gcount(); charcnt++; } } cout<<In << cnt << comentarii avem << charcnt << caractere; } Exemplul 2 . #include <iostream.h> void main() {While(1) { char c; cin.get; if(cin.eof()) break; cout.put( c); } Exemplul 3. #include <iostream.h> void main() {cout <<setfill(%}<<setw(4)<< 17;} 141

Exemplul 4. #include <iostream.h> #include <fstream.h> void main() { int I=42; fstream f (dat.txt,ios::out|ios::binary); f.seekp(17,ios::beg); f.write((const char*)&I,2) ; f.close(); f.open(dat.txt, ios::in|ios::binary); f.seekg(17,ios::beg); f,read((char*)&I,2); f.close(); cout<<I; } Exemplul 5. #include <iostream.h> #include <strstream.h> void main() {char buffer [80]; strstream s (buffer,80,ios::out); float pret=123.45; s<<Preul stocului este de; s;;serw(6)<<setprecision(2) ; } Temele pentru lucrri de laborator: 1. Scriei un program care compar dou fiiere date. 2. Scriei un program care calculeaz numrul de elemente ale unui fiier care snt mai mici ca valoarea medie aritmetic a tuturor elementelor acestui fiier. 3. Scriei un program care tiprete toate cuvintele diferite de ultimul cuvnt dintr-un fiier. Cuvintele din fiier snt separate prin virgul, iar.dup 142

ultimul cuvnt se pune punct. Scriei un program care determin numrul maximal i cel minimal dintr-un ir de 100 de numere aleatoare dintr-un fiier. Subconsecutivitatea de elemente dintre numrul maximal i cel minimal determinat s se nregistreze ntr-un nou fiier. 5. Scriei un program care formeaz un fiier nou dup urmtoarea legitate: din trei fiiere date mai nti se selecteaz numerele divizibile la 3, la 5 i la 7, apoi numerele pozitive pare de pe locuri impare. 6. Scriei un program care formeaz un fiier nou dup urmtoarea legitate: din trei fiiere date mai nti se selecteaz numerele negative, zerourile, apoi numerele pozitive. 7. Scriei un program care ordoneaz lexicografic o consecutivitate de nregistrri (dintr-un fiier) de tipul struct { char nume [30]; int ani} nregistrare; Rezultatul ordonrii s se nscrie ntr-un nou fiier. 8. Scriei un program care din dou fiiere ordonate descresctor se va forma unul n care se va pstra ordinea descresctoare de sortare. 9. Scriei un program care sorteaz lexicografic cuvintele dintr-un text. Pentru fiecare element sortat s se indice numrul de repetri ale cuvntului n textul dat. 10. Scriei un program care calculeaz suma nmulirii numerelor vecine pe pe locuri pare dintr-un fiier dat. 11. Scriei un program care va tipri n ordine invers subconsecutivitatea de numere dintre valoarea minim i maxim ale unei consecutiviti de numere citite dintr-un fiier. 4. 143

12. Scriei un program care formeaz un fiier nou din cel dat dup urmtoarea legitate: elementele fiierului nou se obine din inversul numerelor din fiierul dat. 13. Scriei un program care determin frecvena cu care a fost generat fiecare element n fiierului creat. Valorile elementelor snt cuprinse ntre 1 i 100. 14. Scriei un program care determin numrul maximal i cel minimal din numerele unui fiier dat. S se determine elementele mai mari ca cel minimal i mai mici ca numrul maximal. 15. Scriei un program care efectueaz reformatarea unui fiier textual n felul urmtor. Lungimea rndului de caractere n fiierul nou are lungimea de 60 de caractere. Dac n rndul dat se depisteaz punctul, restul rndului din fiierul dat se scrie din rnd nou. 16. Scriei un program care din dou fiiere date ordonate cresctor se va forma unul nou, n care se va pstra ordinea cresctoare de sortare.

Tema:Templates: Template pentru clase i funcii. Scopul lucrrii: familiarizarea studenilor cu clase i funcii generice. Consideraiile teoretice necesare: Clase i funcii generice Template-ul implementeaz aa-zisul concept de "tip parametrizat" ("parametrized type"). Un template reprezint o familie de tipuri sau funcii, cu alte cuvinte, un ablon sau model. Acest concept a fost introdus, n primul rnd, pentru a crete gradul de 144

Lucrarea de laborator nr. 6.

reutilizare a codului. De exemplu, pentru a implementa o list de numere ntregi este necesar realizarea unei clase speciale (s spunem ListOfIntegers), iar pentru o list de iruri alt clas (s spunem ListOfStrings). Conceptul de template permite realizarea unei clase generale (s spunem List), care s accepte orice tip de element, inclusiv tipuri necunoscute la momentul implementrii acesteia. Tipul template este stabilit n momentul instanierii sale. Template-urile snt foarte utile pentru realizarea de biblioteci care trebuie s ofere metode generice de prelucrare a datelor. Sintaxa general de declarare a unui template este urmtoarea: template < ListaDeParametri > Declaraie unde Declaraie reprezint declararea sau definirea unei clase sau unei funcii, definirea unui membru static al unei clase template, definirea unei clase sau funcii membri al unei clase template, sau definirea unui membru template al unei clase. Clasele parametrizate (sau clasele template) se declar astfel: template <class NumeParametru> class NumeClasa { // ... // definirea clasei } Stabilirea tipului clasei template se face prin intermediul unei construcii de genul: NumeClasa <NumeParametru> unde NumeParametru reprezint tipul obiectului. Funciile template se declar astfel: template <class NumeParametru> // ... // declaraia funciei 145

S considerm n continuare ca exemplu implementarea unei stive generice folosind templateuri. #include <iostream.h> template <class T> class StackItem { public: StackItem *Next; Tip *Data; StackItem(Tip Data_new, StackItem <Tip> *Next_new) { Data = new Tip(Data_new); Next = Next_new; } }; template <class Tip> class Stack {public: Tip pop() { Tip result = *(Data->Data); StackItem <Tip> *temp = Data; Data = Data->Next; delete temp; return result; } Tip top() { return *(Data->Data); } void push(Tip Data_new) { Data = new StackItem <Tip>(Data_new, Data); } int isEmpty() { return Data == 0; } Stack() { Data = 0; } private: StackItem <Tip> *Data; }; void main() { Stack <int> anIntegerStack; anIntegerStack.push(5); 146

anIntegerStack.push(7); if(anIntegerStack.isEmpty()) cout << "Stiva goala" << endl; else cout << anIntegerStack.pop() << endl; cout << anIntegerStack.top() << endl; } Rezultatul ndeplinirii programului: 7 5 n exemplul urmtor a fost implementat o list generic (List). Ca elemente a listei s-au folosit obiecte de tip Point. Pentru parcurgerea uoar a listei a fost implementat o clas de tip "iterator", care poate fi considerat ca fiind un "cursor" care strbate lista. Funcia List.begin() returneaz un iterator poziionat pe primul element al listei, List.end() pe ultimul element al listei. Saltul la urmtorul element al listei se face cu ajutorul operatorului ++ din clasa Iterator. #include <iostream.h> class Point {friend ostream& operator << (ostream& output, Point p); protected: unsigned x, y; public: Point() {x = 0;y = 0;} Point(unsigned X, unsigned Y) {x = X;y = Y;} ~Point() {} unsigned GetX() {return x;} unsigned GetY() {return y;} void SetX(unsigned X) {x = X;} void SetY(unsigned Y) {y = Y;}}; ostream& operator << (ostream& output, Point p) {output<<"(" << p.x <<", " << p.y << ")"; return output;} 147

template <class Tip> class Item {public: Item *Next; Tip *Data; Item(Tip __Data, Item <Tip> *__Next) {Data = new Tip(__Data); Next = __Next;} }; template <class Tip> class List {public: Tip pop_front() {Tip result = *(Data->Data); Item <Tip> *temp = Data; Data = Data->Next; delete temp; return result;} Tip front() {return *(Data->Data);} void push_front(Tip __Data) {Data = new Item <Tip>(__Data, Data);} int empty() {return Data == 0;} List() {Data = 0;} class Iterator { friend class List <Tip>; protected: Item <Tip> *Current; Iterator(Item <Tip> *x) {Current = x;} public: Iterator() {} int operator == (Iterator& x){return Current == x.Current;} int operator != (Iterator& x){return Current != x.Current;} Tip operator *(){return *(Current->Data);} Iterator& operator ++(int) {Current = Current->Next; return *this;} }; Iterator begin(){return Iterator(Data);} 148

Iterator end() {Item <Tip> *temp; for(temp = Data; temp; temp = temp->Next); return Iterator(temp);} private: Item <Tip> *Data; }; void main() {List <Point> anPointList; List <Point>::Iterator index, end; anPointList.push_front(Point(1, 1)); anPointList.push_front(Point(3, 14)); index = anPointList.begin(); end = anPointList.end(); if(anPointList.empty()) cout << "Lista vida" << endl; else for(; index != end; index++) cout << *index << " "; cout << endl; } Rezultatul ndeplinirii programului: (3, 14 ) (1, 1) Clasele template pot avea trei tipuri de prieteni (friends): o clas sau funcie care nu este de tip template; o clas sau funcie template; o clas sau funcie template avnd tipul specificat. Dac este necesar particularizarea unei funcii template sau a unei funcii membri a unei clase template pentru un anumit tip, funcia respectiv poate fi suprancrcat pentru tipul dorit. Trebuie de remarcat, de asemenea, c n cazul n care o clas template conine membri statici, fiecare instan a template-ului n cauz va conine propriile date statice. 149

ntrebrile pentru verificarea cunotinelor: 1. Ce reprezint un template? Dai exemple de funcie template, clas template. 2. Care este corelaia dintre clase derivate i clase template? 3. Pot oare fi redefinite funciile template? 4. Clasele template pot avea trei tipuri de prieteni (friends). Care snt ele? Dai exemple. Temele pentru acas: 1. Ce efectueaz urmtoarea funcie template? template <class X> void swap(X &a, X &b) {X temp; temp=a; a=b; b=temp;} void main() {int 1=10, j=20; float x=10.1, y=23.3; cout<<Valorile i, j snt:<< i << ' '<< j << endl; cout<<Valorile , snt<< x<< ' '<< <<endl; swap(i,j); swap (x,y); // schimb de variabile 150

cout<<Valorile i, j dup schimb:<<i<<' '<<j<<endl; cout<<Valorile , dup schimb:<<x<<' '<< <<endl; } 2. Ce rezultate obinem la rularea urmtorului program? #include<iostream.h> template <class data_t> class list { data_t data; list *next; public: list (data_t d); void add(list *node) {node->next=this; next-0;} list *getnext() {return next;} data_t getdataO {return data;} } template <class data_t> list<data_t>::list(data_t d) {data=n; next=0; } void main() {list<char> start('a'); list<char>*p, *last; // crearea listei last=&start; for(int i=l; i<26; i++) {p=new list<char>(a+1); last=p;} //tiparul listei p=&start; while(p) {cout<<p->getdata(); p=p->getnext();} return 0; } Temele pentru lucrri de laborator: 1. Sortare prin selecie. Se d consecutivitatea de obiecte a1, , an. Fie elementele consecutivitii snt 151

aranjate n ordine cresctoare: a1 a2 an. Aceast problem poart denumirea de problem de sortare a consecutivitii care poate fi aranjat i n ordine descresctoare: a1 a2 an.; dac numerele dou cte dou snt diferite, consecutivitatea poate fi aranjat n cretere sau descretere. S se afle elementul tabelului care are valoare minimal. El se va nlocui cu primul element din consecutivitate, apoi se procedeaz cu restul elementelor din consecutivitate ncepnd cu al doilea element .a.m.d. 2. Sortarea prin interclasare. Se d consecutivitatea de obiecte a1, , an. Fie elementele consecutivitii snt aranjate n ordine cresctoare: a1 a2 an. Aceast problem poart denumirea de problem de sortare a consecutivitii care poate fi aranjat i n ordine descresctoare: a1 a2 an.; dac numerele dou cte dou snt diferite, consecutivitatea poate fi aranjat n cretere sau descretere. Prin analiza consecutiv a elementelor a1, , an s se afle cel mai mic i, astfel ca ai > ai+1. S se schimbe ai i ai+1 cu locul. S se efectueze aceast procedur, s se renceap analiza consecutivitii cu elementul ai+1 .a.m.d. Prin aceste manipulrii elementul cel mai mare se va mica pe ultimul loc al consecutivitii. Urmtoarele analize se vor ncepe de la nceput micornd cantitatea de elemente cercetate cu o unitate. Consecutivitatea va fi sortat dup analiza elementelor , n care au participat numai elementul unu i doi. 3. Sortarea prin inserie. Se d consecutivitatea de obiecte a1, , an. Fie elementele consecutivitii snt aranjate n ordine cresctoare: a1 a2 an. 152

Aceast problem poart denumirea de problem de sortare a consecutivitii care poate fi aranjat i n ordine descresctoare: a1 a2 an.; dac numerele dou cte dou snt diferite, consecutivitatea poate fi aranjat n cretere sau descretere. S se cerceteze consecutiv a2, , an i fiecare element nou ai inclus la locul potrivit n consecutivitatea deja sortat a1, , ai-1. Acest loc se determin prin compararea consecutiv a elementului ai cu elementele sortate a1, , ai-1 . 4. S se cerceteze i s se calculeze numrul de comparri i de schimbri (adic permutri dintr-un loc n altul) ale elementelor a1, , an n procesul de utilizare a algoritmilor descrii n varianta 1. S se demonstreze c numrul de schimbri pentru algoritmul de sortare prin selectare este mrginit de o funcie liniar de n. n acelai timp i numrul de comparri pentru algoritmul de sortare prin selectare i pentru algoritmii de sortare prin interclasare i prin inserie, n unele cazuri, (de exemplu, dac consecutivitatea iniial este de tipul a1>a2>>an) snt funcii ptratice de n. 5. Din definiia problemei din punctul 1 rezult c algoritmul de sortare prin selectare se deosebete de cellali algoritmi n acele cazuri cnd schimbarea locului elementului este cu mult mai dificil dect compararea elementelor. S se utilizeze acest fapt la executarea urmtoarelor probleme. Se d un obiect matrice de numere reale de dimensiunea n m; s se sorteze (s se schimbe cu locul) liniile matrice: ) dup creterea valorilor primelor elemente ale liniilor matricei; b) dup descreterea sumelor elementelor liniilor 153

matricei; dup descreterea valorilor minimale ale elementelor liniilor matricei; d) dup descreterea valorilor maximale ale elementelor liniilor matricei. Pentru punctele b), c), d) s se utilizeze o consecutivitate auxiliar de numere a1, , an. 6. Fie o consecutivitate de numere ntregi sau reale a1 a2 an se d n ordine descresctoare i fie se d un numr b (respectiv ntreg sau real), pentru care trebuie s-i cutm locul ntre numerele a1, , an, astfel ca, dup introducerea numrului b n acest loc, consecutivitatea s rmn sortat n aceeai ordine. Dac unele elemente snt egale dup valoare cu numrul b, acesta poate fi introdus n diferite locuri mai aproape de nceputul consecutivitii. Aceast problem poart numele de problem de cutare a locului elementului.. Pentru b snt n+1 posibiliti: b a1, a1<b a2, an-1 <b an, an < b i ca soluie a acestei probleme de cutare a locului elementului b poate fi corespunztor unul din numerele 1, ..., n+1. Pentru rezolvarea acestei probleme va fi util urmtorul algoritm care se numete algoritmul mpririi n jumti (algoritmul cutrii binare). Se ia mai nti 1 i n + 1 n calitate de hotare de cutare a locului elementului, apoi pn cnd valorile hotarelor nu coincid, pas cu pas se mic aceste hotare n modul urmtor: se compar b cu as, unde s este partea ntreag mediei aritmetice a valorilor hotarelor; dac s<b, atunci se nlocuiete hotarul de jos precedent cu s+1, iar cel de sus rmne fr schimbare, n caz contrar, se las fr nici o c) 154

schimbare hotarul de jos, iar hotarul de sus se schimb cu s. Cnd valorile hotarelor coincid, fiind egale cu careva numr t, executarea algoritmului descris se finalizeaz cu rezultatul t. (Numrul de comparri necesare pentru acest algoritm nu exced [log2 (n +1)]+1) a) Sunt date numere reale a1, , an, b1, , bm (a1 a2 an). S se obin numerele naturale k1, , km astfel, nct ki s fie soluia problemei cutrii locului bi printre a1, , an (i= 1, ..., m). S se utilizeze algoritmul cutrii binare. b) Tabelul de ctiguri a loteriei este reprezentat prin tabelul de numere ctigtoare a1, , an i tabelul de ctiguri n lei p1, , pn (pi este ctigul pentru numrul ai (i = 1, ..., n)). S se determine suma ctigurilor pentru biletele cu numerele b1, , bm. S se utilizeze algoritmul cutrii binare. c). Fie locul numrului b printre cele ale consecutivitii sortate cresctor a1, , an se va alege ca cel mai ndeprtat loc de la nceputul consecutivitii neafectnd legitatea sortrii cresctoare. S se introduc schimbri n descrierea algoritmului de mprire n jumti i respectiv s se rezolve problema a). 7. Cutare binar. Fie T[1..n} un vector sortat cresctor i x un obiect care se afl printre obiectele lui T. S se determine locul lui x n T. Consecutivitatea se organizeaz n form de list dublu lnuit. S se foloseasc algoritmul de sortare a vectorului T[1..n], divizndu-l n doi vectori F i V, sortndu-i pe acetia i apoi interclasndu-i (amestecndu-i). 8. Se presupune c numerele a1, , an snt elementele 155

unui fiier. Valoarea n nu se cunoate. S se rezolve urmtoarele probleme: s se afle valoarea maximal din consecutivitatea dat a1, , an, dup ce se elimin din ea: a) un element cu valoarea m (a1, , an); b) toate elementele cu valoarea max(a1, , an). Se subnelege c nu toate numerele a1, , an snt egale ntre ele. c) s se afle max (a1, , an) i min (a1, , an) utiliznd urmtorul algoritm. Pas cu pas s se obin perechile max(a1, , ai), min(a1, , ai) (i= 1, ..., n). Ca s se obin max(a1, , ai+1), min(a1, , ai+1), se compar ai+1 cu max(a1, , ai), apoi, dac ai+1 < max(a1, , ai), suplimentar se compar ai+1 cu min(a1, , ai). 9. Se presupune c numerele a1, , an snt elementele unui fiier. Valoarea n nu se cunoate. S se rezolve urmtoarele probleme: s se afle valoarea maximal din consecutivitatea dat a1, , an,, dup ce se elimin din ea: a) un element cu valoarea m (a1, , an); b) toate elementele cu valoarea max(a1, , an). Se subnelege c nu toate numerele a1, , an snt egale ntre ele. c) s se afle max (a1, , an) i min (a1, , an) utiliznd urmtorul algoritm. Fie n este un numr par, adic n = 2k. Atunci pas dup pas s se obin max (a1, , a2l), min(a1, , a2l) (l=1,..., k). Ca s se obin max(a1, , a2l+2), min(a1, , a2l+2), mai nti se compar ntre ele a2l+1, a2l+2 i max(a2l+1, a2l+2) se compar cu m(a1, , a2l), iar min(a2l+1, a2l+2) cu min(a1, , a2l). Dac n este un numr impar, se va efectua nc un pas suplimentar: compararea ultimului element an cu max(a1, , an-1) i, posibil, cu min(a1, , an-1). 10. Algoritmul de insertare simpl poate fi schimbat 156

astfel. Locul unde trebuie insertat n consecutivitatea sortat a1, , an, se determin prin algoritmul mpririi n jumti (V. 4). Se obine un nou algoritm de sortate care se numete algoritm de sortate prin insertare binar (mbinarea de cuvinte insertarea binar se subnelege ca insertare prin mprirea n jumti). Acest algoritm necesit aproximativ n* log2 n comparri ale elementelor consecutivitii. S se scrie programul care realizeaz acest algoritm. 11. Sunt date obiectele a1, , an. S se obin n ordine cresctoare toate numerele diferite ce intr n a1, , an. Pentru aceasta s se foloseasc algoritmul de sortare prin insertare binar. n procesul de sortare s se elimine elementele care se repet. Dac n urma cutrii locului ai n consecutivitatea deja sortat a1, , ak (k < i) se va depista c printre a1, , ak este deja un element egal cu ai, trebuie de cercetat urmtorul ai+1 fr schimbarea consecutivitii a1, , ak. 12. Algoritmul von Newmann de sortare a tabelului a1, , an n ordine nedescresctoare (algoritm de sortare prin unire) se bazeaz pe unirea de mai multe ori ale grupelor de elemente deja sortate ale tabelului dat. Mai nti tabelul este privit ca o totalitate de grupe sortate ce conin cte un element. Unirea grupelor vecine ne dau noi grupe deja sortate care conin cte dou elemente (poate, posibil, cu excepia ultimei grupe pentru care nu s-a gsit pereche). Apoi grupele se mresc prin aceeai metod, pn cnd obinem o grup care conine toate elementele consecutivitii iniiale deja sortate. Pentru sortare, la etapele intermediare, se 157

utilizeaz nu numai tabelul a1, , a, dar i unul auxiliar b1, , bn. Figura 1 ne ilustreaz

Fig.1. cum se unesc dou etape ale tabelelor a1, , an i b1, , bn reprezentndu-le n form de segmente mprite n pri corespunztoare grupelor de elemente sortate. Numrul de grupe sortate se micoreaz. Deci se va obine acel moment cnd tabelul a1, , an sau b1, , bn va conine o grup de elemente deja sortate. Aceasta nseamn c tabelul este sortat. Pentru unirea a dou grupe sortate, care conine corespunztor i q elemente, este suficient de a produce nu mai mult de p+q comparri. Rezult c pentru o singur etap de sortare este suficient de a efectua nu mai mult de n comparri i tot attea permutri. Demonstrai c algoritmul lui fon Newmann necesit aproximativ n log2 n comparri i tot attea permutri. Spre deosebite de ali algoritmi de sortare numai algoritmul de insertare binar necesit acelai numr de comparri. ns algoritmul lui fon Newmann se deosebete de cel menionat prin utilizarea a mai puine permutri ale elementelor a1, , an (cu toate c necesit un tabel suplimentar b1, , bn). De scris programul care realizeaz algoritmul lui von Newmann. 13 Fie se d tabelul de obiecte a1, , an. Pentru sortarea tabelului s se utilizeze urmtoarea procedur. S se aranjeze elementele tabelului a1, 158

, an astfel, ca la nceputul tabelului s fie grupa de elemente valorile crora snt mai mari ca elementul de pe primul loc, urmeaz valoarea primul element al consecutivitii date i apoi vor urma elementele cu valori mai mici sau egale ca primul element. Astfel, pas cu pas efectund procedura descris, s se obin tabelul sortat. Numrul de comparri fiecare etap nu trebuie s ntreac n 1. 14. Fie a i b snt fiiere, k este un numr natural. Vom spune c fiierele i b snt corelate k-sortate, dac: a) n fiecare din fiierele a i b primele k componente, urmtoarele dup ele k componente etc. formeaz grupe sortate; ultima grup a componentelor din fiier (tot sortat) poate conine mai puin de k elemente, dar numai un singur fiier poate avea o grup necomplet; b) numrul grupelor sortate a fiierului a se deosebesc de numrul grupelor sortate a fiierului b nu mai mult dect cu o unitate; c) dac ntr-un fiier numrul grupelor sortate snt mai puin cu o unitate dect n cellalt fiier, grupul incomplet de elemente (mai puin de k elemente) poate fi numai n fiierul cu mai multe componente. d) componentele a dou fiiere i b corelate k-sortate pot fi alocate n fiierele g i h astfel, nct g i h s fie corelate 2k-sortate. Aceasta se efectueaz prin intermediul unirii grupelor sortate. Rezultatele unirii se aranjeaz alternativ cnd n fiierul g, cnd n fiierul h. Fig.2 ilustreaz acest proces la primele uniri ale grupelor 159

sortate. Fiierele se prezint n form de segmente, componentele crora se prezint ca grupe sortate cu numrul determinat de componente. S se Fig.2. finalizeze descrierea acestui algoritm cercetnd etapa final. S se demonstreze c fiierele g i h ntr-adevr vor fi corelate 2k- sortate. S se realizeze acest algoritm n form de program.

15. Fie dat tabelul a1, , an. Trebuie de permutat elementele a1, , an astfel, ca la nceput de tabel s fie grupa de elemente mai mari dect elementul care se afl pe primul loc n tabelul iniial, apoi nsi acest element, dup care urmeaz cele mai mici sau egale cu el. Numrul de comparri i permutri nu trebuie s ntreac n - 1. Algoritmul recursiv de sortate (algoritmul de sortare rapid) care se bazeaz pe transformrile descrise mai sus este urmtorul. Dac tabelul conine nu mai mult de un element, tabelul se consider sortat. n caz contrar, efectum transformarea descris n V..10 i determinm rezultatele de utilizare a algoritmului de sortare rapid pentru tabelul a1, , an n felul urmtor: mai nti se formeaz prima grup sortat cu ajutorul algoritmului de sortare rapid, apoi fr schimbare acel element care mparte 160

grupa nti i doi, dup care urmeaz grupa doi de elemente, sortat cu ajutorul algoritmului de sortare rapid. Acest algoritm nu utilizeaz tabelul suplimentar i necesit aproximativ n log2 n comparri i tot attea permutri de elemente. Acestea snt caracteristicele numerice medii. Pentru cel mai ru caz, numrul de comparri poate ajunge la n(n1)/2. n afar de aceasta, acest algoritm necesit recursie. S se scrie un algoritm care realizeaz algoritmul de sortare rapid.

161

Tema: Prelucrarea excepiilor. Blocul try{} throw() catch() Scopul lucrrii: familiarizarea studenilor cu prelucrarea excepiilor, lucrul cu blocul try{} throw () catch() Consideraiile teoretice necesare: Tratarea excepiilor Excepiile snt situaiile neateptate aprute n cadrul sistemului care ruleaz un program. Programele trebuie s conin proceduri de tratare a acestor situaii excepionale. In C++ s-a realizat un mecanism de tratare a excepiilor. Astfel, o excepie este un obiect a crui adres este trimis dinspre zona de cod, unde a aprut problema, ctre o zon de cod, care trebuie so rezolve. Paii care trebuie, n general, urmai n vederea tratrii excepiilor n cadrul programelor C++ snt: se identific acele zone din program, n care se efectueaz o operaie despre care se cunoate c ar putea genera o excepie i se marcheaz n cadrul unui bloc de tip try. In cadrul acestui bloc, se tasteaz condiia de apariie a excepiei, i n caz pozitiv se semnaleaz apariia excepiei prin intermediul cuvntului- cheie throw; se realizeaz blocuri de tip catch, pentru a capta excepiile atunci cnd acestea snt ntlnite. Blocurile catch urmeaz un bloc try, n cadrul crora snt tratate excepiile. 162

Lucrarea de laborator nr. 7.

Sintaxa pentru try: try { // cod throw TipExcepie; } Sintaxa pentru throw: throw TipExcepie; Sintaxa pentru catch: catch(TipExcepie) { // cod tratare excepie } Dac TipExcepie este "...", este captat orice excepie aprut. Dup un bloc try, pot urma unul sau mai multe blocuri catch. Dac excepia corespunde cu una din declaraiile de tratare a excepiilor, aceasta este apelat. Dac nu este definit nici o funcie de tratare a excepiei, sistemul apeleaz funcia predefinit, care ncheie execuia programului n curs. Dup ce funcia este executat, programul continu cu instruciunea imediat urmtoare blocului try. TipExcepie nu este altceva dect instanierea unei clase vide (care determin tipul excepiei), care poate fi declarat ca: class TipExcepie {}; n continuare vom prezenta un exemplu de program care utilizeaz tratarea excepiilor. #include <iostream.h> #define MAXX 80 #define MAXY 25 class Point { public: class xZero {}; 163

class xOutOfScreenBounds {}; Point(unsigned x1, unsigned y1) { x =x1; y =y1; } unsigned GetX() { return x; } unsigned GetY() { return y; } void SetX(unsigned x1) { if(x1 > 0) if(x1 < = MAXX) x =x1; else throw xOutOfScreenBounds(); else throw xZero(); } void SetY(unsigned y1) { if( y1 > 0) if( y1 < = MAXY) y =y1; else throw xOutOfScreenBounds(); else throw xZero(); } protected: int x, y; }; void main() { Point p(1, 1); try { p.SetX(5); cout<<"p.x successfully set to "<<p.GetX()<<"."<< endl; p.SetX(100); } catch(Point::xZero) { cout << "Zero value!\n"; } catch(Point::xOutOfScreenBounds) { cout << "Out of screen bounds!\n"; } catch(...) { cout << Unknown exception!\n"; } } 164

Rezultatul ndeplinirii programului: p.x successfuly set to 5. Datorit faptului c excepia este instanierea unei clase, prin derivare pot fi realizate adevrate ierarhii de tratare a excepiilor. Trebuie de avut ns n vedere posibilitatea de apariie a unor excepii chiar n cadrul codului de tratare a unei excepii, situaii care trebuie evitate. ntrebrile pentru verificarea cunotinelor: 1. Cum snt realizate excepiile n cadrul programelor C+ + ? Dai exemple. Temele pentru acas: 1. Ce rezultate vor fi obinute la rularea urmtorului program: void main() { cout<<nceput; try{ // nceputul blocului try cout<<interiorul blocului try \n; throw 10; // generarea erorii cout<<Aceasta nu se va executa; } catch (int i) { cout<<Este prelucrat eroarea: ; cout<<I<<\n;} cout << Sfrit; } Teme pentru lucrri de laborator: 1. Scriei un program care calculeaz numrul de elemente ale unui fiier care snt mai mici ca valoarea medie aritmetic a tuturor elementelor acestui fiier. 2. Scriei un program care compar dou fiiere date. 3. Scriei un program care determin numrul maximal i cel minimal dintr-un ir de numere 165

aleatoare dintr-un fiier. Subconsecutivitatea de elemente dintre numrul maximal i cel minimal determinat s se nregistreze ntr-un nou fiier. 4. Scriei un program care tiprete toate cuvintele diferite de ultimul cuvnt dintr-un fiier. Cuvintele din fiier snt separate prin virgul, iar.dup ultimul cuvnt se pune punct. 5. Scriei un program care formeaz un fiier nou selectndu-se din trei fiiere date mai nti numerele negative, zerourile, apoi numerele pozitive. 6. Scriei un program care ordoneaz lexicografic o consecutivitate de nregistrri (dintr-un fiier) de tipul struct { char nume [30]; int ani} nregistrare; 7. Scriei un program care formeaz un fiier nou din trei fiiere date dup urmtoarea legitate: se selecteaz mai nti numerele divizibile la 3, la 5 i la 7, apoi numerele pozitive pare de pe locuri impare. 8. Scriei un program care sorteaz lexicografic cuvintele dintr-un text. Pentru fiecare element sortat s se indice numrul de repetri ale cuvntului n textul dat. 9. Scriei un program care din dou fiiere ordonate descresctor se vor uni n unul nou n care se va pstra ordinea descresctoare de sortare. 10. Scriei un program care va tipri n ordine invers subconsecutivitatea de numere dintre valoarea minim i maxim ale unei consecutiviti de numere citit dintr-un fiier. 11. Scriei un program care calculeaz suma nmulirii numerelor vecine pe locuri pare dintr-un fiier dat. 12. Scriei un program care determin frecvena cu care a fost generat fiecare element n fiierului 166

13.

14.

15.

16.

creat. Valorile elementelor snt cuprinse ntre 1 i 100. Scriei un program care determin numrul maximal i cel minimal din numerele unui fiier dat. S se determine elementele mai mari ca cel minimal i mai mici ca numrul maximal. Scriei un program care formeaz un fiier nou din cel dat dup urmtoarea legitate: elementele fiierului nou se obine din inversul numerelor din fiierul dat. Scriei un program care s formeze un fiier nou care conine elementele a dou fiiere ordonate cresctor.Elementele fiierului format va pstra ordinea cresctoare de sortare. Scriei un program care efectueaz reformatarea unui fiier textual n felul urmtor. Lungimea rndului de caractere n fiierul nou are lungimea de 60 de caractere. Dac n rndul dat se depisteaz punctul, restul rndului din fiierul dat se scrie din rnd nou.

Bibliografia
1. D. Somnea, D. Turturea. Introducere n C++. Programarea obiect orientata. Bucureti, ed. Teora, 1993. 2. O. Catrina, L. Cojocaru. Turbo C++. Bucureti, ed. Teora, 1994. 3. D. Costea. Iniiere n limbajul C. Bucureti, ed. Teora, 1996. 4. D. M. Popovici, I. M. Popovici, I. Tnase. C++. Tehnologia orientat pe obiecte. Aplicaii. Bucureti, ed. Teora, 1996. 5. L. Negrescu. Iniiere n limbajul C, C++. Cluj, 1996. 6. . . ++ . . , c, 1996 167

7. . . ++. . ., , BHV, 1997. 8. . . + +. , c, 1997. 9. G. D. Mateescu. C++ limbaj de programare. Bucureti, ed. Petrion, 1998. 10.L. Negrescu. Limbajele C, C++ pentru nceptori. ClujNapoca, ed. Albastra, 2000, v.II. 11. . + +. .-BINOM, 2000. 12.D. M. Popovici, I. M. Popovici, I. Tnase. C++. Tehnologia orientat pe obiecte. Aplicaii. Bucureti, ed. Teora, 2002.

168