UNIVERSITATEA TEHNICĂ A MOLDOVEI

LIMBAJUL DE PROGRAMARE C++ Îndrumar de laborator

Chişinău 2002

2

UNIVERSITATEA TEHNICĂ A MOLDOVEI CATEDRA CALCULATOARE

LIMBAJUL DE PROGRAMARE C++ Îndrumar de laborator

Chişinău U.T.M. 2002 3

U. Prin problematica pe care o tratează îndrumarul poate fi utilizat şi de studenţii altor specializări care studiază şi practică limbajul de programare C++.PREFAŢĂ Îndrumarul este adresat studenţilor cu specializările Calculatoare. şt. Chişinău. Formatul hîrtiei 60x84 1/16 Tipar ofset.T. Ştefan cel Mare.-m. Informatică şi Microelectronică pentru a fi utilizat la executarea lucrărilor de laborator ce ţine de limbajul de programare C++.. f. str. Fiecare lucrare de laborator conţine materialul teoretic necesar executării lucrării respective. Tamara Sibirschi Redactor: Culegere computerizată: dr.şt. Chişinău. Elena Boian Bun de tipar Hîrtie de ofset Tiparul 100 ex.T. şt. Elena Boian Redactor responsabil: dr. 11 ©U.M.M.. 168 Secţia de redactare. Studenţilor. Microelectronica.-m. Coli de tipar Comanda nr.M. 2002 4 . Elaborare: dr. Tehnologii informaţionale. bd.f. tehn. Automatica şi Informatica de la Facultatea de Calculatoare.T. editare şi multiplicare a U. tema pentru acasă şi problemele pentru executarea lucrării.

supraîncărcarea funcţiilor şi operatorilor. Formatarea fluxurilor numerice şi textuale. Lucrarea de laborator nr. destructori). Clase derivate. Lucrare a de laborator nr. Tipuri de date recursive. Fluxurile stringuri şi de memorie. Lucrarea de laborator nr. do-while. Operatorii limbajului C++. 7.Funcţii şi clase prietene. operaţii asupra listelor. Templates: template pentru clase şi funcţii. Fluxurile Input şi Output standard şi definite de utilizator. Clase (constructori. Reprezentarea tipurilor de date ale limbajului C++ în memoria calculatorului. 6. 1. Blocul try{…}catch()… Bibliografie 5 7 59 79 104 111 128 142 147 5 . goto). Construcţiile elementare ale limbajului C++ (instrucţiunile for. while.CUPRINSUL Introducere Lucrarea de laborator nr. 3. 5. Fişierele. if-else. Clase derivate cu moştenire multiplă. switch-break. Lucrarea de laborator nr. Prelucrarea excepţiilor. Lucrarea de laborator nr. funcţii virtuale. Construirea şi elaborarea programelor recursive. 4. Lucrarea de laborator nr. arborilor. 2.

Îndrumarul prezentă lucrările de laborator pe care studenţii trebuie să le execute la disciplina “Programarea în limbajul de programare C++”. care constă din descrierea algoritmului de rezolvare a problemelor lucrărilor de laborator. Aceste compartimente împreună cu bibliografia vor fi utilizate de studenţi pentru executarea lucrărilor de laborator. temele pentru acasă şi de laborator. cît şi un limbaj de nivel înalt cu proprietăţi caracteristice limbajelor din această categorie ce permite de a crea aplicaţii în baza principiilor programării orientate pe obiect. colectează datele pentru darea de seamă a lucrării. – să răspundă la întrebările de control puse de către profesor.INTRODUCERE Limbajul C++ se consideră atît un limbaj de programare intermediar – de nivel scăzut datorită apropierii sale de limbajele de asamblare cu posibilităţi de manipulare a biţilor. Pentru fiecare lucrare studentul pregăteşte dare de seamă pe care o susţine în faţa profesorului. consideraţiile teoretice necesare. etc. Pentru a fi admis la îndeplinirea lucrării de laborator fiecare student trebuie să îndeplinească următoarele condiţii: – să execute tema pentru acasă. În perioada efectuării lucrărilor de laborator studentul compilează şi execută programele corespunzătoare problemelor indicate de către profesor în conformitate cu îndrumarul de laborator. întrebări pentru verificarea cunoştinţelor. Lucrarea de laborator se consideră executată după ce studentul demonstrează profesorului funcţionarea corectă a programelor la calculator.. Pentru fiecare lucrare de laborator este indicată tema şi scopul lucrării. 6 . operaţii cu memoria. apel de sistem de operare. efectuează analiza datelor de intrare şi a rezultantelor obţinute.

Darea de seamă pentru fiecare lucrare de laborator include: foaia de titlu. listingul programului. descrierea algoritmului de rezolvare a problemei propuse. tema. scopul lucrării. conţinutul problemei propuse spre rezolvare. datele obţinute în urma executării lucrării respective. bibliografia utilizată. Temele de laborator pentru studenţi se indică în conformitate cu următoarea tabelă: 1 2 3 4 5 6 7 7 8 9 10 11 12 13 14 15 1 1 2 3 4 5 6 7 1 1 1 1 0 1 1 1 2 1 3 1 4 1 5 2 1 2 3 4 5 6 7 2 2 2 1 0 1 1 1 2 1 3 1 4 1 5 3 1 2 3 4 5 6 7 3 3 3 1 0 1 1 1 2 1 3 1 4 1 5 7 4 1 2 3 4 5 6 7 4 4 4 1 0 1 1 1 2 1 3 1 4 1 5 5 1 2 3 4 5 6 7 5 5 5 1 0 1 1 1 2 1 3 1 4 1 5 6 1 2 3 4 5 6 7 6 6 6 1 0 1 1 1 2 1 3 1 4 1 5 7 1 2 3 4 5 6 7 7 7 7 10 11 12 13 14 15 .

iar pe orizontală este numărul de ordine al lucrării de laborator propuse pentru executare. 8 .unde pe verticală este numărul de ordine al studentului în registru.

while. Construirea şi elaborarea programelor recursive. if-else. operaţii asupra listelor. reprezentînd cuvintele cheie ale limbajelor indicate. if-else. Pi //legal mesaj //legal maxx //legal x3 //legal 3x //ILEGAL În cadrul mulţimii identificatorilor posibili. remarcăm o clasă aparte. do-while. do-while. arborilor. tipuri de date recursive. Cele mai frecvente cuvintele cheie ale limbajul C++sînt auto delete float interrupt register template break do for long return this case double friend near short typedef 9 . Tipuri de date recursive. construcţiile elementare ale limbajului C++ (instrucţiunile for. goto). switch-break. 1 Reprezentarea tipurilor de date ale limbajului C++ în memoria calculatorului. construirea şi elaborarea programelor recursive. Consideraţiile teoretice necesare: Tipurile simple şi structurate ale limbajului C++ . operatorii limbajului C++. lucrul cu fişierele. Operatorii limbajului C++.Lucrarea de laborator nr. Scopul lucrării: familiarizarea studenţilor cu reprezentarea tipurilor de date ale limbajului C++ în memoria calculatorului. Identificatorii limbajului C++ sînt formaţi cu ajutorul caracterelor alfanumerice şi caracterul de subliniere “_”. Fişierele. switch-break. Construcţiile elementare ale limbajului C++ (instrucţiunile for. while. arborilor. operaţii asupra listelor. goto). Primul caracter al unui identificator nu poate fi o cifră.

înainte ca programul să-şi înceapă execuţia. iniţializate explicit. int întreg pe 2 octeţi 10 .0e-5. ca în următoarele exemple: char backslash = '\\'. Variabilele externe şi statice se iniţializează implicit cu zero. ca în exemplul de mai jos: int i. Tipurile fundamentale ale limbajului C++ sînt char reprezentînd tipul caracter pe 1 octet. Fiecare variabilă şi constantă posedă un tip.n.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 Declararea variabilelor poate fi efectuată înainte de a fi folosite. care determină dimensiunea spaţiului necesar memorării lor. char c. aceasta serveşte la iniţializare. iniţializarea are loc o singură dată. sînt iniţializate la fiecare apel al funcţiei în care sînt conţinute. Domeniu de acţiune a variabilelor. Dacă numele este urmat de semnul egal şi de o constantă. Dacă variabila este externă sau statică. float eps = 1. cu toate că anumite declaraţii pot fi făcute în funcţie de context. linie[80]. Tipurile datelor se pot divide în două categorii: tipuri fundamentale şi tipuri derivate sau structurate. dar este un bun stil de programare acela de a efectua iniţializarea lor în orice caz. Variabilele automate pentru care nu există o iniţializare explicită au valoare nedefinită. Variabilele pot fi iniţializate în momentul declaraţiei lor. Variabilele automate. 0 declaraţie specifică un tip şi este urmată de o listă de una sau mai multe variabile de acel tip. int i = 0.

JOS. . adevărat) condiţie. Putem defini tipuri enumerabile fără a specifica numele acestora. primul membru avînd. avîndu-se în vedere că doi membri ai aceluiaşi tip nu pot avea aceeaşi valoare. enum DIRECŢIE {SUS. Exemple de tipuri de date enumerabile: enum ANOTIMP (IARNA=1. implicit. Procedînd astfel. culoare_fond. ataşat unui număr real pe 8 octeţi Aceste tipuri admit diferite variante. Tipurile enumerabile sînt introduse prin sintaxa nume {membrul. PRIMĂVARA. ) varl. . însemnînd un număr real pe 4 octeţi double.var2. Limbajul C++ permite atribuiri de tipul condiţie=0. culoare_linie. Valorile membrilor următori se stabilesc conform regulilor menţionate. Valoarea fiecăruia este obţinută prin incrementarea cu 1 a valorii membrului anterior.long întreg pe 4 octeţi float. . . valoarea 0. defineşte tipul de dată CULORI şi declară variabilele culoarea_punct şi culoare_linie urmate de declarările a încă două variabile culoare_cerc şi culoare_fond de tipul enumerabil. DREAPTA. enum {bine. CULORI culoare_cerc. TOAMNA) .membru2. . 11 . ALBASTRU } culoarea_punct. enum BOOLEAN {fals. numite tipuri de bază de date. Membrii unui tip enumerabil trebuie să fie numai de tip întreg. cel_mai_bine}. Utilizarea variabilelor de tip enumerabil. de exemplu. enum CULORI {ROŞU . De exemplu. foarte_bine. STÎNGA=5}. putem grupa un set de constante fără a denumi acea mulţime. . VARA. VERDE. . Iniţializarea unui membru cu o valoare oarecare.

Dacă j este un alt int. referinţe. Deoarece operatorul adresă & furnizează adresa unei variabile. int *pi=NULL. j. atunci j=*pi asignează lui j conţinutul locaţiei indicate de pi. // pointer la un pointer pe caractere. Tipurile structurate sînt obţinute de la tipurile de bază. de exemplu. } Este bine ca astfel de atribuiri să fie însoţite de conversia de tip corespunzătoare. Un alt operator unar ce însoţeşte clasa pointerilor este * care furnizează conţinutul locaţiei de memorie de pe adresa indicată de către pointer. // i=20. Enumerările. adresa 1000). condiţie=false. definite în interiorul structurilor limbajului C++. tablouri. uniuni şi clase. instrucţiunea pi=&i asignează variabilei pi adresa lui i. nu sînt vizibile în afara acestora. *pi=20. Pentru a defini un pointer vom specifica tipul datei a cărei adresă urmează să o memoreze. // pointer către un întreg char **s. adică j=i. Tipurile derivate acceptate de limbajul C++ sînt: pointeri. 12 . Are loc următoarea echivalenţă: j=*pi.while (! condiţie) { cout <<”Utilizarea variabilei enumerabile”. Pointerul este o variabilă care conţine adresa unei alte variabile de orice tip. int *ip. condiţie=true. condiţie=(enum BOOLEAN) 0. int i=15. // adică 15. pi=&i.(de exemplu. Să considerăm o variabilă de tip int i şi un pointer pi către un întreg. *pi=i. structuri.

double *pd. (*pc)++).Pointerii pot apărea în expresii. *pi=i. dacă pi conţine adresa lui i. operatorii unari * şi & sînt prioritari faţă de cei aritmetici. Referiri prin pointeri pot apărea şi în membrul stîng al atribuirilor. pi++. i=10. În ultimul exemplu parantezele sînt necesare. pd++ =%p. pf++. *pd=d. atunci pj=pi. f=12. void main() { int *pi. (*pf)++ =%p. astfel.001. pc). d=0. De exemplu. *pc). cum ar fi j=*pi+l. char *pc. (*pd)++ =%p. pc++=%p”. printf(“pi++ =%p. // adică j=i+l. *pc=%c”. Dacă pj este un alt pointer la int. pf++ =%p. c=’a’. *pd=%e. pd. atunci *pi poate apărea în orice context în care ar putea apărea i. *pi. ca şi (*pi)++. această expresie adună 1 şi asignează valoarea obţinută lui j ori de cîte ori pointerul pi avansează. pd=%p. printf(“*pi=%i.5. *pf=%f. atunci *pi=0 îl pune pe i ca 0. De exemplu.*pi). (*pf)++. float *pf. *pf. (*pc) ++ = %p”. (*pd)++. pc++). fără ele se incrementează pi în loc să incrementeze ceea ce indică pi. deoarece operatorii unari * şi ++ sînt evaluaţi de la dreapta spre stînga. (*pi)++ . pd++. *pc=c. ei pot fi manevraţi ca orice altă variabilă. pc=%p”. d=sqrt((double)*pi). printf(“pi=%p. printf("%d\n". pf. *pd. pf=%p. 13 . } Deoarece pointerii sînt variabile. iar *pi+=1 îl incrementează pe i. Dacă pi conţine adresa lui i. *pf=f. În expresii ca j=*pi+1. printf(“(*pi)++ =%p. pi.

lipseşte conversia de tip cp=(char*) vp. // legal . 14 .copiază conţinutul lui pi în pj. conţinînd o adresă. // ILEGAL . pentru a defini o referinţă vom folosi simbolul “&”. Pointerii pot fi indicaţi către elemente fără tip. // declararea unui întreg int *p=&i.pointerul la caracter depus în //pointerul către void cp=vp. în a căror declarare se utilizează simbolul “*”. Există însă o diferenţă majoră între p şi r. să fie în legătură cu o altă variabilă. Referinţa prezintă o legătură cu o variabilă. // pointer către void vp=cp. acesta nefiind altceva decît o redenumire a variabilei i. // acţiune asupra lui i r=20. // legal .pointerul către void depus în pointerul către caracter cu conversie de tip. int i. char *cp. // acţiune asupra lui i *p=13. a cărei locaţie de memorie o va conţine. Putem atribui unui pointer void valoarea unui pointer non-void fără a fi necesară o operaţie de conversie de tip typecast. adică pj va pointa la variabila adresa căreia este indicată în pi. la un moment dat. diferită de cea a lui i. în timp ce r nu-şi poate schimba referinţa. cu void. i=55. În ceea ce priveşte utilizarea referinţelor. astfel că pj se va modifica odată cu pi. // pointer către un caracter void *vp. ci şi datorită faptului că p poate. va trebui să ţinem cont de următoarele restricţii: – referinţele trebuie iniţializate chiar în momentul declarării lor. Spre deosebire de pointeri. // definirea unei referinţe la i Atît p. cît şi r acţionează asupra lui i. // definirea unui pointer la i int &r=i. nu numai în modul de apelare. // acţiune asupra lui i.

Dacă pc indică un element al lui linie. este identic cu*((E1)+(E2)). pc+i este adresa lui linie[i]. Astfel. linie va constitui un pointer la caracter. în calcularea dimensiunii memoriei ocupate de obiectul indicat. Aceste remarci sînt adevărate indiferent de tipul variabilelor din tabloul linie. referinţelor nu li se pot schimba locaţiile la care se referă. în acelaşi timp. în general. Referinţele pot fi utilizate drept constante. pc+1 indică elementul următor şi. de fapt. sînt tipuri de date foarte apropiate pointerilor şi referinţelor. va copia conţinutul lui linie[0] în c. Dacă pc este un pointer la un caracter. *(pc+1) referă conţinutul lui linie[1]. prin extensie. atunci atribuirea pc=&1inie[0]. dar putem avea o referinţă la un pointer. Astfel. pot fi iniţializate cu constante.– odată fiind iniţializate. 15 . Dacă pc indică elementul linie[0]. funcţii sau chiar structuri. toată aritmetica pointerilor constă. iar pc+i cu i elemente după acelaşi element. din rîndul cărora fac parte vectorii şi matricele. – nu sînt permise referinţe la referinţe şi pointeri către referinţe. defineşte linie ca fiind un şir de 80 de caractere şi. de forma E1[E2]. Definiţia adunării unităţii la un pointer şi. prin definiţie. face ca pc să refere primul element al tabloului linie (de indice zero). declaraţia char linie[80]. atunci. Observăm că operatorul de indexare []. declarat prin char *pc. Vom vedea că orice operaţie care poate fi rezolvată prin indexarea tablourilor poate fi rezolvată şi cu ajutorul pointerilor. pc-i indică cu i elemente înaintea elementului indicat de pc. iar *(pc+i) este conţinutul lui linie[i]. Acum atribuirea c=*pc. Aceasta înseamnă că pc conţine adresa lui linie[0]. Tablourile.

Dacă pc este un pointer el poate fi utilizat în expresii cu un indice pc[i] fiind identic cu *(pc+i). de exemplu. Există situaţii în care pointerii pot fi separaţi. p<q este adevărată. >.în pc+i i este înmulţit cu lungimea obiectelor pe care le referă pc. Un pointer este o variabilă. zero fiind un caz special. dar nu există nici o şansă în a compara pointeri situaţi în tablouri diferite. în afara celor de indexare. construcţii de tipul linie++ fiind interzise. se garantează că nici un pointer care conţine adresa unei date nu va conţine valoarea zero. Deci. Singurele operaţii permise a fi efectuate asupra numelor tablourilor. Numele acestui tablou este o expresie de tip pointer. // şi pc++. Această valoare este atribuită constantei simbolice NULL pentru a indica mai clar că aceasta este o valoare specială pentru un pointer. Relaţiile == şi != sînt şi ele permise. Dacă p şi q indică elemente ale aceluiaşi tablou. tablouri constituie unul din punctele forte ale limbajului C++. Orice pointer poate fi testat cu NULL. sînt operaţii permise. în cazul în care p indică un element anterior elementului pe care îl indică q. În general. 16 . Referinţa la un tablou este convertită de către compilator într-un pointer spre începutul tabloului. =. întregii nu pot fi asignaţi pointerilor. cele două forme fiind echivalente. Aplicînd operatorul & ambilor termeni ai acestei echivalenţe. unde linie+i fiind adresa elementului i din tabloul linie. pc=linie. Numele unui tablou este o constantă şi nu o variabilă. etc. Corespondenţa între indexare şi aritmetica pointerilor este foarte strînsă. rezultă că linie[i] este identic cu linie+i. înainte de a fi adunat la pc. lucrează conform regulilor cunoscute. valoare rezervată semnalelor de eveniment anormal. Evaluînd elementul linie[i] limbajul C++ îl converteşte în *(linie+i). sînt cele care pot acţiona asupra constantelor Aritmetica adreselor pentru pointeri. operatorii <.

nulă sau pozitivă. Toate manipulările de pointeri iau automat în considerare lungimea obiectului referit. p-q este numărul de elemente dintre p şi q. una utilizînd tablourile. Deoarece p indică şirul de caractere. Varianta cu tablourile: 17 .Este valabilă şi operaţia de scădere a pointerilor. iar cea de a doua utilizînd pointerii. Pentru claritatea problemei. în funcţie de relaţia dintre s şi t (care poate fi s<t. În cadrul ciclului while este examinat conţinutul şirului de caractere. este strlen(). } Prin declarare p este iniţializat cu s şi indică primul caracter din s. Valoarea returnată este obţinută prin scăderea caracterului de pe prima poziţie unde s diferă de t. care returnează lungimea şirului de caractere transmis ca parametru. acesta din urmă semnificînd sfîrşitul şirului. indirect. return p-s. Funcţia strcmp(s. dacă p şi q indică elementele aceluiaşi tablou. vom prezenta două variante. pînă cînd se întîlneşte '\0''. de exemplu. este posibilă omiterea testului explicit. iar p-s dă numărul de caractere parcurse (lungimea şirului). şi dacă p ar fi fost un pointer la float. astfel de cicluri fiind deseori scrise sub forma while (*p) p++. Aritmetica pointerilor este consistentă: dacă am fi lucrat cu float. prin intermediul pointerului p. int strlen(char *s) { char *p=s. Să implementăm. p++ face ca p să avanseze de fiecare dată la caracterul următor. s=t sau s>t). while (*p!='\0') p++. O funcţie. t) compară şirurile de caractere s şi t şi returnează valoare negativă. caracter după caracter. care ocupă mai multă memorie decît char. Dacă while ar testa doar dacă expresia este zero. deosebit de utilă în lucrul cu şiruri de caractere. p++ ar fi avansat la următorul float. o funcţie de comparare a două şiruri de caractere. Astfel.

Dacă E este un tablou n-dimensional de dimensiuni i. j. scăderea sau compararea a doi pointeri).} Dacă ++ şi . . sînt ilegale.strcmp(char s[]. de la stînga la dreapta.*s==*t.sînt folosiţi ca operatori prefixaţi. ultimii. .. acestea din urmă nefiind permise datorită faptului că nu sînt permişi pointerii la referinţe.t++) if(*s=='\0') return(0).: char ecran [25][80]. Tablourile multidimensionale pot fi definite cu ajutorul tablourilor de tablouri. la rîndul său.. *.. este convertit imediat în pointer. k.p decrementează pe p în aceleaşi condiţii. excepţie făcînd tablourile de referinţe. atunci apariţiile lui E în expresii sînt convertite în pointer la un tablou n-1-dimensional de dimensiuni j.s++. return s[i]-t[i]. while (s[i]==t[i]) if (s[i++]=='\0') return 0. care...-. Prima dimensiune a unui tablou se foloseşte numai pentru a determina spaţiul ocupat de 18 . în afara celor menţionate deja (adunarea sau scăderea unui pointer cu întreg. Dacă la acesta se aplică explicit sau implicit (prin indexare) operatorul *.} Varianta cu pointerii: strcmp(char *s. deplasarea logică sau adunarea unui float sau double la pointer. return (*s-*t). deci. indici variază mai repede decît primii. k. de exemplu. rezultatul este tabloul n-1-dimensional indicat de pointer. deşi mai puţin frecvente. ++ şi . Tablourile sînt memorate pe linii şi. char t[]) { int i=0. pot apărea alte combinaţii de *... împărţirea. char *t) { for(. De exemplu: *+ +p incrementează pe p înainte de a aduce caracterul spre care indică p. Nu este permisă adunarea.. Alte operaţii.

În exemplul mesaj2 avem 5 litere plus caracterul nul. conţinutul tabloului fiind aleator. va trebui să dimensionăm corespunzător şirul de caractere. fiind necesare 6 locaţii în vederea memorării cuvîntului “Salut”. vom considera un program care sortează un set de linii de text în ordine alfabetică. în schimb. Pentru exemplificare. Există posibilitatea iniţializărilor parţiale. deosebirea dintre sortarea unui tablou de numere şi a unuia de şiruri 19 . Observăm că şirurile de caractere se comportă oarecum ciudat. char mesaj1[6]={'S1. Iniţializarea tablourilor poate avea loc chiar în cadrul declarării acestora int point[2]={10. prezenţa acestuia rămînînd la latitudinea noastră.19}.'a'. Diferenţa între cele două şiruri nu se află în conţinutul lor. Pointerii sînt ei înşişi variabile. în acest caz fiind determinată dimensiunea din numărul de elemente iniţializate. cît şi mesaj2 sînt şiruri de 6 caractere avînd drept terminator de şir caracterul nul. dacă tabloul este extern. “ “.acesta. Dimensionarea tablourilor se realizează ţinîndu-se în concordanţă cu necesităţile aplicaţiei. În cazul şirurilor de caractere. restul spaţiului rămas neutilizat va conţine numai caracterul nul.'u'. { }. În restul situaţiilor. se recomandă iniţializarea acestuia în cadrul unui ciclu.'t'. În cazul iniţializării prin acolade. Tablouri de pointeri. Atît mesaj1. care nu utilizează întreg spaţiu rezervat. Cu toate că algoritmul de sortare este unul comun.'\0'}. caracterul nul nu este subînţeles. adoptînd o iniţializare prin ghilimele. alocarea făcîndu-se în cadrul altui modul sau cînd se efectuează iniţializarea tabloului în declaraţie. Este permisă omiterea primei dimensiuni al unui tablou. de aceea ei sînt utilizaţi în tablouri de pointeri. ci în cadrul iniţializării lor. ea nefiind luată în consideraţie decît la determinarea unui element de indici daţi.'l'. ţinînd cont de prezenţa terminatorului de şir. char mesaj2[6]="Salut".

cum ar fi -1. . Funcţia de ieşire trebuie doar să tipărească liniile în ordinea în care apar în tabloul de pointeri. nu liniile însele. se inversează pointerii lor în tabelul de pointeri. Împărţim programul în funcţii care efectuează aceste trei etape. Dacă liniile de sortare sînt memorate cap la cap într-un şir de caractere. Avem nevoie de o reprezentare a datelor care să se poată face eficient şi potrivit regulilor de gestionare a liniilor de text de lungimi diferite.citirea tuturor liniilor la intrare. ea va returna o valoare. Va trebui să numere liniile la intrare. Două linii pot fi comparate prin transmiterea pointerilor respectivi lui strcmp(). în cazul în care se vor prezenta mai multe linii.sortarea liniilor.h> #include <string. Pointerii înşişi pot fi memoraţi într-un tablou.h> #define LINII 100 #define MAXLEN 1000 int citeste_linii(char *s[]) 20 . Deoarece funcţia de intrare poate opera doar cu un număr finit de linii.tipărirea liniilor în ordine. . Această informaţie este necesară pentru sortare şi tipărire. Introducem noţiunea de tablou de pointeri. Acest mod de lucru elimină cuplul de probleme legate de gestionarea memoriei şi poate deplasa liniile Procesul de sortare constă din trei etape: .h> #include <conio.de caractere constă în aceea că liniile de text de lungimi diferite nu pot fi comparate sau deplasate printr-o singură operaţie. Cînd două linii neordonate trebuiesc inversate. atunci fiecare linie poate fi accesată printr-un pointer la primul său caracter. #include <stdio. Funcţia de intrare trebuie să colecteze şi să salveze caracterele din fiecare linie şi să construiască un tablou de pointeri pe linii.

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

în sensul că a[5][5] şi b[5][5] sînt referinţe legale ale aceluiaşi int. Fiind date declaraţiile int a[10][10].*linieptr++). Pentru tabloul b. void scrie_linii(char *linieptr[]. Dacă linieptr este el însuşi un tablou care este transmis lui scrie_linii(). arată că linieptr este un tablou de LINII elemente. încît cei doi pot fi copiaţi unul în altul. Sortarea are loc în cadrul funcţiei sortare_linii(). iar găsirea fiecărui element se face prin calculul obişnuit al indicelui. scrie_linii(linieptr. linieptr[i] este un pointer la caractere. } else printf("Input prea mare pentru sortare \n"). plus cele 10 celule pentru pointeri. vom obţine 100 celule de memorie rezervate. temp va fi un astfel de pointer. } Declararea variabilei linieptr: char *linieptr[LINII]. el poate fi tratat ca un pointer.nlinii). fiecare dintre aceştia urmînd să indice un tablou de întregi.int nlinii) { while (. utilizările lui a şi b pot fi similare. prin declararea sa.printf("\n Textul după sortare \n"). int *b[10]. Astfel. fiecare element fiind un pointer la char.. tabloul de pointeri utilizează mai mult spaţiu şi poate cere un mod explicit de iniţializare. iar. cu fiecare incrementare. iar funcţia poate fi scrisă. Presupunînd că fiecare indică la 10 elemente din tablou.nlinii>=0) printf("%s\n". iar *linieptr[i] accesează un caracter. Dar există două avantaje: accesarea unui element se face indirect prin intermediul unui pointer. Toate cele 100 celule de memorie ale tabloului a trebuiesc alocate. } *linieptr adresează iniţial prima linie. în loc să se facă prin înmulţire şi adunare (cum este în 22 . se alocă 10 pointeri. el avansează linia următoare pînă cînd nlinii se epuizează. Dacă orice element individual din v este un pointer la caractere.

struct punct { float x. Definirea unei structuri se realizează cu ajutorul cuvîntului cheie struct. age. obiect_ de_ tip_ structură este una sau o lista de variabile pentru care se alocă memorie.y=30.. “.} p. unde tip structură descrie organizarea structurii. 0 declaraţie de structură care nu este urmată de o listă de variabile nu produce alocarea memoriei. ci descrie organizarea structurii. Asupra componentelor unei structuri putem acţiona prin intermediul operatorului de apartenenţă. element sunt numele elementelor structurii. alţii la cîte 20 de elemente sau chiar la nici unul. cît şi împreună. este o colecţie de elemente de tipuri diferite şi care pot fi referiţi atît separat. atribuirea fiind. de exemplu: p. 23 . Există posibilitatea efectuării operaţiilor cu întreaga structură.x=10. de exemplu : p={10. S-a definit o structură p ca fiind de tip punct.”. } student.cazul tabloului multidimensional). char prp. tip1.. element1. unii pot indica la cîte 2 elemente. iar liniile tabloului pot fi de lungimi diferite. ……… tip n element n } obiect_ de_ tip_ structură. punctul fiind compus din două elemente x şi y reale..30)... Ea are următoarea sintaxă: struct [tip structură] { tip1 element1.. p. int id. . De exemplu... Aceasta înseamnă că nu orice element al lui b este constrîns să indice la un vector de 10 elemente. de exemplu: typedef struct { char name[25].y. tipn indică tipul elementelor structurii. Structura.

pptr->x=10. // Echivalent cu p.y=30. caracteristic tablourilor. În cazul definirii unui pointer la o structură. iar altele invers. Un alt aspect al utilităţii structurilor îl constituie tratarea tablourilor de structuri.x=10. Ordinea de alocare a memoriei pentru cîmpuri este dependentă de sistem. elemente de acest tip pot interveni în definirea altor structuri. de exemplu: punct hexagon[6].”. Un cîmp de biţi este o configuraţie de biţi adiacenţi. masive ale căror elemente sînt de tipul acestei structuri şi. accesul la componentele acelei structuri se va efectua prin expresii de forma punct *pptr. Unele sisteme fac alocarea de la stînga la dreapta. prioritar fiind “*”. Cîmpurile pot fi accesate ca orice alt element de structură. ce apar într-un element de tip int. Accesul către membrii componenţi ai fiecărui element al vectorului se realizează prin combinarea accesului indexat. cu cel utilizat în cazul structurilor: hexagon[i]. Unele elemente ale unei structuri pot fi cîmpuri de biţi. Parantezele au rolul de a indica ordinea în care acţionează cei doi operatori “*” şi “. Cîmpurile nu au adresă şi nu li se poate aplica operatorul de adresare &. Orice cîmp trebuie să aibă toţi biţii în interiorul unei zone de tip int (nu poate avea biţi în două cuvinte diferite). Cîmpurile sînt declarate de tip unsigned. (*pptr) . iar numele cîmpului este urmat de două puncte “:” şi un număr ce reprezintă numărul de biţi ocupaţi de cîmpul respectiv: unsigned nume_cîmp:nr_biţi.x=10.Prin definirea unei structuri se va constitui un nou tip de date. definind în continuare pointeri la acea structură. punct octogon[8].y=30. // Echivalent cu p. 24 . Nu se pot utiliza tablouri de cîmpuri.

Formarea expresiilor în care intervin operatorii unari se produce de la dreapta la stînga. binari şi ternari. Spre deosebire de structuri. :: – operatorul de scop sau de rezoliţie. Operatori unari. // upunct este de tipul structurii punct } variabila. Din lista operatorilor disponibili ai limbajului C++ indicăm operatorii caracteristici lui new – pentru alocarea memoriei. 25 . În funcţie de numărul de operanzi. Toate componentele uniunii ocupă aceeaşi zonă în cadrul memoriei. în uniune este accesibilă o singură componentă a unei uniuni. 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. Acestea sînt structuri alternative pentru care dimensiunea spaţiului necesar memorării lor este egală cu cea mai mare dimensiune necesară memorării unei componente a acelei structuri. Acţiunile desfăşurate în cadrul oricărui program. punct upunct. Limbajul C++ posedă toţi operatorii limbajului C şi completează această listă cu operatori proprii. operatorii se pot clasifica în trei categorii: operatori unari. Uniunea se defineşte în aceeaşi manieră ca şi structurile. în marea lor majoritate. float ufloat. Operatorii Operatori şi expresii. Operatorul de adresare: & poate fi aplicat unei valori (&lvalue) şi are ca rezultat un pointer la obiectul definit de lvalue şi avînd acelaşi tip ca şi tipul lvalue. delete – pentru eliberarea memoriei alocate cu operatorul new. cuvîntul cheie utilizat fiind union. se efectuează prin expresiile formate prin combinaţii de date şi operatori.Un caz special de structuri îl constituie union. char uchar. variabila este de tipul union un_tip { int uint. De exemplu.

decrementează cu 1 valoarea operandului. în caz contrar. tipul rezultatului fiind int.-. întîi se utilizează valoarea acestuia. întîi se acţionează cu operatorul asupra valorii operandului şi apoi se utilizează noua valoare a acestuia..se aplică unei expresii (-expresie) în vederea inversării semnului acesteia. dacă valoarea operandului este 0. Operatorul de decrementare: .Operatorul unar minus: . poate fi utilizat atît ca prefix. Operatorul dimensiune: sizeof este de tipul sizeof (expresie) sau sizeof (tip) şi ne indică dimensiunea în octeţi a operandului. şi 0. Operatorul de incrementare: ++ incrementează cu 1 valoarea operandului. În cazul în care alocarea nu este efectuată cu succes. ca şi operatorul . determinată din declaraţiile elementelor ce apar în expresie. Operatorul ++. În cazul utilizării lor ca prefix. Operatorul de alocare a memoriei: new apare sub forma pointer_la_nume = new nume [ iniţializator] şi încearcă să creeze un obiect nume prin alocarea unui număr egal cu sizeof(nume) de octeţi în memoria heap. În cazul utilizării lor ca sufix. Conversia unei expresii (typecast): este de tipul (tip) expresie sau (expresia) şi produce conversia valorii expresiei la tipul specificat. se returnează valoarea NULL 26 . Operatorul negaţie logică: ! se poate aplica unei expresii aritmetice sau unui pointer (! expresie) şi are ca rezultat 1. Operatorul negaţie pe biţi: ~ se aplică unei expresii de tip întreg (~expresie) şi transformă 0 în 1 şi 1 în 0 în toţi biţii rezultaţi după conversiile uzuale. apoi se acţionează cu operatorul asupra valorii operandului. adresa acestuia fiind returnată. cît şi ca sufix.

} Operatori binari Operatorii aritmetici: +.i<10. else cout « "x este impar". El efectuează evaluarea expresiilor de la stînga la dreapta şi are ca rezultat şi tip valoarea şi tipul ultimei expresii. Trebuie făcută o observaţie asupra operatorului de împărţire /.Operatorul de eliberare a memoriei: delete are sintaxă de forma delete pointer_la_nume şi eliberează memoria alocată începînd de la adresa conţinută de pointer_la_nume. produce expresii de forma expresie.i++) s+=I. De exemplu. *. Deci if (x%2==0) cout « "x este par". Gruparea cu paranteze este permisă şi produce o singură valoare. Operatorul de deplasare la dreapta: » acţionează în mod similar cu precedentul.s=0. De exemplu. rezultatul este întreg (prin trunchierea rezultatului real). expresie. un număr este par dacă este divizibil cu 2. În cazul în care ambii operanzi sînt întregi. Operatorul virgulă: . funcţia 27 26 . singurul element care diferă faţă de operatorul anterior fiind sensul deplasării. Operatorul de deplasare la stînga: « are ca rezultat deplasarea către stînga a valorii operandului stîng cu un număr de biţi egal cu valoarea operandului drept. Operatorul modulo: % furnizează restul împărţirii primului operand la cel de al doilea. -. cout<< “Suma este de ”<<s<<endl. biţii eliberaţi astfel fiind completaţi cu valoarea 0. / acţionează respectînd regulile binecunoscute de calculare a expresiilor. De exemplu: void main() {int s. for(int i=0.

În cazul în care aceasta este nenulă. se returnează valoarea expresiei expr2. } Operatorii logici binari: && (şi). 1. aliniaţi la dreapta. | (sau). care corespunde adresei vide. De exemplu. >. respectiv. } Operatorii de comparaţie: <.^ (sau exclusiv) furnizează un rezultat de tip int (0 pentru valoarea false şi 1 pentru valoarea true). = =(egal). nenulă (pentru || ). funcţia numar_biti() controlează numărul de biţi pe 1 dintr-un argument întreg numar_biti (unsigned n) { int b. iar rezultatul acţiunii operatorului se regăseşte tot în acest operand. Biti (unsigned x. -. n»=1) if (n&O1) b++.unsigned n) {return (x»(p+1-n))&~ (~0«n) . Operatori ternari Operatorul condiţional: (condiţie) ? : produce expresii de forma (expr1) ? expr2 : expr3. *. ! =(neegal) au ca rezultat o valoare de tip int care este 0 în cazul în care condiţia nu este îndeplinită şi 1– în caz contrar. «. for (b=0. Pointerii pot fi comparaţi numai pe aceeaşi structură de date. |} se grupează de la dreapta la stînga. &. >=. return b. Pentru ambii operatori se efectuează evaluări de la stînga spre dreapta pînă la prima expresie de valoare 0 (pentru &&) sau.definită mai jos Biti (x. || (or). Operatorii logici binari pe biţi: & (şi). %.unsigned p. Orice expresie de tipul x op= y este echivalentă cu x =x op y. n!=0. în 28 .3 şi 2. <=. tipul expresiei de atribuire este tipul operandului stîng. /. Operatorii de atribuire: op= unde op face parte din mulţimea { +. în care se evaluează exp1. iar ultimii doi operatori permit compararea pointerului cu NULL. respectiv.3) returnează 3 biţi din poziţiile 4.4. cînd valoarea întregii expresii devine 0 şi. ^. ».

a[i]. se returnează valoarea lui expr3. de ciclare. 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++) printf("%6d %c". instrucţiuni compuse. se va executa instrucţiune_2.' ‘). 10 pe linie.a[i]. Pe o linie de program putem scrie multe instrucţiuni. Forma generală este: {lista_declaraţii lista_instrucţiuni} Instrucţiunea condiţională if. de declaraţii.". 29 . Acest exemplu poate fi scris prin intermediul instrucţiunii if în felul următor: for (i=0. Instrucţiunea if evaluează expresia expr. O instrucţiune este o expresie care se încheie cu punct şi virgulă “. ciclul următor tipăreşte N elemente ale unui tablou. Instrucţiunile pot fi scrise pe mai multe linii program.i++) if. În cazul în care se obţine o valoare nenulă. iar dacă această valoare este nulă şi există instrucţiune_2.caz contrar.(i%10==9||i==N-1)?’-\n':' ‘). else instrucţiune_2.(i%10==9||i==N-1) printf("%6d %c". if-else are una din formele: if (expr) instrucţiune. de salt. else printf("%6d %c". instrucţiuni condiţionale.a[i].i<N. Instrucţiuni Expresiile sînt utilizate în scrierea instrucţiunilor. spaţiile nesemnificative fiind ignorate. Instrucţiunile pot apărea în diferite forme: de atribuiri.i<N. Instrucţiunea compusă (blocul de instrucţiuni) grupează declaraţii şi instrucţiuni în vederea utilizării unui bloc de instrucţiuni echivalent cu o instrucţiune compusă. De exemplu. if (expr) instrucţiune_l. se trece la executarea instrucţiune_1. În cazul absenţei variantei else se va trece la execuţia instrucţiunii imediat următoare instrucţiunii if.’-\n').

fapt pentru care această instrucţiune este din clasa ciclurilor cu precondiţie. Execuţia programului va trece la instrucţiunea imediat următoare instrucţiunii de ciclu. se execută instrucţiune. De exemplu. i=0. } while (i<n). Atît timp cît valoarea expresiei expr este nenulă. iar instrucţiunea do se încadrează în categoria ciclurilor cu postcondiţie. Instrucţiunea de ciclu aritmetic for are următoarea formă generală for (expr_1.Instrucţiunea de ciclu condiţionată anterior while este while (expr) instrucţiune. dacă condiţia ciclului este falsă. do { a[i++]=0. prezenţa sa se impune în cazurile în care este necesară executarea corpului unui ciclu cel puţin o dată. indicate în contextul ciclului while. în ciclul do-while evaluarea expresiei are loc după fiecare executare a corpului ciclului. şi este echivalentă cu următoarea succesiune de instrucţiuni: 30 . urmînd ca ulterior să se execute în funcţie de îndeplinirea condiţiei finale.0. Evaluarea expresiei are loc înaintea execuţiei instrucţiunii. expr_2. Spre deosebire de instrucţiunea while. expr_3) instrucţiune. De exemplu: i=0. Din acest motiv este posibil ca corpul ciclului să nu se execute nici măcar o dată. Datorită acestui fapt instrucţiune se va executa cel puţin o singură dată. instrucţiune se va executa pînă ce valoarea expresiei expr devine falsă.0. while (i<n) a[i++]=0. Cu toate acestea. Instrucţiunea de ciclu condiţionată posterior do-while are forma do instrucţiune while (expr). Ciclul do-while este folosit mai puţin decît ciclul for.

controlul este dat instrucţiunii ce urmează ei. 31 . Sintaxa instrucţiunii este switch (expr) instrucţiune. în caz contrar. Cel mult o instrucţiune poate fi etichetată cu default: La execuţia unei instrucţiuni switch se evaluează expresia expr şi se compară valoarea obţinută cu fiecare constantă ce apare în etichetele asociate instrucţiunii. în funcţie de valoarea unei expresii de control. De exemplu. în care fiecare instrucţiune 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 aceeaşi instrucţiune switch. Instrucţiunea switch face parte din categoria instrucţiunilor de selectare. sau instrucţiunii imediat următoare instrucţiunii switch.0. number_of_nums=0. } Ciclul for este util de folosit atunci cînd există o simplă iniţializare şi reiniţializare. number_of_chars=0. implicit. if (is_num(text[k])) number_of_nums++. i<=n. i++) a[i]=0. while (expr_2) ( instrucţiune. if (is_alpha(text[k])) number_of_chars++. absenţa expresiei expr_2 fiind înlocuită. ) Oricare dintre cele trei expresii poate lipsi. for (int i=0. for ( int k=0. k++) { cout « text[k] «‘\n’. k<strlen(text).expr_1. expr_3. unde instrucţiune este o instrucţiune compusă. Dacă se găseşte o astfel de constantă. dacă aceasta există. deoarece se păstrează instrucţiunile de control al ciclului împreună. Transferul controlului se va efectua la una din variantele posibile. cu valoarea 1. controlul fiind transferat la instrucţiunea de după eticheta default.

case ‘B’: numar_caractere++. case ' a’ : numar_caractere++. int s. De obicei. În cazul ciclului for. Cînd este întîlnită. La începutul unei instrucţiuni switch pot apărea declaraţii. ea se trece la următoarea iteraţie a ciclului (while. controlul va trece la faza de reiniţializare. … // se vor completa toate cazurile posibile case ‘z’: numar_caractere++. De exemplu. break. dar nu se vor efectua iniţializări ale variabilelor de tip auto sau register. for sau switch. fragmentul următor sumează numai elementele pozitive dintr-un tablou a. break sau return. … // se vor completa toate cazurile posibile case '9':numar cifre++. în care valorile negative sînt omise. break.Pentru a se menţiona sfîrşitul unei instrucţiuni ataşate unui caz se va utiliza una dintre instrucţiunile goto. } Instrucţiunea break are forma break. În cazul lui while şi do-while aceasta înseamnă că partea de control se execută imediat. switch (text[k]) { case ‘A’ : numar_caractere++. do-while. controlul fiind transferat primei instrucţiuni din corpul blocului cel mai interior. Are ca efect terminarea execuţiei unui ciclu de tip while. for. Ca exemplu. 32 . break. case ‘0’: :numar cifre++. Instrucţiunea continue are forma continue. Are drept efect trecerea controlului următorului ciclu într-o instrucţiune de tip while sau for în care apare şi nu are nici un efect dacă nu apare în corpul unor astfel de instrucţiuni. do-while). … // se vor completa toate cazurile posibile case 'Z' : numar_caractere++. break. instrucţiunea continue nu se va aplica instrucţiunii switch. break.

de exemplu. instrucţiunea goto nu este necesară şi uşor se poate scrie programe fără ea. Astfel: for(.. i++) for(j=0. cea din urmă fiind echivalentă cu următoarea return expr. //sare indicii elementelor negative s+=a[I].. Sau căutarea primului element negativ într-un tablou bidimensional. de a ieşi afară din două cicluri deodată. Limbajul C++ oferă instrucţiunea goto pentru ramificare. j<M. j++) if (v[i][j]<0) goto found. i<N.) ( .. există cîteva situaţii în care goto îşi poate găsi locul. deoarece ea părăseşte numai ciclul cel mai din interior. if(dezastru) goto error..for (int i=0. O posibilitate este: for (i=0.} …error:. 33 32 . De exemplu. Formal. chiar dacă preţul pentru aceasta este o variabilă suplimentară sau nişte controluri repetate. j Programul cu un goto poate fi scris întotdeauna fără goto. sau return (expr).. Efectul instrucţiunii return este trecerea controlului la funcţia care a apelat funcţia respectivă fără transmiterea unei valori în prima variantă sau cu transmiterea unei valori în ultimele -două variante. i<N..) for(.s=0. i++) { if (a[i]<0) continue. căutarea în tablou devine: found=0. } Instrucţiunea return admite următoarele două forme return. instrucţiunea break nu poate fi folosită. found: // s-a găsit în poziţia i. Cu toate acestea. Cea mai obişnuită folosire este aceea de a abandona prelucrarea în anumite structuri puternic imbricate. Instrucţiunea goto şi etichete.

for (i=0.Dn. . operaţii asupra listelor. Metodele de stocare a listelor liniare se divid în metode consecutive şi stocare lănţuită. j-1 else {………….” şi este utilizată pentru a evita existenţa unei etichete chiar în faţa unei acolade de închidere a unui bloc sau în cazul în care corpul unui ciclu nu conţine nici o instrucţiune. d[1]=10. se alocă într-un tablou d de dimensiune fixă.. Ele sînt consecutivităţi de elemente de acelaşi tip. Dimensiunea 100 mărgineşte dimensiunea maximală a listei liniare. utilizate de metodele consecutive.. 100. .eliminarea unui element din listă.. grafic se poate reprezenta în modul următor: D1  D2  D3  . . i++) for (j=0.sortarea componentelor listei. adică se declară float d[100]. j<M && found.. l=2.. 34 . i<N && found.. . D2. care constă din elemente D1.de căutare a elementului după criteriul dat.insertarea unui element nou înainte sau după o componentă indicată a listei liniare. j++) found = v[i][j]<0.. Tipuri de date recursive. Elementele listei liniare. de exemplu. int l. numărul cărora se schimbă dinamic în procesul de executare a programului. if (found) { ……. Lista liniară F.de determinare a primului element în lista liniară. şi lungimea listei este indicată de variabila l. Listele simplu şi dublu lănţuite. } // a fost găsit la i-1.  Dn Asupra elementelor listelor se pot efectua următoarele operaţii: . arborii sînt formate din elemente definite de structuri cu autoreferire. Lista F în tabloul d se formează în modul următor: d[0]=7..} // nu s-a găsit Instrucţiunea vidă are forma “. arborilor.

În cazul în care alocarea nu este efectuată cu succes. p=new(DL). // pointerul la elementul curent DL *prim.Lista obţinută se păstrează în memorie în conformitate cu schema: l: 2 d: 7 10 . Descrierea acestui tip de structură cu autoreferire şi pointerul în cauză se face în modul următor: typedef struct nod // structura cu autoreferire {float val.. care încearcă să creeze un obiect nume prin alocarea unui număr egal cu sizeof(nume) de octeţi în memoria heap. începutul căreia (prima structură) este indicat de pointerul dl. Structura care defineşte elementul listei conţine în afară de componenta informaţională şi un pointer la următorul element din listă. Operatorul de eliberare delete este apelat printr-o instrucţiune de forma delete pointer_la_nume . // valoarea componentei informaţionale struct nod *urm . [0] [1] [2] [3] [98] [99] Pentru organizarea elementelor în formă de listă simplu lănţuită se utilizează structurile care sînt legate cîte o componentă în lanţ. // pointerul la următorul element din lanţ } DL.. // 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 [ iniţializator]. adresa acestuia este returnată şi asignată variabilei pointer_la_nume. De exemplu. se returnează valoarea NULL. 35 . eliberează memoria alocată începînd cu adresa conţinută de pointer_la_nume. DL *p.

NOD * prim. } NOD. Pointerul dl indică adresa de alocare pentru primul element al listei.p->val=10. * p. 36 . Pentru toate operaţiile asupra listei se va utiliza următoarea descriere a structurii elementelor liste: typedef struct nod { float val. p=new(nod). r=p. dl=new(DL)). p->val=x. Lista are următoarea formă: Operaţii asupra listelor simplu lănţuite Fiecare element al listei simplu lănţuite reprezintă o structură alcătuită din două componente: val – folosit pentru componenta informaţională şi p pentru pointer la următorul element din lista lănţuită.j. dl->val=7. În ultimul element al listei pointerul la elementul vecin are valoarea NULL. p->n=NULL. int n=1. * r. int i. dl->n=p. struct nod * urm. Pentru executarea operaţiilor pot fi utilizate următoarele fragmente de program: 1) formarea listei simplu lănţuite: float x=5.

r->val). while (p->val !=0) { p=new(nod). } 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 elementul %d este egal cu %f ". if(prim==p) printf("\n nu are vecin din stînga" ). r=p. p->val=x-1. r->val). else printf("\n vecinul din dreapta este %f".j++.p->urm=NULL. p->urm=NULL. while( r->urm!=p ) r=r->urm. } 2) tiparul elementului j: r=prim. } 4) eliminarea elementului care este succesorul elementului în cauză. else { r=prim. n++.j). r->urm=p.0*n.r->val). prim=p. la care indică pointerul р 37 .j=2. r=r->urm. while(r!=NULL && j<n-1) { if (r==NULL) printf("\n nu este elementul %d ". printf("\n vecinul de stînga este %f".

r->urm=p->urm. Interpretarea grafică a listei F=< 2.p=prim. 5) insertarea noului element cu valoarea newval=100 după elementul determinat de pointerul p: r=new(NOD). r->val=100. 38 . delete(r->urm).5. * r. // pointer la precedentul element al //listei m } NDD. NDD * prim.7. altul – la succesorul element din listă. Organizarea listelor dublu lănţuite Lista dublu lănţuită este o listă în care fiecare element conţine doi pointeri: unul la precedentul element. Lista dublu lănţuită în program se poate determina cu ajutorul următoarelor descrieri: typedef struct ndd { float val. se efectuează de operatorii r=new(NDD). * p. if ((r=p->urm)==NULL) printf("\n nu este succesorul "). p->urm=r. // pointer la succesorul element al //listei n struct ndd *precedent.1 > ca listă dublu lănţuită este următoarea: Insertarea noului element cu valoarea newval după elementul determinat de pointerul p. // valoarea informaţională a componentei struct ndd * succesor. p->urm=r->urm.

r->val=newval.accesarea elementului din vîrful stivei. ea este vidă. Asupra elementelor stivei pot fi efectuate următoarele operaţii: . numărul de elemente a căreia variază. . Eliminarea elementului urmat de elementul la care indică pointerul p se efectuează în modul următor: p->succesor=r.eliminarea elementului din vîrful stivei. Schema listei ciclice pentru lista F=< 2. iar pointerul dl indică la ultimul element al listei. dacă stiva nu este vidă. p->=r. 39 .7. cozi şi cozi de tip vagon. Dacă stiva n-are elemente. Stiva reprezintă o structură dinamică. tablouri. r->succesor=p->succesor. delete r.includerea unui element nou în vîrful stivei. ( (p->succesor)->succesor )->precedent=p. Stiva este o consecutivitate de elemente de acelaşi tip – variabile scalare.verificarea dacă stiva este vidă. p->succesor=(p->succesor)->succesor. Lista liniară este ciclică dacă ultimul element al listei indică la primul element. Stivă şi coadă În funcţie de metoda de acces la elementele listei liniare pot fi cercetate următoarele tipuri de liste liniare: stive.1 > este următoarea: La rezolvarea problemelor pot apărea diferite tipuri de liste lănţuite. (p->succesor)->precedent=r. . .5. structuri sau uniuni.

înmulţire şi împărţire se aleg numerele: -1.3>. float eval (float *m. <14>. <70>. 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 (vîrful şi sfîrşitul) ale listei.6>. accesarea elementului are loc numai asupra elementului din vîrful stivei. int l) 40 . iar operaţia se execută asupra următoarelor două elemente din vîrful stivei. cît şi dinamic – prin listă (simplu sau dublu lănţuită). <70. Fiecare număr se introduce în stivă. iar operanzii se află nemijlocit în faţa operaţiei. Pentru organizarea stivei se utilizează tabloul interior stack. În astfel de mod de prezentare a expresiei operaţiile se înregistrează în ordinea executării lor. În calitate de coduri pentru operaţiile de adunare. m[i]>0 indică numărul nenegativ.5>.6.operaţia. scădere. de asemenea. operaţia de includere şi eliminare a elementului. care calculează valoarea expresiei indicate în tabloul m în formă de expresie inversă poloneză.8>. <6>. -4. înlocuindu-le cu rezultatul operaţiei efectuate.2>. Dinamica schimbărilor din stivă va fi următoarea: S = < >. expresia (6+8)*5-6/2 în forma inversă poloneză are forma: 6 8 + 5 * 6 2 / Utilizînd noţiunea de stivă expresia aritmetică în formă inversă poloneză se execută print-o singură trecere de examinare a expresiei. Mai jos este descrisă funcţia eval. iar valoarea m[i]<0 . <6. <67>. <70. Stiva şi coada se organizează atît static prin intermediul tabloului.Astfel. Parametrii funcţiei sînt tabloul de intrare m şi lungimea sa l. -2. Vom cerceta cum se utilizează lista în formă de stivă pentru implementarea calculării expresiei aritmetice în formă inversă poloneză. -3. <70. De exemplu. <14.

Astfel de noduri se numesc terminale. break. A2. Într-un arbore există noduri cărora nu le corespund subarbori.. Prin arbore se înţelege o mulţime finită şi nevidă de elemente (noduri): A={A1. 41 . arborii respectivi se numesc subarbori ai rădăcinii. Dacă mulţimea de elemente a arborelui binar este vidă. i < l . break.n. În multe aplicaţii se utilizează noţiunea de arbori binari. case -4: stack[p]/=c. arborele binar se divide în două submulţimi: subarborele drept şi cel de stînga.. se consideră că arborele constă numai din rădăcină.i++) if ((n=m[i])<0) { c=st[p--]. care formează fiecare cîte un arbore. cu autoreferire. An}. case -3: stack[p]*=c. De exemplu. deoarece în fiecare nod subarborele stîng se consideră că precede subarborele drept. float stack[50]. for(int i=0. n>0 cu următoarele proprietăţi: –există un nod şi numai unul care se numeşte rădăcina arborelui. case -2: stack[p]-=c. return(stack[p]). } } else stack[++p]=n. } Arbori Arborii sînt structuri de date dinamice... break.{ int p. switch(n) { case -1: stack[p]+=c. –celelalte noduri formează submulţimi ale lui A.c. Arborele binar este ordonat. Dacă mulţimea de elemente este nevidă. Un nod al unui arbore binar poate să aibă numai un descendent: subarborele drept sau subarborele stîng.

int f. //declaraţii struct tnod *st. // este pointerul spre subarborele drept al //nodului curent } TNOD. Asupra arborilor binari pot fi definite următoarele operaţii: – afişarea componentelor informaţionale ale nodului. Afişarea componentelor informaţionale ale nodului se poate de efectuat prin funcţia: void prelucrare (TNOD *p) {printf(“numarul = %d apariţii= %d \n”. – insertarea unui nod terminal într-un arbore binar. –accesarea unui nod al arborelui. *q) { if (q->nr < p -> nr ) return –1. –parcurgerea unui arbore. – determinarea echivalenţei a doi arbori.un nod al unui arbore binar poate fi o structură care poate fi definită în felul următor: typedef struct tnod { int nr. – specificarea criteriului de determinare a poziţiei în care să se inserteze în arbore nodul curent.} Criteriul de determinare a poziţiei în care să se inserteze în arbore nodul curent se defineşte de funcţia: int criteriu(TNOD *p. –ştergerea unui arbore. p->nr. p->f). // insertarea nodului curent 42 . // insertarea nodului curent //în subarborele stîng al nodului spre care indică //pointerul p if (q->nr > p-> nr ) return 1. // este pointerul spre subarborele stîng al //nodului curent struct tnod *dr.

parb))==0) return q. } else { q=q->st. return p. } Accesarea unui nod al unui arbore poate fi realizată prin următoarea funcţie: TNOD * cauta (TNOD *p) {TNOD *parb. continue. } return eq(q. } int i. 43 .) if ((i=criteriu(q. continue. exit(1). *q.//în subarborele drept al nodului spre care indică //pointerul p } Insertarea unui nod terminal într-un arbore binar poate fi efectuată prin următoarea funcţie: TNOD* insertnod() { TNOD *parb. q=parb. *q. if (parb==0) return 0.} elibnod(p).q. *p. } if (i>0) if (q->dr ==0) {q->dr=p. } } if(p==0) { printf(“eroare: memorie insuficientă\n”).) if ((i=criteriu(q.p). int n=sizeof(TNOD). return p. return 0.. for (q=parb. return p. if (parb ==0) { parb=p.p)) <0) {q->st=p. int i.} else {q=q->dr. for(.

} Parcurgerea unui arbore poate fi efectuată în trei modalităţi: în preordine. } } Parcurgerea în inordine presupune parcurgerea mai întîi a subarborelui stîng. accesul la rădăcina arborelui. în final.} } Parcurgerea în postordine presupune parcurgerea mai întîi a subarborelui stîng.if(I<0) q=q->st. preord(p->dr). } void stergearbore (TNOD *p) { if (p!=0) 44 else . apoi a arborelui drept şi. prelucrare(p). apoi accesul la rădăcină şi în continuare se parcurge subarborele drept. postord(p->dr). inord(p->dr). void postord (TNOD *p) { if (p!=0) { postord(p->st). preord(p->st). Parcurgerea în preordine presupune accesul la rădăcină şi apoi parcurgerea celor doi subarbori ai săi: mai întîi subarborele stîng. void inord (TNOD *p) { if (p!=0) {inord(p->st). return 0. prelucrare(p). apoi cel drept. în inordine. void preord (TNOD *p) { if (p!=0) { prelucrare(p). } } Ştergerea unui arbore poate fi efectuată de următoarea funcţie: void elibnod(TNOD *p) { delete(p). în postordine. else q=q->dr.

....c sînt recursive...} c(){.a(). Funcţiile a.. se spune că are loc recursia înainte. se 45 ... elibnod(p).b().} Funcţia este indirect recursivă dacă se efectuează apel recursiv prin intermediul unei succesivităţi de apeluri ale altor funcţii. Toate funcţiile componente ale acestei succesivităţi de apeluri se socot recursive. care prin apelări succesive valoarea ei creşte pînă la o valoare ce satisface condiţia de finalizare a recursivităţii...c().. deoarece la apelul unei din funcţii are loc apelul altor funcţii inclusiv şi pe ea însăşi.... De exemplu. Execuţia algoritmului recursiv presupune crearea unui număr (finit) de copii ale algoritmului.} b(){. În construirea algoritmului recursiv este inclusă o condiţie de terminare a apelării recursive de o expresie.. La executarea programului cu funcţii recursive se creează copii ale acestora.... fiecare din ele corespunzînd unei valori a expresiei de recursie.b. care corespund diferitelor valori ale unei variabile. Atît timp cît expresia recursiei se calculează pînă cînd creşte pînă la o valoare ce satisface condiţia de finalizare a recursivităţii. a(){.. De exemplu: int a() {.... Cînd expresia atinge valoarea soluţiei recursiei.. Funcţia se numeşte recursivă dacă în momentul executării sale funcţia se apelează pe ea însăşi. Funcţie este nemijlocit recursivă dacă ea se apelează din corpul aceleiaşi funcţii. postord(p->dr).... } } Recursivitatea ca metodă de programare Recursivitatea presupune o repetare.} ... Ea constă în apelarea unei funcţii de către ea însăşi.a().{ postord(p->st).... sau indirect. printr-o succesivitate de apeluri ale altor funcţii.

astfel încît se obţine soluţia problemei. } if ((i=n/10)!=0) print_cifre(i).. n=-n. De exemplu. în caz contrar sistemul de calcul poate intra în impas.h> 46 . În limbajul C++ funcţiile pot să se autoapeleze. adică să fie recursivă void print_cifre(int n) { int i. În acest caz are loc recursia înapoi. Vom numi această funcţie factorial(). } Apelul funcţiei recursive creează noi copii ale variabilelor locale şi ale parametrilor pentru clasa de memorie auto şi register.execută copiile create. Executarea programelor cu funcţii recursive necesită multă memorie şi mult timp de calcul. funcţia de tipărire unui număr (ca un şir de caractere): poate fi apelată de ea însăşi.. Variabilele declarate cu clasa de memorie static nu necesită crearea noilor copii. codul programelor cu funcţii recursive este mai compact şi mai uşor de înţeles. Recursivitatea ca metodă de programare este mai eficientă. } Programul de mai jos calculează funcţia Akkerman cu utilizarea funcţiei recursive ackr şi funcţiei auxiliare smacc: // calculul recursiv al funcţiei Аkkerman # include <stdio. cu o complexitate mai mare decît cele nerecursive. valorile lor din apelurile precedente se păstrează. Pentru fiecare moment sînt accesibile numai valorile ale apelului curent. În corpul funcţiei recursive este necesar de indicat condiţia de ieşire din procesul recursiv. Valorile lor sînt accesibile în orice moment de executare a programului. if (n<0) { putchar(‘-’). long factorial(int n) {return((n==1)?1: n*factorial(n-1) ). Exemplul clasic de funcţie recursivă este calcularea factorialului numărului N! = 1*2*3*. putchar(n%10+’0’).*N.

// apeluri recursive ackr(. 47 . if(n==0 || y==0) z=smacc(n.x). case 1: return (x).&n. unde df este variabilă de tip int (descriptorul de fişier). Funcţiile de prelucrare a fişierelor de nivel inferior pot fi utilizate prin includerea fişierelor io. int). int smacc( int.x.n.x). fcntl. int x.y-1). Citirea şi scrierea în fişiere.int). Deschiderea şi închiderea fişierelor. else { z=ackr(n.&x. int..z. default: return (2).&y). Limbajul C++ include în sine funcţiile standard input/output ale limbajului C de prelucrare a fişierelor la nivelul jos. } return z. scanf("%d %d %d". case 3: return (1).y). int ackr(int.h. int y) // funcţie recursivă { int z. inferior şi superior. case 2: return (0). printf("%d". Pentru deschiderea unui fişier se utilizează funcţia open care are următoarea formă sintactică: df = open(…).void main () // funcţia în care se apelează funcţia { int x.. } } int ackr( int n. t=ackr(n.t).x. } int smacc( int n. } Fişierele input/output ale limbajul C++.y.h şi stat.) z=ackr(n-1.t.int x ) // funcţie auxiliară { switch (n ) { case 0: return(x+1).h.

Al doilea parametru se indică de una din următoarele constante: S_IREAD fişierul va fi creat numai pentru citire S_IWRITE fişierul va fi creat numai pentru înregistrare S_IEXE fişierul va fi creat numai pentru executare Citirea dintr-un fişier se efectuează prin funcţia read indicîndu-se următorii parametri: read(df. lung) unde df este descriptor de fişier. Această funcţie are următorii parametric: df – descriptorul fişierului. Înregistrarea în fişier se efectuează prin funcţia write. Această funcţie are aceiaşi parametri ca şi funcţia read. Poziţionarea într-un fişier se efectuează prin funcţia fseek(df.Funcţia open are următorii parametri: se indică calea de acces la fişier şi modalitatea de accesare a componentelor fişierului. lung – lungimea în octeţi a înregistrării citite. buf. Modalitatea de accesare a componentelor se indică prin una din următoarele constante: O_RDONLY fişierul se deschide numai pentru citirea componentelor lui O_WRONLY fişierul se deschide numai pentru înregistrarea componentelor lui O_RDWR fişierul se deschide pentru citirea şi înregistrarea componentelor lui O_APPEND fişierul se deschide pentru adăugarea componentelor noi la sfîrşitul lui O_BINARY fişierul se prelucrează binar O_TEXT fişierul se prelucrează textual Pentru a crea un fişier se utilizează funcţia creat cu următorii parametri: calea de acces la fişierul creat şi modalitatea de utilizare a fişierului. deplasarea indică numărul de octeţi pentru a 48 . deplasare. buf – pointer spre zona de memorie în care se va păstra înregistrarea citită din fişier. origine).

care conţine indicaţii de formatare.82) . 49 . Prototipurile acestor funcţii se află în biblioteca stdio. Închiderea fişierului se efectuează de funcţia close (df). char *p. close(df).h. Fişierele input/output standard se efectuează prin intermediul funcţiilor scanf şi printf. Funcţia de afişare printf utilizează ca argumente nume de variabile.deplasa capul de citire sau scriere al discului. 1faţă de poziţia curentă a capului de citire sau înregistrare.3f". respectiv. urmată de o listă de argumente. Funcţiile scanf şi fscanf. df=open(nfis. dată sub forma unui şir de caractere. Ambele funcţii utilizează specificaţiile de scriere sau citire plasate într-o constantă de tip şir de caractere. ieşirea standard sau înregistrare într-un fişier. #include<«stdio.O_RDONLY). getch şi puts. gets.h. read(df. origine are următoarele valori pentru efectuarea deplasării: 0 – faţă de începutul fişierului. int df. Funcţiile getc(). Principiul de utilizare al funcţiei printf constă în asocierea unei liste.80). getc. respective. printf şi fprintf permite citirea.316.dat”.h> void main() {printf("întreg:%6i \n real: %9. Exemplu de utilizare ale acestor funcţii: char nfis[]=”fisier1. respectiv.144. getch() citesc cîte un caracter din fişierul standard input .p. De exemplu. 2 – faţă de sfîrşitul fişierului. Prototipurile funcţiilor de prelucrare a fişierelor de nivel superior pot fi utilizate prin includerea fişierului stdio. iar funcţia de citire scanf utilizează drept argumente adrese de variabile. putc. afişarea uneia sau a mai multor valori la intrarea standard sau dintr-un fişier. o listă de variabile.

argumentul corespunzător trebuie să fie un pointer la întreg. argumentul corespunzător trebuie să fie un pointer la întreg. scanf(“%d”. %x un întreg hexazecimal este aşteptat la intrare. argumentul trebuie să fie un pointer la un întreg short. Caracterul de conversie e este*f. argumentul corespunzător trebuie să fie un pointer la un cîmp float. Fiecare semn este asociat cu caracterul care îl urmează şi este interpretat drept caracter de control. %h un întreg short este aşteptat la intrare. Fiecare semn % este interpretat ca începutul descrierii caracteristicilor de tipărire a unei valori. argumentul să fie pointer la întreg. argumentul corespunzător trebuie să fie un pointer la un cîmp double. \t tabulator. Cele mau utilizate semne sînt următoarele: Semnul Descrierea %d. %u un întreg fără semn zecimal este aşteptat la intrare. Caracterele de control utilizate sînt: \n avans la început de linie nouă. %o un întreg octal este aşteptat la intrare. Caracterul de conversie e este*e. %f un număr în virgulă flotantă este aşteptat.% i un întreg zecimal este aşteptat la intrare. \a emite un semnal sonor .int z. Formatul prezentat la intrare pentru un double este alcătuit dintr50 .&z). } Lista este parcursă de la stînga la dreapta.argumentul corespunzător trebuie să fie un pointer la întreg. \r poziţionare la începutul liniei curente. Formatul prezentat la intrare pentru un float este alcătuit dintr-un semn opţional %e un număr în virgulă flotantă este aşteptat.

char nume[50].&i.%c %s un semn opţional. mai degrabă decît la int. În acest caz.2f%2s”. care apare în lista de argumente.32E-1. u. cu linia de intrare 25 244. pentru a citi următorul caracter altul decît caracterele albe se va utiliza %1s.32E-1 Mircea va asigna lui i valoarea 25. şi x pot fi precedate de litera l. Caracterele de conversie d. float x. scanf (“%2d%4. scanf (“%d%f%s”. urmat de un întreg cu semn. care va fi adăugat.32E-1 Mircea 51 . float x. Similar. argumentul corespunzător trebuie să fie un pointer al unui tablou de caractere. un singur caracter este aşteptat la intrare. cu linia de intrare 25 244.nume) . Cele trei cîmpuri de la intrare pot fi separate de oricîte spaţii. argumentul corespunzător trebuie să fie un pointer la caracter.nume) . destul de mare pentru a încăpea şirul şi un terminator ‘\0’. pentru a indica un pointer la long.&x. un şir de numere care pot să conţină şi un punct zecimal şi un cîmp de exponent care este format din E sau e. lui x valoarea 244.&x. iar lui nume valoarea “Mircea”. De exemplu: int i. i .&i. ignorarea caracterelor albe este suprimată. taburi şi caractere de linie nouă. un şir de caractere este aşteptat. char nume[50]. Apelarea int i.o. litera l înaintea lui e sau f indică un pointer la double în lista de argumente.

cere alinierea la stînga. astfel semnul . 44. cifra 5 arată că afişarea se va face pe 5 poziţii. Cele mai utilizate secvenţe asociate valorilor de tip întreg sînt %ssNX sau %ssNU. în programul de mai jos 52 . Prin funcţia printf se afişează un mesaj care adesea este un comentariu legat de valoarea care urmează să fie introdusă.va asigna 25 lui i.3).h> void main() { printf("% -+5i". De exemplu. simbolul i arată că va fi afişată o valoare de tip întreg.arată că se va face o aliniere la stînga. Funcţiile scanf şi printf sînt utilizate împreună. ch). printf("%s %c ". Valorile de tip real pot fi tipărite utilizînd secvenţe asociate de forma %ssNMf sau %sSN. în cîmpul afectat valorii. unde s este semnul + dacă se doreşte afişarea explicită a semnului. semnul + cere afişarea explicită a semnului. De exemplu. De exemplu. în care simbolul M semnifică precizia cu care vor fi reprezentate numerele (numărul de cifre după punctul zecimal).h> void main () { char * p="abracadabra".tte. . Toate caracterele care nu aparţin secvenţelor de control sînt afişate şi sînt tratate ca şiruri de caractere.p. Funcţia scanf se utilizează la iniţializarea unor variabile. } programul afişează şirul de caractere adresat prin intermediul pointerului p şi valoarea variabilei ch. } programul indicat va afişa valoarea +3 prin aliniere la stînga în conformitate cu specificaţiile date. char ch=’B’. N este un număr care arată pe cîte poziţii se va face afişarea. iar nume va obţine valoarea “Mi”. #include<stdio. #include<stdio.32 lui x.

printf(“\n Introdu un numar întreg: "). //la fel se procedează //în cazul variabilei i scanf ("%i. Această funcţie are două argumente: un şir de caractere care conţine numele fişierului fizic recunoscut de sistemul de operare şi un alt şir de caractere care conţine indicaţii relativ la modul de utilizare al fişierului. 3f". Biblioteca stdio. specifică limbajului C. // afişarea textului “număr întreg” va printf("\n Număr întreg: %6i \n Număr real: % 9. obţinută prin intermediul funcţiei fopen().//utilizatorul va introduce. Pointerul p conţine o valoare de tip FILE fumizată de funcţia fopen(). Ultimul parametru poate conţine caracterul r pentru fişiere deschise pentru citire. //la scanf("%f". Pointerul va primi drept valoare adresa unei variabile de tip FILE. &i). printf ("Introdu un numar real: ").am afişat mesajul “numar real”. conţine definiţia unui tip de date FILE. a).h> void main() { float a. un număr urmat de Enter. după care urmează apelul funcţiei scanf care aşteaptă introducerea unei valori de tip real sau întreg: #include<stdio. } // fi urmată de introducerea. a numărului dorit. 53 .h. t pentru fişiere de tip text sau b pentru fişiere binare În exemplul de mai jos am creat un fişier prin intermediul unui pointer de tipul FILE şi o iniţializare prin funcţia fopen(). i. //tastatură. Etapele care trebuie să fie parcurse sînt definirea unui pointer de tip FILE şi asocierea unui fişier fizic. &a). la tastatură. Accesul la un fişier se face printr-un pointer de tip FILE. w pentru fişiere deschise pentru creare sau scriere. Funcţiile de scriere şi citire anterioare pot fi folosite şi în cazul fişierelor.".

”wt”). Daţi exemple de utilizare a operatorilor unari. 54 . binari şi ternari. 9. Exemple de utilizare.h> void main() { FILE *p. #include<stdio . Pentru ce se utilizează variabilele în limbajul C++? 3.3f". De ce fiecare variantă a instrucţiunii switch se poate termina cu instrucţiunea break? Exemple de utilizare a instrucţiunii switch cu şi fără instrucţiunea break."%6i\n%9. } Întrebări pentru verificarea cunoştinţelor: 1. Prin ce se deosebeşte operatorii de incrementare şi decrementare prefixate de cele sufixate? Exemple de expresii. Prin ce se deosebeşte noţiunile union şi struct? 7. Prin ce se deosebesc variabilele descrise ca extern de cele descrise ca static? 4. p = fopen(“disk.dat.71). De ce nu este permisă definirea unui vector de referinţe? 6. Prin ce se deosebeşte o referinţă de un pointer? 5. 29.dat”. Care sînt operaţiile aritmetice admise asupra pointerilor? 8. 2. 11. Corectaţi programul de mai jos care efectuează sortarea parţială a listei simplu lănţuite. după sortare pointerul v indică la elementul k1. 10. Am scris în acest fişier două valori în acelaşi mod cum am făcut afişarea la ieşirea standard. Definiţi un tablou tridimensional elementele cărora sînt de tip point. fclose (p) .care deschide pentru înregistrare fişierul disk. fprintf(p. Enumeraţi tipurile de date fundamentale în limbajul C++? 2. Explicaţi acţiunea instrucţiunilor de ciclu. 12. 13.

void main() { NOD *arrange(void). if (v->val. Indicaţie: La rezolvarea acestei probleme avem o listă sortată crescător Fi. r=prim.elementul se insertează la începutul listei. NOD *p. } 14.lista Fi este vidă. B2. ..NOD *v. sortate crescător. } else r=v.. Cercetaţi programul de mai jos typedef struct str1 { float val. 55 . .elementul se insertează la sfîrşitul listei. Pentru insertarea elementului nou în lista Fi să se cerceteze trei cazuri: . La introducerea unui nou element Bi+1 acest element se insertează la locul său în lista Fi. 15. v->n=prim. p=arrange(). } NOD. Bn din intervalul de la 1 pînă la 9999.. v=v->urm. while( r->urm!=NULL ) { v=r->urm. float k1. Alcătuiţi un program care organizează o listă ciclică Fi (1<I) dintr-o consecutivitate de numere întregi B1. prim=v. struct str1 *n.. k1=prim->val.

} } NOD *arrange() // formarea listei sortate { NOD *dl.v – pointeri la //două elemente vecine. p->n=r. 17. dl->val=0.începutul listei. *p. *r. Inversaţi consecutivitatea de simboluri introduse. De exemplu. } r->n=v. // r – fixează pointerul la elementul curent //care conţine valoarea în char *is. v=p->n. p. r->val=in. } 16. p=dl. if(* is=='q') break. s-a introdus consecutivitatea ABcEr-1. p=p->n. dl=new(NOD).while(p!=NULL) { cout<< p->val<<endl. // primul element dl->n=r=new(NOD). // dl . Cercetaţi acţiunea operaţiilor efectuate asupra stivei sînt efectuate în programul de mai jos: 56 . r->val=10000. in=atof(is). float in=1. *v. consecutivitatea inversată va fi 1-rEcBA. Utilizaţi noţiunea de listă simplu lănţuită. // ultimul element while(1) { cin>> is. r=new(NOD). while(v->valn). } return(dl). r->n=NULL.

apoi numerele pozitive. Analizaţi ce efectuează programul de mai jos: typedef struct nod { float val. } while(a!='. } NOD. struct nod *n. care se termină cu zero. p=NULL.'). apoi numerele pare. Scrieţi un program în care numerele din baza 10 din lista dublu lănţuită dată se înlocuiesc cu cele din baza 2. } 18. p=q. } while(p->ps!=NULL). do // completarea stivei { a=getch(). 57 . q->ch=a. 19. } STACK.typedef struct st // declararea tipului STACK { char ch. Scrieţi un program care din trei cozi se selectează o coadă nouă mai întîi numerele negative. 20. Se creează o listă dintr-un şir de numere întregi. struct st *ps. q=p. Din listă se şterg mai întîi elementele negative. char a.*q. Scrieţi un program care ar efectua următoarele operaţii. zerourile. main() { STACK *p. q->ps=p. q=new(STR1). 21. cout<< p->ch. do // tiparul stivei { p=q->ps. delete (q).

i++) x[i]=NULL. Cum se utilizează funcţiile standard de citire a valorilor variabilelor de la tastatură şi din fişier? 24. int p=25. 58 64 . i=inp%100+1. Care dintre exemplele de declaraţie şi/sau iniţializare sînt corecte: int r. 22. 23. int 25. float inp. p=new(NOD). cin>> inp. Care sînt caracterele de control şi specificare a tiparului variabilelor la ecran? Temele pentru acasă: 1. } return j. p->n=x[i]. p->val=inp.j=0. Cum se utilizează funcţiile standard de tipar la ecran şi înregistrare într-un fişier a valorilor variabilelor? 25. int i. int i. x[i]=p. for (i=0. } Valoarea returnată de funcţia index va fi numărul de elemente cercetate din listă. i<100.int index (NOD *x[100]) { NOD *p. while (inp!=0) { j++. Scrieţi un program care din lista L1 ce conţine numere întregi să se extragă în lista L2 elementele cu numere impare din lista L1. cin>>inp.

12. 2. int r=j. int *p=25. Scrieţi un program care inversează cei n biţi ai săi care încep de la poziţia p. Scrieţi un program de convertire a unui şir de caractere într-un întreg. 59 . Scrieţi variante de programe cu pointeri care ar efectua funcţiile citeşte_linie(). | const int j=25. r=25. Scrieţi un program care roteşte întregul n la dreapta cu b poziţii. int i. 6. 7. reverse_linie(). r=25. 9. 8. într-un şir de caractere. Scrieţi un program care converteşte numărul întreg n în baza zecimală. Scrieţi un program pentru a număra biţii de la dreapta spre stînga pentru un număr dat. Scrieţi un program care converteşte literele mari în litere mici utilizînd o expresie condiţională. int &r=j. Scrieţi un program care converteşte un întreg într-un număr hexazecimal. lăsîndu-i pe ceilalţi neschimbaţi.int r=i. 11. 3. Care dintre exemplele de declaraţie şi/sau iniţializare sînt corecte: int &r. int &r=25. const int j=25. 4. Scrieţi o versiune de funcţie cu pointeri pentru strcat() concatenare a două şiruri de caractere. 5. 10. Scrieţi un program care converteşte întregii fără semn n. int &r=i. într-o reprezentare binară în s.

Scrieţi un program care compară două stive date. b este un număr aleator de la 1 la 16. 2. 3. 2. în final afişînd frecvenţa cu care a fost generat fiecare număr. 1. 17.13. II. Scrieţi un program de convertire a fiecărui element dintr-o stivă într-un şir de caractere. 14. Scrieţi un program care generează o mie de seturi de cinci numere aeratoare cuprinse între 1 şi 40. Scrieţi un program care roteşte un întreg n (citit dintr-un fişier) la dreapta cu b poziţii. III. 19. Scrieţi un program care calculează numărul de elemente dintr-o listă simplu lănţuită care sînt mai mici ca valoarea medie aritmetică a tuturor elementelor acestei liste. p ia valoare de la 1 la 10. citit dintr-un fişier. Scrieţi un program care inversează un şir de caractere s. Scrieţi un program pentru a număra biţii de la dreapta spre stînga pentru un număr introdus de la tastatură. 18. Scrieţi un program pentru a număra biţii de la dreapta spre stînga pentru fiecare număr citit dintr-un fişier. Scrieţi un program care converteşte numărul întreg n în baza zecimală. Temele pentru lucrări de laborator: I. 1. Scrieţi un program care în fiecare număr citit dintr-un fişier inversează cei n biţi ai săi care încep de la poziţia p. 16. într-un şir de caractere. lăsîndu-i pe ceilalţi neschimbaţi. 15. 3. Scrieţi un program care converteşte literele mari în litere mici dintr-un fişier textual. 60 . Scrieţi un program care efectuează înmulţirea cifrelor unui număr dat. Scrieţi un program care converteşte numărul întreg n în baza zecimală într-un şir de caractere.

IV. 3. Scrieţi un program cu o funcţie recursivă care calculează cel mai mare divizor comun al elementelor dintr-o consecutivitate. Scrieţi un program care converteşte un întreg într-un număr hexazecimal. postordine. Valorile numerelor aleatoare ale consecutivităţii sînt din intervalul 0 …100000. 1. Scrieţi un program care converteşte întregii fără semn dintr-o listă dublu lănţuită în reprezentare binară. V. 2. Scrieţi un program care converteşte întregii fără semn n selectaţi dintr-un fişier. 1. Scrieţi un program care inversează un şir de caractere s. Scrieţi un program de convertire a unui întreg într-un şir de caractere.1.100). Scrieţi un program care determină un număr obişnuit din inversul cifrelor numărului dat. preordine. 3. Scrieţi un program care determină numărul de ordine a numărului minimal dintr-o consecutivitate de numere aleatoare. Scrieţi un program care permite crearea unui arbore binar şi traversarea lui în inordine. Cantitatea de numere aleatoare ale consecutivităţii este aleatoare (N = 1. Să se determine consecutivitatea de elemente ce se află între numărul maximal şi cel minimal determinat. 2. Scrieţi un program care din 100 de numere aleatoare se determină numărul maximal şi cel minimal. 2. 1. într-o reprezentare binară în s. VI. 61 . Să se determine diferenţa dintre numărul maximal şi cel minimal determinat 2. Scrieţi un program care dintr-o listă circulară de 100 de numere aleatoare se determină numărul maximal şi cel minimal. Scrieţi un program care calculează suma cifrelor pentru fiecare număr din consecutivitatea de 100 de numere aleatoare. …. 3. 3.

Să se determine frecvenţa cu care a fost generat fiecare element în fişierul creat. Elementele lui v trebuie să fie în ordine crescătoare. şi –1. Scrieţi un program care inversează cei n biţi ai elementelor unei liste simplu lănţuită care încep de pe poziţia p. X. 62 . Scrieţi un program care din două fişiere ordonate descrescător se unesc în unul nou păstrîndu-i-se ordinea descrescătoare de sortare. 3. 3. Scrieţi un program care determină cîte numere din consecutivitatea de 100 de numere aleatoare sînt mai mari ca “vecinii” săi. Scrieţi un program care generează un fişier al căror valori ale elementelor sînt cuprinse între 1 şi 100. 1. 1. 2. Scrieţi un program care înlocuiesc numerele din baza 10 din consecutivitatea dată cu cele din baza 2. Scrieţi un program care atribuie unei liste simplu lănţuite elementele altei liste în ordine inversă. Scrieţi un program care din trei liste simplu lănţuite se selectează într-o listă nouă mai întîi numerele divizibile la 3. lăsîndu-i pe ceilalţi neschimbaţi. Se tipăreşte numărul elementului din listă (un număr între 0 şi n-1). IX. 5 şi 7. 1. 2.VII. 3. Scrieţi un program care decide dacă o valoare x aparţine unei liste dublu lănţuite v. 1. dacă x apare în v. Scrieţi un program care va tipări în ordine inversă subconsecutivitatea de numere dintre valoarea minimă şi maximă ale unei liste simplu lănţuită. VIII. apoi numerele pozitive pare. Scrieţi un program cu funcţie recursivă care calculează cel mai mare divizor comun dintr-un şir de numere date. dacă nu apare. 2.

1. 2. Utilizînd funcţia recursivă. 63 . 2. XII. 3. 1. Să se scrie un program care ar conţine două funcţii: una recursivă. 1. Scrieţi un program care converteşte întregii fără semn dintr-o listă simplu lănţuită n. 3. Scrieţi un program care creează o listă circulară a căror valori ale elementelor sînt cuprinse între 1 şi 100. într-o reprezentare binară. scrieţi un program care converteşte un întreg citit dintr-un fişier în numere hexazecimale. XIV. Scrieţi un program care converteşte literele mari în litere mici utilizînd din elementele unei stive. a doua .2. Scrieţi un program care determină un număr obişnuit din inversul cifrelor numărului dat. 1. 2. Să se scrie un program care din lista L1 ce conţine numere întregi să se extragă în lista L2 elementele cu numere impare din lista L1. Scrieţi un program care inversează fiecare element de tip şir de caractere dintr-o listă simplu lănţuită. Scrieţi un program care formează o listă dublu lănţuită nouă din cea dată după următoarea legitate: elementele listei noi se obţine din inversul cifrelor numărului din lista dată. Să se determine frecvenţa cu care a fost generat fiecare element al listei create. Scrieţi un program care roteşte fiecare element al listei dublu lănţuite n la dreapta cu b poziţii. XIII. Scrieţi un program care calculează suma cifrelor pentru fiecare număr din consecutivitatea de 100 de numere aleatoare.nerecursivă pentru numărarea elementelor unei liste. 3. Scrieţi un program care atribuie unui fişier elementele altui fişier în ordine inversă. XI. 3.

– cãutarea elementului dupã criteriul dat. Scrieţi un program care formează un fişier nou din cel dat după următoarea legitate: elementele fişierului nou se obţine din inversul cifrelor numărului din fişierul dat. Alcătuiţi un program care ar efectua următoarele operaţii asupra listei dublu lănţuite: – iniţializarea listei. 1. destructorilor. Scrieţi un program care determină cîte numere ale unei cozi de 100 de numere aleatoare sînt mai mari ca “vecinii” săi. cu funcţii şi clase prietene. Lucrarea de laborator nr 2 Clase (constructori. 3. Scrieţi un program care calculează suma cifrelor pentru fiecare număr din consecutivitatea de 100 de numere aleatoare. 2.Funcţii şi clase prieten Scopul lucrării: familiarizarea studenţilor cu noţiunea de clase. XV. Scrieţi un program care converteşte fiecare element al listei dublu lănţuite într-un număr hexazecimal. sortarea componentelor listei. destructori). – insertarea unui element nou înainte sau dupã o componentã indicatã a listei. 3. Consideraţiile teoretice necesare: Clase Sintaxa simplificată a declarării unei clase este următoarea: 64 .2. – eliminarea unui element din listã. utilizarea constructorilor.

.. termenul de date membre. O clasă permite incapsularea în interiorul sau a datelor şi a codului. cît şi de tip funcţie.class NumeClasă {.. lista de parametri). Într-un mod similar declaraţiei int i. de regulă. declaraţii variabile membre... iar pentru funcţii -denumirea de funcţii membre sau metode. Implementarea funcţiilor membri are loc prin implementarea clasei. trebuie sa definim o variabilă de acel tip. Pentru datele din interiorul clasei se utilizează. Declaraţia clasei prezintă membrii clasei.h> class myclass // se declară un nou tip de date myclass 65 . numele funcţiei. O clasă este compusă din două părţi: declaraţia şi implementarea ei. Pentru a putea utiliza efectiv un tip de date (în cazul de faţă o clasă).. Ea are în componenţa sa membri atît de tip variabilă. } Din definirea clasei se poate observa că clasa este asemănătoare cu o structură. declaraţii funcţii membre. De exemplu: #include<iostream. public – toate elementele sînt disponibile în exteriorul clasei. Gradul de accesibilitate la elementele componente ale clasei este indicat prin cuvintele: private sau protected – elementele clasei sînt accesate numai prin intermediul funcţiilor membri sau prietene friend. Membrii clasei sînt variabile de instanţiere şi funcţii membri indicate prin prototipul lor (tipul returnat. putem scrie: NumeClasă variabilă Vom considera că variabilă este un obiect. Exprimarea uzuală este că un obiect este instanţierea unei clase.

} // această funcţie furnizează valoarea componentei a void main () // funcţia de bază a programului { myclass ob1. nu sînt necesare.{private: int a.set_a (99).set_a (10). pentru setarea şi furnizarea valorii pentru componenta a.} // această funcţie setează valoarea componentei a int myclass::get_a(){return a. // se declară două obiecte ob1 şi ob2 //de tipul myclass ob1. }.dacă componenta a va fi inclusă în zona public. Exemplul de mai sus se va modifica în felul următor: #include<iostream.. // prin intermediul acestor funcţii se // accesează componenta a int get_a (). // pentru obiectul ob2 se setează //valoarea //componentei a egală cu 99 cout << ob1. ob2.get_a ()<<“ \n”. // pentru obiectul ob2 se //furnizează valoarea componentei a //care apoi se tipăreşte la ecran } Funcţiile set_a şi get_a. Componenta a va fi explicit accesată şi i se va iniţializa sau atribui valoare. // pentru obiectul ob1 se setează //valoarea //componentei a egală cu 10 ob2.get_a () << “ \n”. void myclass::set_a(int num) { a=num. // componenta int a se declară implicit în //zona private public: // funcţiile membri declarate mai joc sînt din //zona public void set_a (int num). // pentru obiectul ob1 se //furnizează valoarea componentei a //care apoi se tipăreşte la ecran cout << ob2.h> 66 .

a =10. // pentru obiectul ob1 se //tipăreşte valoarea componentei a cout << ob2. avînd acelaşi nume ca şi numele clasei.a << “\n”.a << “\n”. void main () { myclass ob1. De exemplu: class persoana { private: char nume[40]. // pentru obiectul ob1 se iniţializează //valoarea componentei a în mod //explicit cu valoarea 10 ob2. fie ea statică sau dinamică. cum ar fi. Ei au scopul de a atribui valori iniţiale elementelor membri.a. nu returnează rezultat şi sînt apelaţi automat la instanţierea unei clase. // componenta int a se declară explicit în //zona public // funcţiile membri declarate mai sus nu //sînt necesare }. alocarea dinamică de memorie. dar pot efectua şi unele operaţii. deschiderea unui fişier ş. // pentru obiectul ob2 se iniţializează //valoarea componentei a în mod // explicit cu valoarea 99 cout << ob1. // se declară două obiecte ob1 şi ob2 //de tipul myclass ob1. ob2.class myclass // se declară un nou tip de date myclass {public: int a.a = 99. long int telefon. // pentru obiectul ob2 se //tipăreşte componentei a } Constructorii sînt un tip special de funcţie membru. public: 67 .

la încheierea timpului de viaţă în cazul static.h> #include<string.telefon=t. 743567). }.n). Destructorii dezactivează toate funcţiile unui obiect. sau persoana p (“Vasilina”.}. Destructorul are acelaşi nume ca şi constructorul. telefon =0. Dacă se va declara un obiect de tipul persoana fără date iniţiale. Apelul constructorului se efectuează în momentul declarării unui obiect. //aceste funcţii setează valori pentru elementele membri nume persoana(long int telefon) {return telefon. fie persoana p = persoana (“Vasilina”. return p.h> 68 . constructorul va iniţializa elementele membri nume şi telefon ale clase persoana respectiv cu valorile “Vasilina” şi 743567. strcpy(p. cînd constructorii efectuează alocări dinamice de memorie. constructorul va completa elementele membri nume cu stringul vid ‘\0’ şi telefon cu valoarea 0. fiind precedat de semnul “~”.nume. //constructorul iniţializează valori nule elementelor membri persoana(char*p. }. îl distruge şi sînt apelaţi automat la eliminarea unui obiect. long int t) {strcpy(nume.}. p. 743567). Dacă declarăm o variabilă de tipul persoana. destructorii sînt utilizaţi în cazul.persoana() {nume=’\0’. long int t=0) {persoana p. De exemplu: #include<iostream. sau la apelul unui delete în cazul dinamic.p). //şi telefon persoana persoana_ input (char *n. De regulă. }. telefon=t.} //constructor iniţializează valori concrete pentru elementele //membri ale clasei persoana(char* nume) {return nume.

} *p=’\0’. atunci se va proceda la executarea destructorilor obiectelor incluse. în care aceştea apar în declaraţia clasei. int len. } // destructorul void set (char*ptr) { if (strlen(ptr)> Size ) cout<<”Stringul conţine mai mult de 255 de caractere \n”. delete p.s2.show(). if (!p){cout << “Eroare la alocarea memoriei \n”.ptr).} }. deci în faza finală a creării 69 . } Destructorii obiectelor membri sînt apelaţi. s2. s1. Dacă obiectul membru este compus din alte obiecte. Destructorii obiectelor membri sînt apelaţi în ordine inversă. exit(1). constructorul este apelat după alocarea memoriei necesare. public: strtype() // constructorul { p=new char.set(“Program C++”). Din punct de vedere cronologic.set (“Test”).#include<stdlib. void show() { cout << p << “. s1.show(). len=strlen(p).}. strcpy(p. după ce destructorul obiectului principal a fost executat. s2.h> #define Size 255 class strtype { private: char *p. ~strtype() {cout << “Eliberarea memoriei\n”.lungimea “<< len << “\n”. }. void main() { strtype s1. len=0.

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 către constructori sau destructori – constructorii pot avea parametri. pentru apelarea unei funcţii membre.obiectului. Am mai declarat o funcţie care calculează aria dreptunghiului avînd punctele (0.FuncţieMembră(). y. destructorii insa nu. în care o clasa nu dispune de constructori sau destructori. Pentru exemplificare să consideram o implementare a noţiunii de punct.}. y). 0) şi (x. deci în faza iniţială a distrugerii sale. Un constructor fără parametri poartă denumirea de constructor implicit. implicit. dar prezintă o serie de caracteristici specifice: – numele lor coincide cu numele clasei căreia ii aparţin. Membrii unei clase Accesarea membrilor unei clase se face în felul următor: obiect. pentru accesul la o variabilă membra. respectiv destructor. Ca variabile membre avem nevoie doar de coordonatele x şi y care definesc poziţia în spaţiu a unui punct. şi obiect. unsigned long Arie() {return x * y. iar destructorul înaintea eliberării memoriei aferente. unsigned GetX().VariabiăMembră = valoare. 70 . class Point {unsigned x. compilatorul de C++ generează automat un constructor. Constructorii şi destructorii se declară şi se definesc similar cu celelalte funcţii membre.

El permite accesul la un identificator dintr-un bloc în care acesta nu este vizibil datorită unei alte declaraţii locale. deşi este definită în afara declaraţiei. ::sir). Cuvîntul cheie this Toate funcţiile membre ale unei clase primesc un parametru ascuns. } Am folosit un operator nou. // afişează variabila globală printf("%s\n". printf("%s\n". } void Point::SetY(unsigned Y) { y = Y. void SetX(unsigned X). unsigned Point::GetX() {return x. void SetY(unsigned Y). }. numit şi operator de acces sau de domeniu.} unsigned Point::GetY(){return y. } void Point::SetX(unsigned X){ x = X. indicînd faptul că funcţia are acelaşi domeniu cu declaraţia clasei respective şi este membră a ei. specific C++. Acesta poate fi utilizat în cadrul funcţiilor membre. pointer-ul this. ::. Un exemplu de folosire este următorul: char *sir = "variabilă globală". void funcţie() { char *sir = "variabilă locală". // afişează variabila locală } Pentru definiţiile funcţiilor membre aflate în afara declaraţiei clasei este necesară specificarea numelui clasei urmat de acest operator. care reprezintă adresa obiectului în cauza. numit operator de rezoluţie. sir).unsigned GetY(). } Crearea şi distrugerea obiectelor 71 . De exemplu: unsigned long Point::Arie() {return this->x * this->y.

a treia alocă un tablou de dimensiune n. Deşi aceşti doi operatori oferă metode flexibile de gestionare a obiectelor. p->y = 10. p = new Point. sau un pointer cu valoarea NULL (practic 0) cînd alocarea nu a reuşit.Să considerăm următorul program C++: void main() {Point p. va fi alocat automat spaţiul de memorie necesar. p->Aria()). există situaţii în care aceasta nu rezolva toate problemele. Sintaxa acestuia este: delete variabilă. variabila = new tip[n]. void main() { Point *p. acesta fiind eliberat la terminarea programului. } Operatorul new este folosit pentru alocarea memoriei. } În momentul definirii variabilei p. variabila = new tip(valoare_iniţială). De aceea pentru crearea şi distrugerea obiectelor în C++ se folosesc 72 . Acest operator furnizează ca rezultat un pointer conţinînd adresa zonei de memorie alocate. variabila p este de tip static. a doua variantă ii alocă spaţiu şi o iniţializează cu valoarea specificată. p->x = 5. în caz de succes. Eliminarea unei variabile dinamice şi eliberarea zonei de memorie aferente se realizează cu ajutorul operatorului delete. printf("Aria = %d\n". În continuare vom modifica acest program pentru a folosi o variabilă dinamică (pointer). Prima variantă alocă spaţiu pentru variabilă dar nu o iniţializează. În exemplul de mai sus. delete p. iar sintaxa acestuia este: variabila = new tip.

despre care s-a menţionat mai sus. Funcţii şi Clase friend Conceptul friend permite abateri controlate de la ideea proiecţiei datelor prin incapsulare. care au rolul de metode de prelucrare a datelor incapsulate în interiorul clasei. indiferent de natura acesteia. O întrebare care poate apare este motivul pentru care am realizat funcţiile GetX(). unsigned Y) { x = X. De notat este faptul ca definiţii de forma Point p. Funcţiile prietene sînt funcţii care nu sînt metode ale unei clase. GetY(). //sau Point *p = new Point(). Deoarece una din regulile programării C++ este de a proteja variabilele membru. Sa completam în continuare clasa Point cu un constructor şi un destructor: Point::Point() // constructor implicit { x = 0.nişte funcţii membre speciale. } Point::~Point() { } Aţi remarcat cu aceasta ocazie modul de marcare a comentariilor în C++: tot ce se afla după caracterul // este considerat comentariu. Sintaxa declarării unei funcţii prietene în cadrul declaraţiei unei clase este următoarea: friend NumeFuncţie 73 . } Point::Point(unsigned X. duc la apelarea constructorului implicit. y = Y. cînd puteam utiliza direct variabilele membru x şi y. SetY(). y = 0. numite constructori şi destructori. dar care au totuşi acces la membrii privaţi ai acesteia. SetX(). acestea pot fi accesate numai prin intermediul unor funcţii. Mecanismul de friend (sau prietenie) a apărut datorita imposibilităţii ca o metoda sa fie membru a mai multor clase. Orice funcţie poate fi prietenă a unei clase.

class B { friend void A::A1(B &x). unsigned Y) { . dar. prietene clasei. }. } Funcţii membri ca prietene Orice funcţie membru nu poate fi prietenă aceleiaşi clase. în care un obiect de tipul primei clase este declarat friend... de fapt. Prima variantă este specificarea funcţiei membru a unei clase. A doua variantă este declararea unei clase prietenă. class B.. Astfel. să fie prietena altei clase. void Al (B &x) . ca fiind prietenă altei clase. class B. class A {…. …. Există două moduri de a face. ca o funcţie membru a unei clase.. ca fiind de tip friend. astfel. unsigned Y) {return X * Y / 2. că toate funcţiile sale membri. vom declara funcţia membru în prima clasă.. . unsigned Y). Deci. ). în cea de-a doua clasă. posibil. sînt.. } unsigned long AltăClasă::Calcul(unsigned X. funcţiile friend constituie o punte de legătură între clase. class A 74 . să fie prietena altei clase.De exemplu: class Point { friend unsigned long Calcul(unsigned X. public: friend unsigned long AltăClasă::Calcul(unsigned X. unsigned Y). ). unsigned long Calcul(unsigned X.

aflîndu-se pe acelaşi nivel ierarhic.. Vom utiliza două clase. }.h> class stiva. De asemenea. care va fi prietenă sau va conţine funcţii. Sintaxa declarării unei clase prietene este: friend class NumeClasăPrietenă De exemplu: class PrimaClasă { . class ADouaClasă { .. se impune predeclararea clasei. proprietatea de prietenie nu se moşteneşte în clasele derivate. class B {… friend A. una ataşată nodurilor din listă şi una – stivei propriu-zise. care să comunice între ele deseori. în care avem nevoie de clase. Clasele prietene sînt clase care au acces la membrii privaţi ai unei clase. friend class PrimaClasă.. şi clasa B este prietena a unei clase C. ). 75 . presupunem. Pentru exemplificare. Daca o clasa A este prietena a clasei B.. #include<conio. aceasta nu înseamnă ca A este prietena a clasei C. că vom implementa o stivă de caractere ca o listă simplu înlănţuită. Indiferent de metodă.{ void Al (B &x) .h> #include<stdio. Clase prietene sînt utile în situaţia. Relaţia de prietenie nu este tranzitivă. acestea. Clasa PrimaClasă are acces la membrii privaţi ai clasei ADouaClasă. }. …}. care sînt prietene unei alte clase.

class nod { private: 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 în *”); while ((c=getch ())!='*') 76

{

cs.push (c);} putch(c); while ((c=cs.pop ())!=-1) { putch (c); } c='\n'; putch(c); }

} } Prototipul unei funcţii friend cu o clasă se află, de regulă, în cadrul clasei respective. Funcţia friend nu este membru a acestei clase. Indiferent de poziţia declaraţiei unei asemenea funcţii, în cadrul declaraţiei clasei funcţia 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 sînt egal depărtate. 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 funcţie friend unei clase poate fi transformată într-o funcţie membru a acelei clase, renunţîndu-se, însă la gradul de “prietenie”. Exemplul de mai sus se modifică în felul următor: class punct { private: 77

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"); } Întrebări pentru verificarea cunoştinţelor: 1. Ce se va afişa 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 unei clase? Cum sînt activaţi constructorii şi destructorii? Ce se numesc clase prietene? 3. Ce se numesc funcţii prietene? Temele pentru acasă: 78

8

}: ~o_clasa() { printf("Apel destructor \n"). class alta_clasa2 ( public: alta_clasa *ac. numere::numere(int x. int y. }. 2.1. Introduceţi şi lansaţi în execuţie următorul program.h> class 0_clasa {public: o_clasa () { printf("Apel constructor\n").c. numere(int x. Ce rezultat furnizează? #include <stdio. -numere(). <stdio. 3). } }. int z) : c(z). } void main () {numere bon(1. } numere::~numere {printf("A fost apelat un destructor\n"). 79 .h> class numar {public: int n. }.b. class numere { public: numar a. }.n). b(y) { printf("A fost apelat un constructor \n"). }. int y. a(x). numar(int i) { printf( "num() %d \n". Introduceţi şi lansaţi în execuţie următorul program.n). int z). -numar() { printf("-num() %d \n".) 2. Ce rezultat se afişează? #include.

O). 80 . echivalent cu următorul program C++. } }. } 4.0).memorie) . c. num(int i) { data=i. printf ("Memoria este %f\n" . } void main () {calculator c.}. c. calculator () .aduna(30.c. return memorie. ) int set (int i) { return data=i. } float calculator::aduna (float f) (memorie+=f. float aduna (float f). } calculator::~calculator(){printf("S-a oprit calculatorul\n"). Creaţi funcţii în limbajul C echivalente constructorului şi destructorului şi verificaţi apelul lor la momentul oportun. #include <stdio.0. Scrieţi un program în C.aduna(lO.h> class calculator { public: float memorie. memorie=0 . c. Se va compila corect următoarea clasă? class num { public: int data. }.aduna(2.0). void main() { alta_clasa acc. ~calculator (). calculator::calculator () { printf("S-a pornit calculatorul\n") .} 3.

Scrieţi un program care determină pentru o clasă listă simplu lănţuită cu următoarele funcţii: 81 . Să se scrie un program în care se defineşte o clasă stivă elementele căreia sînt de un tip abstract de date cu următoarele funcţii: – empty(Q) care curăţă stiva Q. int lungime. concatenare şi eliminare a unui substring) pentru clasa: class sir { private: char continut[80]. – error (k) care indică eroarea cu numărul k (k=1 dacă stiva este supraîncărcată. k=2 dacă stiva este vidă).5.} }. Temele pentru lucrări de laborator: 1.x) care adaugă elementul x în stiva Q. includere a unui substring. – in_query(Q.x) care scoate elementul x din stiva Q. Să se scrie un program care ar evalua o funcţie. Descrieţi funcţiile de modificare a stringului (copiere. public: sir (). 4. – out_query(Q. – if_is_empty(Q) care verifică dacă stiva Q este vidă. a cărei expresie analitică se introduce de la terminalul calculatorului ca un şir de caractere. Să se scrie un program care să definească un nou tip de variabile tipul complex şi să construiască funcţii adecvate pentru operaţiile de bază cu acest tip de date (adunare. scădere. înmulţire. 2. calculul modulului numărului complex). 3. char *contine() (return continut.

error (k) care indică eroarea cu numărul k (k=1 dacă coada este supraîncărcată.x) care scoate elementul x din stiva S. k=2 dacă coada este vidă). – – – – – 6.x) care adaugă elementul x în stiva S.următoarele operaţii: 82 .– – – – 5. error (k) care indică eroarea cu numărul k (k=1 dacă stiva este supraîncărcată.fin) care citeşte elementele din fişierul fin în lista L. Scrieţi un program care la o singură trecere prin fişierul fin fără utilizare a fişierelor suplimentare va tipări elementele fişierului fin în următoarea ordine: toate elementele mai mici ca valoarea a.b]. if_is_empty(S) care verifică dacă stiva S este vidă. Scrieţi un program care determină o clasă stivă în care sînt determinate următoarele funcţii: empty(S) care curăţă stiva S. member(x.L) care determină apartenenţa elementului x listei L. print(L) care tipăreşte toate elementele listei L. if_is_empty(Q) care verifică dacă coada Q este vidă. k=2 dacă stiva este vidă). (a<b. equal(L1. out_query(Q. Scrieţi un program care determină o clasă coadă în care sînt determinate următoarele funcţii: empty(Q) care curăţă coada Q.L2) care determină echivalenţa a două liste L1 şi L2. in_query(Q. – – – – – 8. elementele din segmentul [a. pop(S. readlist(L. restul elementelor păstrînd aceeaşi ordine din fişierul fin.x) care scoate elementul x din coada Q. push(S. Scrieţi un program care efectuează asupra elementelor fin fişierului de tip text .x) care adaugă elementul x în coada Q. a şi b sînt cunoscute şi sînt de acelaşi tip ca şi elementele fişierului fin). – – – 7.

y) care determină suma elementelor din lista L în intervalul elementelor cu valorile x şi y. 9.x) care determină numărul de repetări ale elementului x în arborele A. 83 . – dacă elementele arborelui A sînt de tip întreg sau real valorile maximale şi minimale se schimb cu locurile. media (A) =0. valorile negative ale elementelor se înlocuiesc cu valorile lor absolute. L2) care determină echivalenţa a două liste L1 şi L2. cît şi cuvintele inversate. – substract(L) care determină diferenţa elementelor din lista L. x. – sortează lexicografic atît cuvintele din fişierul fin. – sum(L) care determină suma elementelor din lista L. 10.x. Scrieţi un program care determină clasa arbore. y) care determină elementele cu valoare minimală x şi cu valoare maximală y din lista L. – determină cuvintele “simetrice” din fişierul fin. Scrieţi un program care determină pentru o clasă listă dublu lănţuită următoarele funcţii: – member(x. – multiply(L) care determină produsul elementelor din lista L.x. – min_max(L. – dacă elementele arborelui A sînt de tip întreg sau real.z)=0. – media(A) care calculează media aritmetică a elementelor de tip întreg sau real. elementele de lungime minimală se schimb cu locurile cu elementele de lungime maximală. – dacă elementele arborelui A sînt de tip string. Dacă elementele sînt de alt tip. – print(L) care tipăreşte toate elementele listei L.– tipăreşte inversat conţinutul fiecărui cuvînt al liniei fişierului fin. – sum(L. – equal(L1. Asupra elementelor arborelui să se determine următoarele operaţii: – repeat (A. în caz contrar sum(L. L) care determină apartenenţa elementului x listei L.

– scăderea. – împărţirea la un coeficient. – înmulţirea numerelor fracţionare. – înmulţirea cu un coeficient. tone. – înmulţirea cu un coeficient. bani): – adunarea. centimetri. – scăderea. 12.– readlist(L. Scrieţi un program care efectuează următoarelor operaţii asupra unităţilor băneşti (de exemplu. – valoarea numerică a unităţilor de lungime să se tipărească cu cuvinte. – împărţirea la un coeficient. – scăderea numerelor fracţionare. – adunarea numerelor fracţionare. – împărţirea la un coeficient. – scăderea. 84 . kilograme. Scrieţi un program care efectuează următoarele operaţii asupra numerelor fracţionare: – transformarea unui număr fracţionar compus într-o fracţie supraunitară. – valoarea numerică a unităţilor băneşti să se tipărească cu cuvinte. grame): – adunarea.fin) care creează lista L din elementele fişierului fin. Scrieţi un program care efectuează următoarele operaţii asupra unităţilor de lungime (de exemplu. – înmulţirea cu un coeficient. – împărţirea numerelor fracţionare. metri. lei. Scrieţi un program care efectuează următoarele operaţii asupra unităţilor de greutate (de exemplu. 13. 11. 14. milimetri): – adunarea.

caracterizată prin trecerea atributelor de la o clasă. operatori supraîncărcaţi. Noţiunea de derivare este o abstractizare a noţiunii de moştenire. minuta. ea 85 . O clasă care adaugă proprietăţi noi la o clasă deja existentă vom spune ca este derivata clasei de bază. 15. derivată. Totodată se observă o ierarhizare datorită faptului că există posibilitatea ca o clasă derivată să aibă mai multe clase de bază ordinea importanţei nivelelor rămînînd aceeaşi. funcţii virtuale şi redefinite. funcţii virtuale. Clasa derivată moşteneşte toate datele şi funcţiile membre ale clasei de bază. de bază. luna. 3 Clase derivate. Moştenirea este o relaţie între clase. cît şi funcţional. Scrieţi un program care efectuează următoarele operaţii asupra unităţilor de timp (de exemplu. la alta. supraîncărcarea funcţiilor şi operatorilor Scopul lucrării: familiarizarea studenţilor cu noţiunile de clase derivate. anul. obiecte Consideraţiile teoretice necesare: Clase derivate. Ele pot fi îmbogăţite atît structural. – înmulţirea cu un coeficient. – împărţirea la un coeficient. secunde): – adunarea. Lucrarea de laborator nr. Clasele derivate posedă toate caracteristicile clasei de bază. – scăderea.– valoarea numerică a unităţilor de greutate să se tipărească cu cuvinte. ceasul. ziua. – valoarea numerică a unităţilor de timp să se tipărească cu cuvinte.

nu trebuie recompilată. Astfel.poate adăuga noi date la cele existente şi poate suprascrie sau adăuga funcţii membre. ceea ce permite reutilizarea şi adaptarea uşoară a codului deja existent. Construirea ierarhiei de clase reprezintă activitatea fundamentală de realizare a unei aplicaţii orientate obiect. Se poate astfel realiza o ierarhie de clase. Clasa de bază nu este afectată în nici un fel în urma acestui proces de derivare şi. În funcţie de necesităţi. care va "şti" să deseneze punctul pe ecran: class GraphicPoint : public Point {unsigned color. chiar dacă fişierul sursă nu este disponibil. unsigned Color). GraphicPoint. ~GraphicPoint(). fiecare nivel al ierarhiei acumulează caracteristicile claselor "părinte" şi le adaugă un anumit grad de specializare. unsigned Y. void Draw(). }. O clasă poate să moştenească simultan proprietăţile mai multor clase. În acest sens. procedură numită moştenire multiplă. void SetX(unsigned X). Declaraţia şi codul obiect sînt suficiente pentru crearea clasei derivate. 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. derivarea claselor va fi un proces cu durată variabilă. ca urmare. care să modeleze adecvat sisteme complexe. se preferă conceperea unor clase de bază simple. în locul unora dezvoltate. nu este necesar ca programatorul unei clase derivate să cunoască modul de implementare a funcţiilor membre din componenta clasei de bază. GraphicPoint(unsigned X. Sintaxa simplificată a derivării este: class NumeClasăDerivată : NumeClasaDeBază În continuare vom deriva din clasa Point o clasă specializată. Pornind de la clase simple şi generale. void SetY(unsigned Y). 86 .

apelînd în ambele funcţiile originale. unsigned Color) : Point(X. unsigned Y. } În exemplul de mai sus s-a adăugat o variabilă nouă faţă de clasa Point. Regulile de funcţionare ale constructorilor şi destructorilor. Draw(). În constructorul derivat s-a apelat constructorul original folosind construcţia: ClasaDerivată::ClasaDerivată() : ClasaDeBază() În clasa GraphicPoint s-a adăugat o funcţie membră nouă. Draw(). s-a suprascris constructorul şi destructorul clasei părinte. De asemenea. color. // funcţia SetX() este membru a clasei de bază Draw(). Am suprascris funcţiile SetX() şi SetY(). Y) {color = Color. pentru a putea memora culoarea cu care se face desenarea punctului. cu două observaţii privind ordinea de apelare a acestora: 87 . Draw().. care desenează punctul pe ecran. // apelarea primitivelor grafice pentru desenarea punctului } GraphicPoint::SetX(unsignedX) {Point::SetX(). descrise în lucrarea de laborator 2. rămîn valabile şi în cazul claselor derivate. utilizînd sintaxa: ClasaDeBază::FuncţieMembră() apelînd apoi funcţie de desenare.GraphicPoint::GraphicPoint(unsigned X.. } GraphicPoint::~GraphicPoint() {} GraphicPoint::Draw() { // . } GraphicPoint::SetY(unsigned Y) {Point::SetY().

Elementele 88 . membrul poate fi accesat de orice funcţie din domeniul declaraţiei clasei. este: class NumeClasăDerivată : SpecificatorAcces NumeClasaDeBază unde SpecificatorAcces poate fi public sau private. apoi se apelează propriul constructor. – la distrugerea unui obiect al unei clase derivate.– la instanţierea clasei derivate. se apelează mai întîi constructorul clasei de bază. 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. acesta trebuie declarat protected sau public. Accesul Atributul din Modificator Accesul din moştenit de clasa de bază de acces exterior clasa derivată private private inaccesibil inaccesibil protected private private inaccesibil public private private inaccesibil private public inaccesibil inaccesibil protected public protected inaccesibil public public public accesibil Pentru a oferi clasei derivate acces la un membru al clasei de bază. Aşa dar. – private. incluzînd controlul accesului. similar cu private. este apelat mai întîi propriul destructor. – protected. sintaxa declaraţiei unei clase derivate. şi apoi destructorul clasei de bază (în ordine inversa creării obiectului). O funcţie membră a unei clase are acces la toţi membrii clasei. membrul este accesibil numai funcţiilor membre şi prietene ale clasei. însă accesul se extinde şi la funcţiile membre şi prietene ale claselor derivate. indiferent de specificatorul de acces.

y. ~Point(). void SetX(unsigned X). unsigned long Arie(). Stabilirea atributelor de acces ale membrilor unei clase. 89 . }. De asemenea. ele pot fi accesate doar prin unele funcţii declarate special (care returnează valorile elementelor din clasa de baza descrise cu specificatorul private sau protected). unsigned GetX(). class GraphicPoint : public Point {unsigned color. Pentru respectarea principiului incapsulării datelor. datele membre pentru care se oferă acces claselor derivate se declară în clasa de bază cu atributul protected. unsigned Y). Accesul poate fi stopat pe orice nivel al ierarhiei de clase printr-o derivare private. completat cu specificatori de acces: class Point { protected: unsigned x. se utilizează derivarea public. Să cercetăm exemplul următor. public: Point(). Point(unsigned X. precum şi ale derivărilor. dezvoltarea ierarhiei de clase. trebuie să se facă astfel ca să nu afecteze incapsularea datelor. pentru a conserva dreptul de acces în urma derivării. unsigned GetY(). void SetY(unsigned Y).declarate cu specificatorul public în clasa de bază sînt accesibile elementelor în clasa derivată. Elementele declarate cu specificatorul private sau protected în clasa de bază nu sînt accesibile în mod direct elementelor din clasa derivată.

} Deci din exteriorul unei clase nu pot fi accesate datele membre private sau protected. spunem că aceste clase sînt polimorfe. Atunci cînd funcţiile unei clase de bază sînt rescrise într-una derivată. delete p. p->x = 5. întrucît nu sînt utilizaţi decît în interiorul clasei Point. p->Aria()). }. p = new Point. void SetX(unsigned X). Funcţiile din GraphicPoint nu accesează aceşti doi membri direct. Implicit. membrii sînt consideraţi private. // operaţie imposibilă: x este membru privat p->y = 8.public: GraphicPoint(unsigned X. dacă nu este utilizat nici un specificator de acces. unsigned Color). Variabilele membru x şi y sînt declarate protected. // corect: acces la variabila x prin intermediul //funcţiei SetY() printf("Aria = %d\n". // corect: acces la variabila x prin intermediul //funcţiei SetX() p->SetY(8). aşa încît vor fi vizibile şi vor avea acelaşi atribut în clasa GraphicPoint (deşi nu sînt utilizate). ci prin intermediul metodelor publice de accesare a lor oferite de clasa Point. ~GraphicPoint(). În mod normal. void SetY(unsigned Y). x şi y ar trebui sa fie declaraţi private. unsigned Y. void Draw(). void main() {Point *p. // operaţie imposibilă: y este membru privat p->SetX(5). vom avea o singură 90 .

void main() { Punct p. } float Aria(){ return pi*raza*raza. printf("Aria unui punct este: %5. } Clasa Cerc este derivată de la clasa Punct. // funcţie redefinită }. }. Ea se referă la redefinirea unor funcţii a clasei de bază în clasa derivată. Să vedem cum arată obiectele polimorfe.Aria(). }. determinăm clasele de bază Punct şi cea derivată Cerc şi funcţia membru Aria(). Cerc c.a).a).float yi) {x=xi.0. o singură interfaţă cu metode multiple. Funcţiile din clasa părinte sînt în continuare accesibile în clasa derivată.Aria(). class Cerc : public Punct {float raza. Cerc şi Punct sînt 91 . virtual float Aria() { return 0.h> class Punct { float x. ce va avea ca efect determinarea ariei obiectului respectiv: #include <stdio.14159. Pentru aceasta.}.65637). c.y. public: void Incarc_punct(float xi.2f\n". adică.y=yi.2f\n". public: void Incarc_raza (float r) { raza=r. printf("Aria cercului este: %5. a=c. float a=p. const float pi=3. } . Această noţiune legată de derivare este cea de supraîncărcare sau suprascriere a funcţiilor membre.Incarc_raza(3.denumire şi mai multe acţiuni.

deci o nouă implementare a acestora. numele clasei obiectului şi o identifică. Cum? O posibilitate ar fi de a utiliza pointeri la funcţii. Pentru a obţine obiecte polimorfe. în cazul. efectuînd. În instrucţiunea.obiecte din aceeaşi categorie. – utilizînd funcţiile virtuale. După 92 . astfel. că acest pointer va fi moştenit de către Cerc. că funcţia membru Aria() a clasei Punct este moştenită de clasa Cerc. Este evident. Cerc c. în care este apelată funcţia Aria(). Identificarea dinamică se va realiza în momentul execuţiei. existînd. ce va indica spre funcţia Aria(). Ataşarea codului unei funcţii la numele său poate fi efectuată static sau dinamic. în care aceasta este membru. spre care a indicat acel pointer. float aria=p->Aria (). va trebui să construim o ierarhie de clase şi apoi să redefinim funcţiile. deci două metode de identificare: statică şi dinamică. p indică spre obiectul c de tipul Cerc. Reţinem. Compilatorul preia numele funcţiei. argumentele sale şi. este apelată funcţia Cerc::Aria(). funcţia nefiind identificată. Vom defini un pointer în interiorul clasei Punct. dar nu convine din punctul de vedere al valorii returnate. Punct *p. că doar în momentul rulării vom şti funcţia. sînt polimorfe. Această operaţie poate fi realizată în două moduri: – rescriind funcţiile respective. decît la această etapă. Să vedem cum are loc rescrierea funcţiilor. Identificarea statică are loc la un apel normal de funcţie. Acest proces poartă denumirea de identificare a funcţiilor. p=&c. s-a impus reimplementarea acesteia. Astfel. aparţinînd clasei de bază în clasele derivate.

legăturile cu obiectele fiind realizate dinamic (în timpul execuţiei). în timp ce funcţiile virtuale fac parte din cea de– a doua categorie. 93 . vom iniţializa pointerul spre funcţia corectă. că Punct::Aria() nu va modifica rezultatul furnizat. încît această redefinire de funcţii să funcţioneze în ambele situaţii. în timp ce funcţiile virtuale trebuie să posede aceeaşi listă de parametri. – funcţiile membri redefinite pot avea liste diferite de parametri. const float pi=3. float Aria() . aportul său la aceasta fiind nul. Funcţiile virtuale sînt utilizate pentru reimplementarea unor funcţii membri ale unor clase de bază. urmînd procedura statică (la compilare). Evident. public: void Incarca_ inaltime(int h) {inaltime =h. moştenită de la clasa de bază Punct. float Cilindru::Aria() {return 2*pi*raza*inaltime +2 *Cerc::Aria(). Totuşi. } În implementarea noii versiuni a funcţiei membri Cilindru::Aria() se utilizează versiunea anterioară. class Cilindru : public Cerc { float inaltime. astfel. ţinînd cont de nivelurile de protecţie ( private. dar se sugerează. transformînd o funcţie virtuală în funcţie redefinită datorită faptului. există cîteva diferenţe dintre aceste două categorii de funcţii: – funcţiile membri redefinite sînt ataşate obiectului. că cele două categorii se aseamănă. că este permisă apelarea funcţiilor moştenite pentru oricare nivel al ierarhiei.crearea unui obiect de tip Punct sau Cerc. O a doua posibilitate este aceea de a utiliza funcţiile virtuale. În procesul de lucru cu clase derivate putem foarte uşor greşi. protected şi public).}.14159. }.

Există trei elemente. eventual. argumente. }. pe care le are orice funcţie. Versiunea clasei scade acceptă un argument unsigned char. ea este redefinită. în cadrul ierarhiei de clase.} În acest exemplu programul va afişa răspunsul 42. într-adevăr. int k=p->executa(43). void main() {scade *p=new aduna. pentru a ne asigura. ne referim la versiunea scade. }. class aduna : public scade { public: int executa ( char c) { return ++c. # include <stdio. este operator unar sau binar. Chiar dacă prin intermediul lui p acţionăm asupra unui obiect aduna. a doua versiune a funcţiei executa() nu este virtuală. funcţiile nu se comportă exact cum vrem noi.h> class scade {public: virtual int executa (unsigned char c) { return --c.k). 94 . este postfixat sau prefixat ca poziţie şi este funcţie membru sau nu –domeniu de acţiune. un tip returnat. printf(“k = %d \n”. iar versiunea clasei aduna – un argument de tip char. apartenenţa la o clasă.Dacă. }. va trebui să verificăm toate funcţiile virtuale. care trebuie stabilite la declararea operatorului. O funcţie operator are aceleaşi componente. include un nume. Deci programul va afişa valoarea 42. sînt virtuale şi nu redefinite. Datorită acestei schimb de tip. şi anume. apelînd funcţia executa(). corp şi. că. }.

întocmai ca o funcţie membră (vezi Tabela 1. etc. operatorul sizeof.) Există două variante de definire a operatorilor: – ca funcţie membră a clasei. Tipul Simbolul Asociativitate Observaţii operatorului operatorului Se definesc ca Binar () [] -> -> funcţii membre Unar + . exceptînd: . Sintaxa supraîncărcării unui operator este: operator Simbol unde Simbol este simbolul oricărui operator C++.şi postfixare Poate fi supraUnar new.Funcţiile operator membri vor avea cu un argument mai puţin decît cele non-membri. sau p. Tabela 1. operator+=(5). – ca funcţie prietenă a clasei.operatorul condiţional. Această definire se face în cadrul clasei. () ?:.& | -> 95 . *adresare la componenta prin pointer. argumentul ascuns..operatorul de rezoluţie. Redefinirea operatorilor Limbajul C++ permite programatorilor să definească operatori pentru a lucra cu propriile clase. ::. delete <definit şi pentru o clasă Binar -> * / % + .~ * & (tip) <Nu se poate disUnar ++ -<tinge între pre. Apelul unui astfel de operator membru va fi p+=5.

&& || << >> < <= > Binar -> >= == != = += -= *= /= Se definesc ca Binar %= &= ^= |= <funcţii membre <<= >>= 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); 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) 96

{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 funcţii membre. 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 funcţii membre a unei clase prezintă o restricţie majoră: primul operand este obligatoriu să fie de tipul clasa respectiv. În limbajul C++ supradefinirea operatorilor este supusă unui set de restricţii: – nu este permis introducerea de noi simboluri de operatori; – patru operatori nu pot fi redefiniţi (vezi mai sus); – caracteristicile operatorilor nu pot fi schimbate: pluralitatea (nu se poate supradefini un operator unar ca operator binar sau invers), precedenţa şi asociativitatea, prioritatea lor; – funcţia operator trebuie sa aibă cel puţin un parametru de tipul clasa căruia îi este asociat operatorul supradefinit. 97

Programatorul are libertatea de a alege natura operaţiei realizate de un operator, însă este recomandat ca noua operaţie să fie apropiată de semnificaţia iniţială. Redefinirea operatorului +=. Acţiunea acestuia este aceea de a adăuga o valoare unui număr 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); }; 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);} Pentru aceasta, vom defini o funcţie avînd numele operator 98

void persoana::operator+=(int v) {Seteaza_Varsta(varsta+v).} Redefinirea operatorilor new şi delete poate fi efectuată pentru a realiza operaţii specializate de alocare/eliberare dinamică a memoriei. Aceşti parametri sînt cei doi operanzi ai operatorului +=.y. int v) { p. }. funcţia operator poate fi membru a clasei asupra căreia acţionează class persoana { public: … void operator+=(int v). sau apelîndu-l ca funcţie operator+=(p. Bineînţeles. 9) . În caz de o atribuire specifică a clasei. 0 . atribuirea se face membru cu membru în mod similar cu iniţializarea obiectului. operatorul = poate fi supradefinit. Funcţia operator new trebuie sa primească un argument de 99 .Virsta()+v). return *this. y =p.+=() cu doi parametri. } Redefinirea operatorului =. efectuată de către compilator.} Redefinirea operatorului []. şi apelul p+=5. Point& Point::operator = (Point p) {x = p.Operatorul de indexare [] se defineşte astfel: int &operator[](int) De exemplu Point Array_Point::operator []= (Point *p. Dacă nu este supradefinită. } Utilizarea operatorului definit se realizează prin iniţializarea persoana p('Mircea" . int j) {return p[j].x. void operator +=(persoana &p. pentru operanzi de tip clasă.Seteaza_Virsta(p. Operatorul = este deja predefinit în C++.5).

În limbajul C++ este definit un set de reguli de conversie pentru tipurile de bază de date. de exemplu. De exemplu: Point Point::operator++ () {x++. size_t) Operatorii new şi delete supradefiniţi păstrează toate proprietăţile operatorilor new şi delete standard. return *this. Redefinirea operatorilor unari poate fi efectuată utilizînd o funcţie membră fără parametri sau o funcţie prietenă cu un parametru de tipul clasei respectiv. pentru o atribuire.h.dispare distincţia între utilizarea ca prefix şi cea ca postfix. calculul dimensiunii obiectului în cauză şi generarea sa se face de către compilator. x-. void operator delete(void *.şi –x. Pentru operatorii ++ şi -. se verifica mai întîi 100 . şi un al doilea parametru. de tip size_t. Funcţia nu întoarce nici un rezultat.} Conversii de tip definite de programator. y++. conţinînd adresa obiectului de distrus. opţional. respectiv. Funcţia operator delete trebuie sa primească ca prim parametru un pointer de tipul clasei în cauză sau void. – se recurge la aceste conversii numai după ce se verifică existenţa altor soluţii (de exemplu.tipul size_t care să precizeze dimensiunea în octeţi a obiectului alocat şi să returneze un pointer de tip void conţinînd adresa zonei alocate: void *operator new(size_t) cu menţiunea că size_t este definit în stdlib. intre x++ şi ++x. Chiar dacă parametrul de tip size_t este obligatoriu. C++ permite definirea de reguli de conversie pentru clasele create de programator. Regulile astfel definite sînt supuse unor restricţii: – într-un şir de conversii nu este admisă decît o singură conversie definită de programator.

de exemplu. În cazul conversiei dintr-un tip clasă într-un alt tip clasă. şi întoarce un rezultat de tipul operatorului. Constructorul de copiere. Conversiile de tip folosind constructori constă în definirea unui constructor ce primeşte ca parametru tipul de la care se face conversia. constructorul trebuie să aibă acces la datele membre ale clasei de la care se face conversia. Sintaxa este: operator TipData() respectiv: operator (TipData) Operatorul "cast" este unar. deci trebuie declarată prietenă a clasei respective. la transferul unui obiect ca parametru sau la crearea unei instanţe temporare a unei clase. Exista două metode de a realiza conversii de tip. în limbajul C++ a fost introdus un constructor special. Constructorul întoarce întotdeauna ca rezultat un obiect de tipul clasei de care aparţine. O situaţie care poate apărea deseori este iniţializarea unui obiect cu datele membre ale unui obiect de acelaşi tip. adresa obiectului în cauză. Sintaxa este: 101 . aşa dar are un singur parametru. numit constructorul de copiere. Exista totuşi situaţii în care operatorul de atribuire nu poate fi utilizat. ca urmare folosind această metodă. Pentru a rezolva aceste situaţii. deci trebuie declarată prietenă a clasei respective. se pot realiza numai conversii dintr-un tip de bază sau un tip clasă întrun tip clasă.supraîncărcarea operatorului de atribuire şi în lipsa acestuia se face conversia). Supraîncărcarea operatorului unar "cast". funcţia operator trebuie să aibă acces la datele membre ale clasei de la care se face conversia. prin această metodă se pot defini numai conversii dintr-un tip clasă într-un tip de bază sau un alt tip clasă. cînd copierea membru cu membru nu este adecvată. În cazul conversiei dintr-un tip clasă într-un alt tip clasă. Ca urmare.

Acestea sînt alocate o singură dată. O clasă care conţine o funcţie virtuală pură este considerată abstractă. se pot defini date membre cu o comportare specială. Clase abstracte. În vederea construirii unor astfel de clase.x = x. Sintaxa definirii acestor funcţii este: virtual TipData NumeFunctieMembră() = 0 Funcţiile virtuale pure trebuie definite în clasele derivate. de exemplu. existînd sub forma unei singuri copii. pentru a impune anumite restricţii în realizarea claselor derivate.} În cazul în care clasa nu dispune de constructor de copiere. putînd fi folosite. compilatorul generează automat un constructor de copiere care realizează copierea membru cu membru. care sînt destinate creării de noi clase prin derivare. numite date statice. Membri statici ai unei clase. datele membre ale unei clase sînt alocate în cadrul fiecărui obiect. comună tuturor obiectelor de tipul clasa respectiv. iniţializarea şi accesul la aceste date sînt independente de obiectele clasei. Ele nu pot fi instanţiate şi utilizate ca atare. În mod normal. iar crearea. O astfel de funcţie este declarată în cadrul clasei.NumeClasă::NumeClasă (NumeClasă &NumeObiectSursă) În continuare vom completa clasa Point cu un constructor de copiere: Point::Point(Point &p) {p. Acest gen de clase se numesc clase abstracte. dar nu este definită. Ele se constituie ca bază în cadrul elaborării de ierarhii de clase. altfel şi acestea vor fi considerate abstracte. s-a introdus conceptul de funcţie virtuală pură. p.y = y. În limbajul C++ exista posibilitatea de a defini clase generale. În C++. Sintaxa este: static DeclarareMembru 102 .

Care dintre instrucţiunile de mai jos sînt legale? a: class sir {private: static char eos. De asemenea. aşa dar în cadrul lor cuvîntul cheie this nu poate fi utilizat.} 103 . continut = new char[3. Întrebări pentru verificarea cunoştinţelor: 1. void main() {sir s(80). Funcţiile exterioare clasei pot accesa membrii statici ale acesteia astfel: NumeClasă::NumeMembru Obiect::NumeMembru Funcţiile membre statice nu primesc ca parametru implicit adresa unui obiect. ci întregii clase.Funcţiile membre statice efectuează de asemenea operaţii care nu sînt asociate obiectelor individuale. }. public: sir(int sz). char *q=s. char *p=continut. b: char sir::eos=Ox1A. void copy(sir &s).continut. *p=eos. void sir::copy(sir 63) delete continut. char *continut. while <*qt=eos) c: *p++=*q++.dimensiune]. membrii normali ai clasei nu pot fi referiţi decît specificînd numele unui obiect. int dimensiune.

De ce acest lucru nu se impune atunci cînd redefinim funcţia Aria() din cadrul claselor Punct şi Cerc? 3.h> Class Bunic { public : virtual void sfat() { cout<< “Distrează-te”. e: s. Ce va afişa programul următor? #include<iostream. } } class Fiu : public Tata { public: void sfat() { Bunic::sfat(). listele de argumente trebuie să difere de la implementare la implementare. } }. }. cîte argumente explicite va avea aceasta? 4. atunci cînd redefinim funcţii. Scrieţi o funcţie operator membră a clasei persoana pentru a rescrie operatorul ==. class Tata : public Bunic { public: void sfat() { cout « "Fă-ţi lecţiile'\n". Există mai multe variante ale acestei funcţii? Temele pentru acasă: 1.eos='$'. Dacă redefinim operatorul+ ataşat clasei persoana şi facem această funcţie membră. void main() 104 } . 2. După cum ştim.d: sir::eos=0.

. Ce se va afişa la ecran după executarea programului următor? #include <iostream. } void act2() { cout«"Prinţul o sărută\n".h> class Mesaje_bune { public: virtual void act1() { cout«"Prinţul vede prinţesa\n". act4(). 105 108 . } 2. } }. \n" . cout«"Care varianta vreţi sa o vedeţi (L/B) ?\n" . if (p=='L') | | (c=='l’)) mes=new Mesaje_bune. .sfat().( Fiu lon. act3(). ) void act5() { cout « "Sfîrşit '\n". act5(). Mesaje bune *mes.act4 () . act2().act5(). } }. } void act4() { cout«"Prinţul fuge îngrozit\n". } virtual void act4 () { cout«"Si au trăit fericiţi . nefericit' \n" . void main () { char c. cin»c. class Mesaje rele : public Mesaje bune { public: void act3() { cout«"Prinţesa rămîne teapănă\n" . } virtual void act3() { cout«"Prinţesa se trezeşte\n". lon. } void act5() { cout«"Un sfîrşit.

Creaţi clasa derivată outbin care moşteneşte num. Scrieţi un program care ar defini clasa de bază vector. Scrieţi un program care care ar defini clasa de bază num. În această clasă determinaţi un număr întreg şi funcţia shownum(). care afişează valoarea timpului necesar pentru a parcurge distanţa în mile. Scrieţi un program care ar defini clasa de bază num. În această clasă determinaţi un vector de numere întregi şi funcţia showvector().else mes=new Mesaje rele. delete mes. valorile din sistemele de numeraţie hexazecimală şi octală. 2. să se efectueze toate operaţiile aritmetice asupra numerelor din aceste sisteme de numeraţie. mes->act1(). Scrieţi un program care defineşte clasa de bază distance pentru a păstra în variabila de tipul double distanţa dintre două puncte. 3. Funcţia showvector() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile tabloului pe linii şi pe coloane. Creaţi clasa derivată array care moşteneşte vector. viteza fiind de 100 km/oră. În clasa distance să se creeze funcţia virtuală trav_time(). 4. să se efectueze toate operaţiile aritmetice şi logice asupra numerelor binare. Funcţia shownum() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile din sistemul de numeraţie binar. În clasa derivată metric se va redefini funcţia trav_time() astfel va ea să afişeze timpul necesar pentru parcurgerea distanţei în kilometri. viteza fiind de 60 mile/oră. Să se calculeze suma elementelor tabloului. Creaţi două clase derivate outhex şi outoct care moştenesc num. Funcţia shownum() se va redefini în clasele derivate astfel ca ea să afişeze la disply respectiv. În această clasă determinaţi un număr întreg şi funcţia shownum().} Temele pentru lucrări de laborator: 1. să se efectueze operaţiile 106 .

– să se caute un substring din stringul dat. – să se lichideze un substring din stringul dat din poziţia dată. În această clasă determinaţi valorile logice true şi false şi funcţia showbit(). scăderea. Funcţia showstring() se va redefini în clasa derivată astfel ca ea să afişeze la disply simbolurile prin codurile lor interioare. 7. Scrieţi un program care ar defini clasa de bază bit. Creaţi clasa derivată poligon care moşteneşte punct. Funcţia showbit() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unui şir de biţi. Să se determine poziţia punctului faţă de celelalte puncte din poligon. Creaţi clasa derivată newstring care moşteneşte string. 5. să se determine distanţa minimă de la punctul dat pînă la primul punct ce aparţine poligonului. Să se efectueze următoarele operaţii asupra codurilor: – să se determine spaţiul pentru stringul dat. – să se compare două şiruri de biţi. 107 . Scrieţi un program care ar defini clasa de bază string. În această clasă determinaţi un punct în plan şi funcţia showpoint(). – să se extragă un substring din stringul dat. – să se inverseze un string dat. Funcţia showpoint() se va redefini în clasa derivată astfel ca ea să afişeze la disply punctele cu coordonatele date. 6. Scrieţi un program care ar defini clasa de bază punct. Să se efectueze următoarele operaţii asupra şirului de biţi: – să se determine lungimea şirului de biţi. – să se compare două stringuri. Creaţi clasa derivată outbit care moşteneşte bit. În această clasă determinaţi funcţia showstring(). – să se extragă un subşir de biţi din şirul de biţi dat. înmulţirea).algebrice asupra a două tablouri (adunarea. să se determine spaţiul pentru şirului de biţi dat.

– să se lichideze un subşir de biţi din şirul de biţi dat din poziţia dată, – să se inverseze un şir de biţi dat, – să se caute un subşir de biţi din şirul de biţi dat. 8. Scrieţi un program care ar defini clasa de bază set_bit. În această clasă determinaţi funcţia showset_bit(). Creaţi clasa derivată mulţime care moşteneşte set_bit. Funcţia showset_bit() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unei mulţimi. Să se efectueze următoarele operaţii asupra mulţimii: – să se determine numărul de elemente a mulţimii – să se determine spaţiul pentru mulţimea dată, – să se compare două mulţimi, – să se extragă o submulţime din mulţimea dată, – să se adauge un nou element la mulţime, – să se şteargă un element din mulţime, – să se caute o submulţime din mulţimea dată. 9. Scrieţi un program care ar defini clasa de bază int. În această clasă determinaţi valorile întregi şi funcţia showint(). Creaţi clasa derivată longint care moşteneşte int. Funcţia showint() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unui număr de tip longint..Să se efectueze următoarele operaţii asupra tipului longint: – să se determine numărul de cifre din numărul dat, – să se compare două numere longint, – să se extragă numerele de tip int din numărul longint (partea inferioară şi partea superioară), – să se efectueze operaţiile algebrice asupra numerelor date, – să se inverseze un număr de tip longint (partea inferioară cu cea superioară). 108

10. Scrieţi un program care ar defini clasa de bază real. În această clasă determinaţi valorile reale şi funcţia showreal(). Creaţi clasa derivată double care moşteneşte real. Funcţia showreal() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unui număr de tip double. Să se efectueze următoarele operaţii asupra tipului double: – să se determine numărul de cifre din partea întreagă a numărul dat, – să se determine numărul de cifre din partea fracţionară a numărul dat, – să se compare două numere double, – să se transforme numărul dat într-un şir de caractere, – să se efectueze operaţiile algebrice asupra numerelor date. 11. Scrieţi un program care ar defini clasa de bază int în sistemul de numeraţie p. În această clasă determinaţi valorile întregi şi funcţia showint(). Creaţi clasa derivată longint în sistemul de numeraţie p care moşteneşte int. Funcţia showint() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unui număr de tip int. în sistemul de numeraţie p. Să se efectueze următoarele operaţii asupra tipului longint în sistemul de numeraţie p: – să se determine numărul de cifre din numărul dat, – să se compare două numere longint, – să se extragă numerele de tip int din numărul longint (partea inferioară şi partea superioară), – să se efectueze operaţiile algebrice asupra numerelor date, – să se inverseze un număr de tip longint (partea inferioară cu cea superioară). 12. Scrieţi un program care ar defini clasa de bază real în sistemul de numeraţie p. În această clasă determinaţi valorile reale în sistemul de numeraţie p şi funcţia showreal(). Creaţi clasa 109

derivată double în sistemul de numeraţie p care moşteneşte real în sistemul de numeraţie p. Funcţia showdouble() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unui număr de tip double în sistemul de numeraţie. Să se efectueze următoarele operaţii asupra tipului double în sistemul de numeraţie p: – să se determine numărul de cifre din partea întreagă a numărul dat, – să se determine numărul de cifre din partea fracţionară a numărul dat, – să se compare două numere double, – să se transforme numărul dat într-un şir de caractere, – să se efectueze operaţiile algebrice asupra numerelor date. 13. Scrieţi un program care ar defini clasa de bază int. În această clasă determinaţi valorile întregi şi funcţia showint(). Creaţi clasa derivată fraction care moşteneşte int. Funcţia showfraction() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unui număr fracţionar. Să se efectueze următoarele operaţii asupra numerelor fracţionare: – transformarea unui număr fracţionar mixt într-o fracţie supraunitară, – adunarea numerelor fracţionare, – înmulţirea numerelor fracţionare, – scăderea numerelor fracţionare, – împărţirea numerelor fracţionare. 14. Scrieţi un program care ar defini clasa de bază list. În această clasă determinaţi valorile întregi ale listei şi funcţia showlist(). Creaţi clasa derivată stiva care moşteneşte list. Funcţia showlist() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unei stive. Să se efectueze următoarele operaţii asupra stivei: 110

Scrieţi un program care ar defini clasa de bază int. Creaţi clasa derivată date care moşteneşte int. Creaţi clasa derivată date care moşteneşte int. – înmulţirea cu un coeficient. Scrieţi un program care ar defini clasa de bază data. Să se efectueze următoarele operaţii asupra datei: – adunarea. – împărţirea la un coeficient. 15. Funcţia showdate() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unei date. În această clasă determinaţi valorile întregi şi funcţia showdate().empty(S) care curăţă stiva S. – scăderea. Să se efectueze următoarele operaţii asupra datei: – adunarea. – scăderea. – împărţirea la un coeficient. k=2 dacă stiva este vidă). În această clasă determinaţi valorile întregi şi funcţia showdate(). Funcţia showdate() se va redefini în clasa derivată astfel ca ea să afişeze la disply valorile unei date. Clase derivate cu moştenire multiplă.x) care scoate elementul x din stiva S. error (k) care indică eroarea cu numărul k (k=1 dacă stiva este supraîncărcată.x) care adaugă elementul x în stiva S. push(S. pop(S. – – – – – Lucrarea de laborator nr. – valoarea lunii datei să se tipărească cu cuvinte. 16. if_is_empty(S) care verifică dacă stiva S este vidă. – valoarea lunii datei să se tipărească cu cuvinte. 111 . 4. – înmulţirea cu un coeficient.

această 112 .Scopul lucrării: familiarizarea studenţilor cu clase derivate cu moştenire multiplă. Moştenirea multiplă creşte astfel flexibilitatea dezvoltării ierarhiei de clase. O clasă poate să moştenească simultan proprietăţile mai multor clase. noua clasă. fiecare nivel al ierarhiei acumulează caracteristicile claselor "părinte" şi le adaugă un anumit grad de specializare. Totuşi. derivarea multiplă va genera ierarhii de tip graf. ClasaDeBază. procedură numită moştenire multiplă. Se poate astfel realiza o ierarhie de clase. Clasa1 şi Clasa2. va conţine datele membre ale clasei ClasaDeBază duplicate. Clase virtuale. Sintaxa completă pentru operaţia de derivare este următoarea: class NumeClasăDerivată : ListaClaseDeBază unde ListaClaseDeBază este: SpecificatorAcces NumeClasaDeBază. ClasaNouă. . ::.. în cele mai multe cazuri. Utilizarea moştenirii multiple se poate complica odată cu creşterea dimensiunii ierarhiei de clase. Dacă derivarea normală duce la construirea unei ierarhii de tip arbore.. care la rîndul lor sînt derivate dintr-o clasă comună. 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. Construirea ierarhiei de clase reprezintă activitatea fundamentală de realizare a unei aplicaţii orientate obiect. Pornind de la clase simple şi generale. Consideraţiile teoretice necesare: Moştenirea multipla Limbajul C++ permite crearea de clase care moştenesc proprietăţile mai multor clase de bază. Dacă prezenţa acestor date duplicate este utilă. care să modeleze adecvat sisteme complexe. O situaţie care poate apare este derivarea din două clase de bază. ele pot fi distinse evident cu ajutorul operatorului de rezoluţie. În acest caz.

specificate de constructorii Clasa1 şi Clasa2. De aceea. prin intermediul conceptului de clasă virtuală. în C++ a fost creat un mecanism care sa evite aceasta situaţie. class C1: public B1.duplicare nu este necesară şi duce la consum inutil de memorie. Constructorul ClasaNouă va trebui modificat astfel încît să trimită datele pentru constructorul ClasaDeBază. De exemplu. Declararea virtual a acestor clase va afecta definirea constructorului clasei ClasaNouă. Astfel. În mod grafic acest exemplu se poate de prezentat în modul următor: class A (virtual virtual class B1 113 class B2 . ci numai clasele derivate din aceasta. întro ierarhie de clase derivate. deoarece compilatorul nu poate hotărî care date vor fi transferate către constructorul ClasaDeBază. class A { …}. Sintaxa este: class NumeClasaDerivată : SpecificatorAcces virtual NumeClasaDeBază Această declaraţie nu afectează clasa în cauză. De asemenea. public B2 { …}. constructorul clasei virtuale este întotdeauna apelat primul. class B1: virtual public A {…} class B2: virtual public A {…}. clasele Clasa1 şi Clasa2 considerate vor fi declarate virtuale.

class D : public Bl. // Moştenirea a două clase de bază KOHCTp ( cout « " yKTOpa cout « main () { C ob.} ~B1() { cout « "Destructorul B1 \n". Ce se va afişa la ecran după rularea programului următor? #include <iostream. Ce se numeşte clasă virtuală? Exemple. public : B2 ( ) { cout « "Constructorul B2\n". return 0. } }. 2.class C Întrebări pentru verificarea cunoştinţelor: 1.h> cassBl { public : Bl() { cout « "Constructorul B1 \n". } ~B2 ( ) { cout « "Destructorul B2 \n". Temele pentru acasă: 1. Ce se numeşte moştenire multiplă? Exemple. public B2 { public: 114 .} class B2 { int b.

} ~D() { cout « "Destructorul D\n". return 0.} }. cout « "Constructorul B2\n". } }. public : B2 (int a=0 ) { j=a.D() { cout « "Constructorul D \n".} ~B1() { cout « "Destructorul B1 \n". class B { int j. public B2 { int k. class C : public Bl.h> class A { int i.cout « "Constructorul A \n". public : Al(int a=0) { i=a. // Moştenirea a două clase de bază KOHCTp ( cout « " yKTOpa cout « main () { C ob. } 2. public: 115 . #include <iostream. } ~B2 ( ) { cout « "Destructorul B2 \n". Care va fi rezultatul lansării programului de mai jos? Corectaţi greşelile din program. void main() { D obj.

Să se efectueze următoarele operaţii asupra figurilor geometrice: de rotire a figurii. secunda). Plata se efectuează în lei ţinînd cont de cursul valutar {valuta. secunda}. punct din plan = {coordonata x. şi anume. 5. coordonata y. numitor}. triunghi = {vîrfl. Să se efectueze operaţii algebrice şi conversii asupra următoarelor structuri: număr complex= {partea reală. număr raţional= {numărător. 2. 3. cerc = {centru. şi coordonate polare = {raza. dimensiune}. vîrf3}.. an}. de colorare./* Scrieţi constructorul pentru clasa C astfel ca el să activeze constructorii claselor de bază A şi B*/ c() { cout « "Constructorul D \n". La un depozit a fost adusă marfă de export = {denumire. ~C() { cout « "Destructorul D\n". 4. elipsa = (centru. Structurile de bază sînt deceniu = {veac. data. monitor = {tipul. vînzarea. vîrf2}. raza1. unde unghi = {grad. Să se efectueze operaţii de conversie a următoarelor structuri: punct din scatiu = { coordonata x. preţul. timp = {ora. data = {zi. } Temele pentru lucrări de laborator: 1. coordonata y}. vîrf2. luna. diagonala}. coordonata z}. viteza de lucru}. de decupare a unei părţi din obiect. Să se determine suma de bani necesară pentru achitarea mărfii aduse. minuta. unghiul}. }. dreptunghi ={vîrfl. de a scrie text în figură. data livrării}. raza}. minuta. de mutare pe ecran. Figurile geometrice au următoarele structuri:. ţara de exportare. era}. cumpărarea}. cantitatea. Să se calculeze cît timp a trecut de la începutul erei noastre. raza2}. wincester = {firma. void main() { C obj. partea imaginară}. procesor = {tip. 116 .

La competiţii sportive de diferite probe sportive au participat persoane din mai multe ţări. Într-un oraş a fost construit un centru de prestare a serviciilor telefonice. Fiecare utilizator este abonat telefonic pentru care se indică {numele. cod poştal}.. cum ar fi {minute. copeici}. oraşul. locul de editare}. sutimi} {kilogram. cod telefonic.. gram}. numarul solicitatului. 9. Denumirea ţării are următoarea structură ţară= {denumire. Să se determine frecvenţa de utilizare a fiecărei cărţi. numărul casei. anul filmării}.6. apartamentul. denumire. apartamentul}. cost = {lei. 7. Filmele sunt înregistrate în baza de date cu următoarea structură: film = {denumire. {metri. Rezultatele sportive pot fi estimate în diferite unităţi. numărul solicitantului. gram} şi poate participa numai la una din probele sportive propuse. cod telefonic.. 117 . numarul casei. data montării}. anul de editare.a. Cititorul este înregistrat la bibliotecă după următoarea structură: {strada. În municipiul Chişinău sunt multe cinematografe unde rulează filme. gen. regizor. 8. centimetri}. Informaţia despre oraş are următoarea structură oraş= {denumire. ş. scara. numărul de minute}. Să se determine cele mai bune rezultate sportive obţinute în diferite probe sportive. cenţi}. bani}. ţara. secunde. adresa.. numărul telefonului. O bibliotecă s-a completat cu 100 de cărţi. Fiecare participant are greutatea = {kilogram. numărul de telefon}. Plata pentru servicii se primeşte în diferite unităţi valutare. cod poştal}. Să se afle cît trebuie să achite serviciile prestate aceşti utilizatori dacă ei au ţinut convorbiri telefonice cu alte oraşe. sau {ruble. sau {dolari. scara. La acest centru au fost conectaţi 100 de utilizatori cu adresa: {strada. Informaţia despre convorbireatelefonică a utilizatorului are următoarea structură: convorbire telefonică = {cod ţara. Fiecare carte a fost pusă la evidenţă cu următoarea structură: carte ={autor.

12. strada. Pacientul doreşte să cumpere aceste leacuri. Medicul prescrie reţetă pacientului. destinaţia. data fabricării. punctul de destinaţie. deschiderea. telefon. Pacientul este înregistrat la policlinică după următoarea structură: {nume. Comunicarea între aceste două oraşe are loc cu autocarele ( autocar ={model. data plecării. costul rutei ). destinaţie. cost}. a ora sosirii la destinaţie. cantitate. Hotelurile din Chişinău prestează servicii pentru mai multe persoane din mai multe ţări. 13. ora aterizării}). adresa. 11. cu trenul (mersul trenurilor ={număr. adresă. adresa. categoria }). catedra. diagnostică. numărul de telefon}. închiderea} . Fiecare farmacie este pusă la evidenţă cu următoarea structură farmacie={număr. apartamentul. numarul casei. data îmbolnăvirii}. 10. Să se determine care gen de filme este mai solicitat de vizitatorii cinematografele din Chişinău. cantitatea. data livrării}. cu avionul (ruta de avion = {număr . data sosirii.Fiecare cinematograf este înregistrat cu următoarea structură = {denumire. costul}. Se se afle ruta cea mai optimală (din punct de vedere a costului. Pentru fiecare hotel în baza de date se va introduce următoarea structură {denumirea. Să se determine frecvenţa de solicitare a medicamentelor. ora decolării. tara de exportare. 118 . scara. Fiecare număr la hotel are structura: {număr . numărul de locuri}. telefon. a timpului) dintre două oraşe. La Universitatea Tehnică din Moldova sînt specialităţi la care se învaţă obiecte ce ţin de informatică: specialitatea={facultatea. numărul de stele}. Să se determine din ce ţară ne vizitează mai mult. adresa. ambalaj. ora plecării . numele locatarului. Medicamentele sînt înregistrate în baza de date cu următoarea structură: {denumire. Fiecare persoană este înregistrată la hotel: {ţara. Să se afle adresele farmaciilor unde sînt depozitate medicamentele solicitate. ora sosirii. numărul de telefon. La depozitele unor farmacii a fost adusă marfa de export = {denumire. preţul. tip.

15. studenţi.={adresa. telefon. 5. grupa. Informaţia despre bilet are următoarea structură bilet_de_intrare={muzeu. telefon. etaj. Să se determine frecvenţa de vizitate a muzeului ţinînd cont de numărul de bilete de intrare vîndute. numar camere. La aeroportul din Chişinău la depozitul de lucruri pierdute se află bagaje uitate de pasageri. data şi locul naşterii. numar camere. stăpîn. Informaţia despre un muzeu poate fi introdusă în următoarea structură: muzeu ={ denumire. Să se determine din punct de vedere geografic cîţi studenţi din diferite judeţe la facultăţi au ore de informatică ( studentul = {numele. Formatarea fluxurilor numerice şi textuale. 16. telefon. începutul lucrului. Fluxurile stringuri şi de memorie. În Chişinău sînt multe muzee. cost}). Scopul lucrării: familiarizarea studenţilor cu fluxurile input şi 119 . tip_bilet. Pentru diferite categorii de vizitatori (pensionari. Pentru recuperarea bagajului uitat pasagerul trebuie să prezinte paşaportul (buletinul de identitate). cost. an. oameni maturi. Fluxurile Input şi Output standard şi definite de utilizatori. Lucrarea de laborator nr. La o casă de vindere a imobilului se duce evidenţa caselor (casa. obiect ={denumire. elevi) costul biletului de intrare este diferit.={adresa. tipul lecţiei.obiectul}. ) 14. sfîrşitul lucrului} . Fiecare bagaj este etichetat cu următoarea informaţie: bagaj = {numar rută. Să se determine cele mai solicitate tipuri de vînzări efectuate de casa de vindere a imobilului. data. greutate}. numar ore). media }. copii mici. Să se determine persoanele a cărei ţări mai des uită bagajul în aeroportul din Chişinău. comodităţi }} şi a apartamentelor de vînzare ( apartament. adresa. costul}.

Ierarhia claselor de flux este ilustrată în Fig. cu fluxurile stringuri şi de memorie. Consideraţiile teoretice necesare: Noţiune de Fluxuri Limbajul C++ foloseşte conceptul abstract de stream (flux) pentru realizarea operaţiilor input/output. Acestea pot fi clasificate în funcţie de dispozitivul fizic asociat respectivei operaţii. Fluxurile incapsulează (ascund) problemele specifice dispozitivului cu care se lucrează. sub biblioteca standard iostream.output standard şi definite de utilizatori.input/output prin intermediul fişierelor. Biblioteca iostream.input/output în memorie. Din punctul de vedere al limbajului C++ fluxul este o clasă în sens obişnuit. cu formatarea fluxurilor numerice şi textuale. Din acest punct de vedere limbajul C++ distinge trei categorii de fluxuri: .input/output standard (de exemplu.h. El oferă metode de scriere şi citire a datelor independente de dispozitivul I/O.h. 1.. este scrisă însăşi în limbajul C++ şi este o bibliotecă a claselor. ios iostream iostream istrstream ifstream strstream fstream 120 ostrstream ofstream iostream . tastatura şi ecranul). . Operaţiile de input/output se fac prin intermediul obiectelor de tip flux. .

cerr gestionează ieşirea către dispozitivul standard de eroare (ecranul). In limbajul C++ fluxurile au fost implementate utilizînd clase.h este lansat în execuţie. sînt create şi iniţializare automat patru obiecte: cin gestionează intrarea de la intrarea standard (tastatura). din care sînt descărcate către dispozitiv în momentul umplerii acestei zone de memorie. Cînd un program C++ care include iostream. o–output. şi ostream clasa iostream este derivată din istream şi ostream şi oferă metode pentru lucrul cu terminalul. clasele istream sînt derivate din ios.(Abreviaturile utilizate: I– input. clog gestionează ieşirea către dispozitivul standard de eroare (ecranul). Clasa ios are ca variabilă membru un obiect de tip streambuf. str–string. neutilizînd zone tampon. Ierarhia claselor de flux Alt avantaj al utilizării fluxurilor se datorează implementării bibliotecii iostream. clasa fstream oferă metode pentru operaţii cu fişiere. clasa ios este clasa de bază pentru clasele de stream-uri de intrare şi de ieşire. . cout gestionează ieşirea către ieşirea standard (ecranul). care utilizează un sistem de zone tampon.Obiecte standard. după cum urmează: clasa streambuf gestionează zonele tampon.h. f– file) Fig. Informaţiile trimise către un flux nu sînt scrise imediat în dispozitivul în cauză. ci sînt transferate într-o zonă de memorie tampon.1. utilizînd zone tampon 121 . Operaţiile de intrare/ieşire cu dispozitivele periferice sînt consumatoare de timp.

Expresia "cout «" se utilizează pentru afişarea diverselor valori.m. De fapt. cin >> IntegerNumber. pentru fiecare tip de parametru în parte (int.d (ca la printf. ieşire şi eroare pot fi redirectate către alte dispozitive.a.).h. Respectiv pentru intrare. cout<<"\nWhat you entered = "<<IntegerNumber<<endl. pe care o trimite apoi către ieşirea standard. cout << "IntegerNumber = ".): istream &operator >> (TipParametru &) De exemplu: #include <iostream. iar operaţia input (citirea) se realizează prin intermediul operatorului supraîncărcat ». adică trimiterea datelor imediat către ieşire. '\t'. Redirectări. cin: cin >> NumeVariabilă. } Acest scurt program citeşte de la intrarea standard o valoare întreaga. cout: cout << InformatieDeTrimisLaIesire. etc. Dispozitivele standard de intrare. Utilizarea simbolului endl va forţa golirea zonei tampon. De fapt. care au supraîncărcat operatorul >> respectiv << de mai multe ori. Operaţia output (afişare) se realizează prin intermediul operatorului supraîncărcat «. iar intrarea şi ieşirea pot fi conduse ("piped") către fişiere utilizînd comenzi ale sistemului de operare (utilizarea ieşirii unui program ca intrare pentru altul). Erorile sînt de obicei redirectate către fişiere. şi anume obiectul “flux standard output”.h> void main() {int IntegerNumber. cin şi cout sînt nişte obiecte definite global. Obiectele cout şi cin sînt declarate în biblioteca iostream. scanf. s. Se observă posibilitatea de a utiliza simbolurile '\n'. Obiectul de tip flux standard input este cin. Sintaxa pentru operaţii de ieşire. etc. cout este un obiect de tip flux. 122 . char *.

În astfel de cazuri trebuie folosită funcţia get(). Sintaxa de utilizare a funcţiei get în acest caz este următoarea: cin. caz în care returnează valoarea utilizată. Funcţia cin. care este opţional (implicit considerat '\n'). apelînd-o fără nici un parametru.get(char *PointerLaSirulDeCaractere. Un exemplu de utilizare: #include <iostream. Primul parametru este un pointer la zona de memorie în care va fi depus şirul de caractere. Cel de-al treilea parametru este caracterul de încheiere a citirii.get()) != EOF) {cout << "c = " << c << endl. valoarea returnată de o operaţie de citire/scriere din/în stream poate fi utilizată ca intrare/ieşire pentru următoarea operaţie de acelaşi fel. int Lungime Maximă. Operatorul >> nu poate fi utilizat pentru a citi corect şiruri de caractere de la intrare deoarece spaţiile sînt interpretate ca separator între diverse valori de intrare. este şi el un obiect istream.} } Citirea de şiruri de caractere utilizînd get(). Deoarece cin.Atît operatorul >> cît şi << returnează o referentă către un obiect al clasei istream. funcţia nu poate fi utilizată pentru a citi mai multe intrări. funcţia întoarce valoarea caracterului găsit. sau ca referinţă la un caracter. deoarece valoarea returnată este de tip întreg. Spre deosebire de operatorul >>. nu un obiect istream. get().h> void main() {char c. //fără parametri În această formă. while((c = cin. respectiv cout. Operaţia de intrare cin. char Sfîrşit).get() poate fi utilizată pentru a obţine un singur caracter din intrare. Al doilea parametru reprezintă numarul maxim de caractere ce poate fi citit plus unu. 123 .

int CaractereDeScris). Funcţia cout. cu excepţia faptului că se poate specifica numarul maxim de caractere ce se doresc scrise.put()) scrie un caracter către ieşire.write() este: cout. Funcţionarea sa este identică cu get().write() are acelaşi rol ca şi operatorul <<. cu excepţia faptului că acel ultim caracter menţionat mai sus este şi el extras din flux.put('H'). Sintaxa sa este: cin.write(char *SirDeCaractere. Funcţii membre ale cout Funcţia cout. Această funcţie poate fi apelată şi în forma cout << flush. Funcţia cin.ignore(int NumarMaximDeCaractere.peek() returnează următorul caracter din flux. ca în exemplul de mai jos: #include <iostream.În cazul în care caracterul de încheiere este întîlnit înainte de a fi citit numarul maxim de caractere. Primul parametru reprezintă numarul maxim de caractere ce vor fi ignorate. acest caracter nu va fi extras din flux.put('i').h> void main() {cout.ignore() se utilizează pentru a trece peste un număr de caractere pînă la întîlnirea unui anume caracter. cout .} Funcţia cout. iar al doilea parametru caracterul care trebuie găsit. Există o funcţie similară funcţiei get().putback() inserează în flux un caracter. Sintaxa sa este următoarea: cout. cu aceeaşi sintaxă. Funcţia cin.put(char Caracter). char Sfîrşit). pot fi utilizate apeluri succesive ale acesteia. 124 .flush()) determină trimiterea către ieşire a tuturor informaţiilor aflate în zona de memorie tampon. Deoarece această funcţie returnează o referinţă de tip ostream. Funcţia cin.put('\n'). fără însă a-l extrage. numită getline().put('!'). Sintaxa funcţiei cout.

left. Funcţie cout. Ea modifică dimensiunea numai pentru următoarea operaţie de ieşire. Opţiuni de formatare a ieşirii.Formatarea ieşirii Funcţia cout.setf() activează o opţiune de formatare a ieşirii. showbase determina adăugarea identificatorului bazei de numeraţie în faţa valorilor numerice.h> #include <iomanip.fill() permite modificarea caracterului utilizat pentru umplerea eventualului spaţiu liber creat prin utilizarea unei dimensiuni mai mari decît cea necesară ieşirii. care implicit este considerată exact mărimea cîmpului în cauză. right.setf(ios::Opţiune). La dreapta. Sintaxa sa este: cout. internal centrează). dec.fill(char Caracter).width() permite modificarea dimensiunii valorii trimise spre ieşire. schimbă alinierea ieşirii (la stînga.width(). hex schimbă baza de numeraţie pentru valori numerice.width(int Dimensiune). În continuare vom exemplifica utilizarea funcţiilor pentru formatarea ieşirii: #include <iostream. primită ca parametru: cout. Funcţia cout. oct. unde Opţiune poate fi: showpos determină adăugarea semnului plus (+) în faţa valorilor numerice pozitive.width(). şi anume: Funcţia cout.setw() modifică dimensiunea ieşirii. fiind similară funcţiei cout. Sintaxa este: cout. cu funcţia cout. Sintaxa acesteia este: cout.setw(int Dimensiune).h> 125 . Pentru formatarea ieşirii sînt definite doua funcţii membre ale cout.

cout << "number = " << number. long int telefon. class obj) { // tipărirea componentelor din clasa obj return s. vom redefini operatorii menţionaţi pentru clasa persoana. class obj) { //citirea componentelor din clasa obj return s. 126 . char prenume[40].h> # include <string. cout << "Numar însistemul octal. aliniat la stînga = " << oct << number. cout<<"Numar în sistem hexazecimal="<<hex << number.setf(ios::showbase).h> class persoana { private: char nume[40]. } Redefinirea operatorilor de intrare şi ieşire pentru fluxul standard. cout. } Operatorul de ieşire a fluxului standard pentru un obiect din clasa obj se redefineşte în modul următor: ostream & operator<< (ostream &s.void main() {int number = 783. # include <stdio. } De exemplu. Operatorul de intrare a fluxului standard pentru un obiect din clasa obj se redefineşte în modul următor: istream & operator >> (istream &s. Operatorii de intrare şi ieşire pentru fluxul standard pot fi redefiniţi pentru citirea şi înregistrarea obiectului de tipul clasei definite de utilizator. cout.setf(ios::left).

telefon). strcpy(prenume. Numele va fi un obiect de tipul clasei corespunzătoare. }. cout<<”Introdu datele despre persoana” cin >>p. persoana P). int v. char *p.telefon). }.virsta) &&(P. Lucrul cu fişiere se face prin intermediul clasei ifstream pentru citire respectiv 127 .virsta.nume<<P. long int t) {strcpy(nume. persoana P). istream & operator >> (istream &s.prenume.Nume0. long int t) {strcpy(nume. virsta=v. persoana P) {return(s<<P. void set(char n.set(P. Î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) && (s >>P. } Operaţii de intrare/ieşire cu fişiere. return s. persoana P) { if ((s >>P. int v.telefon)) P. } friend istream & operator >> (istream &s.prenume<<P.Nume0. cout<< p.prenume)&& (s>>P. friend ostream & operator<< (ostream &s.} void main() { persoana p.n). virsta=v. char *n.P.int virsta. public: persoana(char *Nume. Fstream nume flux utilizat şi pentru citire şi pentru scriere în oricare din cazurile de mai sus. } ostream & operator << (ostream &s.nume. telefon= t.virsta<<P. strcpy(prenume. telefon= t.P. P.n).

scriere. De exemplu: #include<fstream. ifstream Variabila(char *NumeFişier). creează un fişier cu numele disk. Execuţia programului va determina crearea unui fişier. Definirea fluxurilor se poate face numai prin includerea în program a fişierului fstream. Fiind o funcţie membru. ofstream şi fstream sînt clase.h. scriere. } Programul de mai sus exemplifică faptul.h. că nu există nici o diferenţă între lucrul obişnuit cu clase şi definirea unui flux output.ofstream pentru scriere. Funcţia de deschidere a unui stream este funcţia open. ios::Mod). care. Definiţia clasei ofstream poate fi găsită în fişierul fstream. apelată prin intermediul obiectului scriere. ifstream. aplicaţiile trebuie să includă fstream. Declararea unui flux al utilizatorului este o declarare a unui obiect din clasa respectivă. Clasele ofstream şi ifstream sînt derivate din clasa iostream. În continuare acesta este închis prin apelul funcţiei membru close(). însă nu va conţine nimic. cît şi metode (funcţii). Sintaxele pentru constructorii acestor două clase sînt: ofstream Variabila(char *NumeFişier. prin intermediul obiectului scriere.close().h> void main() { ofstream scriere. ea va fi apelată numai prin intermediul unui obiect declarat de tipul uneia din clasele de mai sus.h. Funcţia membru open.dat. Operaţiile input/output se fac prin intermediul unor funcţii membri ale claselor respective. Scrierea propriu-zisă se face cu operatorul 128 . ca urmare toţi operatorii şi toate funcţiile descrise mai sus sînt moştenite şi de această clasă. Pentru a le utiliza.dat").open(“disk. Astfel. variabila scriere a fost declarată ca fiind de tipul (clasa) ofstream. Clasele pot conţine atît date.

utilizează codificarea ASCII a datelor. se asociază unui flux de intrare. care va fi asociat fluxului. Ideea de a utiliza noţiunea abstractă de flux s-a impus din dorinţa de a asigura independenţa operaţiilor input/output faţă de calculator sau de sistemul de operare.h. această independenţă să fie asigurată. De exemplu: 129 . Caracteristicile de tip text şi binary (binar) trebuie privite în legătură cu operatorii folosiţi în operaţiile input/output. ios:: ate. se asociază unui flux de ieşire. fişierul trebuie să existe deja. definite în fişierul ifstream. funcţii. Valorile de mai sus sînt constante definite în clasa ios. iar citirea cu operatorul ». variabile. ios::noreplace. în mare măsură. fişierul trebuie să nu existe. Aceşti constructori au rolul de a deschide fişierul specificat ca parametru. poziţionarea se face la sfîrşitul fişierului. Producătorii de compilatoare livrează împreună cu compilatorul biblioteci. ios:: binary. care conţin clase. care permit ca. Al treilea parametru este ignorat în cazul. ios::in. Operatorii. distruge un fişier preexistent. Funcţia open are primul parametru de tip şir de caractere modificată prin modificatorul const. accesul se face prin adăugare la sfîrşitul fişierului. fişierul este interpretat ca fişier binar. Al doilea parametru specifică modul de acces la fişierul asociat fluxului şi poate avea următoarele valori: ios:: app. ios::out. Acesta reprezintă numele fişierului. ios::nocreate. în care parametrul al doilea este ios :: nocreate. pe care i-am folosit pînă acum ( « şi »). – la fel ca în cazul fluxurilor standard input/output.(supraîncărcat) «. avînd acelaşi nume. ios::trunc.

sînt read şi write. open ("d1.close (). s2«"text \n"«12«"\n"«4. ofstream s2. s1. s2.2. care realizează copii ale memoriei.dat"). s1.open("d2.open("d2. De exemplu: #include<fstream. sizeof(i)) .h> void main() {ofstream s1. s2.ios::binary). s1«"text \n"«12«"\n"«4. s1.2. 0 zonă de memorie poate fi privită ca un şir de octeţi.#include<fstream. int i.open("dl. 4).dat"). s2. ofstream s2. close ().write((char *) &i.h> void main () {ofstream s1. care trebuie să fie adresată printr-un pointer de tip caracter. s1. s1. ios:: binary) . } 130 .dat". s2.4) . dat". Funcţiile read şi write. sizeof(i)). write (" \n".close(). Funcţia write scrie într-un fişier conţinutul unei zone de memorie.write("\n". } Funcţiile specializate pentru operaţii input/output. s2.write((char *) &i. s1. indicat prin adresă de început şi lungime.close(). s2.

care se utilizează pentru înregistrarea (<< sau write) respectiv. se poate de interpretat orice adresă din memorie ca.j.dat") . De cîte ori citim un fişier. Astfel.open("d1.close(). fiind adresa unui şir de caractere. despre care ştim cum a fost creat.write(p. este bine să-l citim cu aceleaşi caracteristici şi cu aceeaşi operatori. } Reprezentarea datelor într-un fişier depinde exclusiv de funcţiile. Diferenţa între fişierele de tip text şi binary se păstrează şi în cazul utilizării funcţiilor read şi write şi constă în reprezentarea diferită a caracterelor de control. De exemplu: #include<fstream.dat". caracter cu caracter.h> void main() {int i. s2.Funcţia de conversie (char *) se utilizează pentru a converti adresele oricăror obiecte la tipul adresă de caracter.write(p. Operatorul sizeof se utilizează pentru determinarea lungimii acestei zone de memorie.open("d2. utilizînd funcţia read. ofstream s2. se face respectînd aceleaşi principii ca în cazul funcţiei write. Citirea unui fişier. float k. 131 . Cînd vrem să citim din fişier.sizeof(p)). despre care nu ştim cum a fost creat. char p[10]. ofstream s1. s1. s1. s2. este bine să-l citim în mod binar. De exemplu: #include<fstream.sizeof(p)).ios::binary).h> void main() {char *p=''\n". s1. s2.close() . citirea acestora (>> sau read).

s1« "\n abcd\n" « 9. } fişierul creat poate fi citit în mod binar. s2. s1. s.close(). Pentru a închide aceste fişiere trebuie apelată funcţia membră close().dat") . Atribuirea explicită p[19]=NULL este obligatorie pentru a marca sfîrşitul şirului de caractere.h> void main() { char p[20].ios::binary).close(). } În alt exemplu: #include<fstream>. Instrucţiunea read copie conţinutul fişierului în şirul de caractere p.3. cout « p.read(p.close(). ifstream s. de 132 . 1. s2 « i « 3 « p « k.ios::binary). s2. s. s.open("d. » şi. Ultima instrucţiune din programul de mai sus afişează la ieşirea standard conţinutul vectorului p. Am utilizat fluxurile standard şi operatorii «.open("d.dat".open("d. utilizînd funcţia read. conţinutul acestuia se ia ca un şir de caractere. ofstream s2. Fluxuri în memorie constituie o interfaţă între program şi un dispozitiv fizic.ofstream s1. În acest mod conţinutul fişierului este considerat copia unei zone de memorie. s1« 122 « "\n" « 147. Deoarece fişierul a fost creat printr-o codificare ASCII. p[19]=NULL.19).dat".

asemenea, fluxurile asociate cu fişiere. Există posibilitatea de definire a unor fluxuri în memorie. Acestea sînt fişiere, care fizic sînt localizate în memorie. Un flux în memorie este un şir de caractere, care are exact aceeaşi structură ca un fişier obişnuit. Clasele, care definesc aceste fluxuri, sînt: istrstream(input string stream); flux input, ostrstream(output string stream); flux output, strstream (string stream); flux input/output. Funcţii de formatare. Clasele bazate pe fluxuri conţin o serie de funcţii, care oferă o mare flexibilitate a operaţiilor de scriere cu formatare. Pentru datele numerice cele mai importante sînt funcţiile, care permit tipărirea într-un cîmp de lungime fixă, alinierea (la stînga sau la dreapta) şi precizia, cu care se face afişarea. Funcţia width stabileşte dimensiunea cîmpului, calculată în caractere, pe care se va face scrierea. Funcţia 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 funcţia asociată a. width, prin care am cerut ca fiecare dată, să fie înregistrată într-un cîmp format din 8 caractere. Funcţia setf are, de asemenea, un singur parametru în cazul dat ios::right, avînd semnificaţia de aliniere la dreapta. Funcţia precision are un singur parametru de tip întreg, prin care se specifică numărul de poziţii, 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); 133

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 << "\xOA";}; s[164]=NULL; cout << s; } În programul de mai sus parametrul funcţiei 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 operaţiilor de intrare/ieşire poate fi testat prin intermediul a patru funcţie membre: – eof() verifica daca s-a ajuns la sfîrşitul fişierului; – bad() verifica daca s-a executat o operaţie invalida; – fail() verifica daca ultima operaţie a eşuat; – good() verifica daca toate cele trei rezultate precedente sînt false Funcţiile de formatare pot fi aplicate oricărui obiect de tip flux, fie că este un flux standard, un flux de tip fişier sau un flux în memorie. Întrebări pentru verificarea cunoştinţelor: 1. Care sînt tipurile de fluxuri standard definite de utilizator? 2. Scrieţi un exemplu de utilizare a funcţiilor read şi write a conţinutului unei zone de memorie. 134

3. open ale unei clase flux. 4. 5. fluxuri?

Definiţi

parametrii funcţiei membru

Cum se definesc fluxurile de memorie? Care sînt funcţiile de formatare pentru

Temele pentru acasă: 1. Corectaţi greşelile şi lansaţi următoarele exemple la execuţie. Ce rezultate vor apărea 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) 135

f.} Exemplul 4.serw(6)<<setprecision(2) . #include <iostream.seekg(17.txt”.ios::out).80.seekp(17..45.eof()) break.h> void main() {cout <<setfill(‘%’}<<setw(4)<< 17. s<<”Preţul stocului este de”.close().read((char*)&I.2) . #include <iostream.h> void main() {char buffer [80]. cout<<I.ios::beg).txt”. f. if(cin. } Exemplul 5. cin. #include <iostream.write((const char*)&I.h> #include <strstream.ios::beg). strstream s (buffer. f. fstream f (“dat.2). ios::in|ios::binary). 136 . f.h> void main() { int I=42. float pret=123. f. cout.put( c).{ char c.close(). f.h> #include <fstream.open(“dat. s. f.ios::out|ios::binary). } Exemplul 3.get©.

4. Scrieţi un program care ordonează lexicografic o consecutivitate de înregistrări (dintr-un fişier) de tipul struct { char nume [30]. 9. Cuvintele din fişier sînt separate prin virgulă. Scrieţi un program care din trei fişiere se selectează în unul nou mai întîi numerele divizibile la 3. int ani} înregistrare. 8. Scrieţi un program care din trei fişiere se selectează în unul nou mai întîi numerele negative. Scrieţi un program care din două fişiere ordonate descrescător se vor uni în unul nou în care se va păstra ordinea descrescătoare de sortare. Scrieţi un program care din 100 de numere aleatoare dintr-un fişier se determină numărul maximal şi cel minimal. Scrieţi un program care compară două fişiere date. 6. Pentru fiecare element sortat să se indice numărul de repetări ale cuvîntului în textul dat. 2. Rezultatul ordonării să se înscrie într-un nou fişier. 5. 7. 10. Scrieţi un program care sortează lexicografic cuvintele dintr-un text. apoi numerele pozitive pare de pe locuri impare. 137 . apoi numerele pozitive. iar.} Temele pentru lucrări de laborator: 1. Scrieţi un program care calculează numărul de elemente ale unui fişier care sînt mai mici ca valoarea medie aritmetică a tuturor elementelor acestui fişier. Scrieţi un program care calculează suma înmulţirii numerelor vecine pe pe locuri pare dintr-un fişier dat.după ultimul cuvînt se pune punct. 3. la 5 şi la 7. zerourile. Scrieţi un program care tipăreşte toate cuvintele diferite de ultimul cuvînt dintr-un fişier. Subconsecutivitatea de elemente dintre numărul maximal şi cel minimal determinat să se înregistreze într-un nou fişier.

13. Templates: Template pentru clase şi funcţii. 16. Scrieţi un program care determină numărul maximal şi cel minimal din numerele unui fişier dat. 14. Scrieţi un program care determină frecvenţa cu care a fost generat fiecare element în fişierului creat.11. 6. Valorile elementelor sînt cuprinse între 1 şi 100. Scrieţi un program care din două fişiere ordonate crescător se vor uni în unul nou în care se va păstra ordinea crescătoare de sortare. restul rîndului din fişierul dat se scrie din rînd nou. 15. Scrieţi un program care va tipări în ordine inversă subconsecutivitatea de numere dintre valoarea minimă şi maximă ale unei consecutivităţi de numere citită dintr-un fişier. Consideraţiile teoretice necesare: Clase şi funcţii generice 138 . Lucrarea de laborator nr. Dacă în rîndul dat se depistează punctul. Scopul lucrării: familiarizarea studenţilor cu clase şi funcţii generice. 12. Scrieţi un program care formează un fişier nou din cel dat după următoarea legitate: elementele fişierului nou se obţine din inversul numerelor din fişierul dat. Să se determine elementele mai mari ca cel minimal şi mai mici ca numărul maximal. Lungimea rîndului de caractere în fişierul nou are lungimea de 60 de caractere. Scrieţi un program care efectuează reformatarea unui fişier textual în felul următor.

. // definirea clasei } Particularizarea or stabilirea tipului clasei template se face prin intermediul unei construcţii de genul: NumeClasa <NumeParametru> unde NumeParametru reprezintă tipul obiectului. Clasele parametrizate (sau clasele template) se declară astfel: template <class NumeParametru> class NumeClasa { // . Funcţiile template se declară astfel: template <class NumeParametru> // 139 .Template-ul implementează asa-zisul concept de "tip parametrizat" ("parametrized type"). Template-urile sînt foarte utile pentru realizarea de biblioteci care trebuie să ofere metode generice de prelucrare a datelor. De exemplu. definirea unei clase sau funcţii membre al unei clase template. inclusiv tipuri necunoscute la momentul implementării acesteia.. Un template reprezintă o familie de tipuri sau funcţii. cu alte cuvinte. Conceptul de template permite realizarea unei clase generale (să spunem List). iar pentru o listă de şiruri altă clasă (să spunem ListOfStrings). Tipul template este stabilit în momentul instanţierii sale. Acest concept a fost introdus în primul rînd pentru a creşte gradul de reutilizabilitate a codului. sau definirea unui membru template al unei clase. pentru a implementa o listă de numere întregi este necesară în mod normal realizarea unei clase speciale (să spunem ListOfIntegers). care să accepte orice tip de element. un şablon sau model. definirea unui membru static al unei clase template. Sintaxa generală de declarare a unui template este următoarea: template < ListaDeParametri > Declaratie unde Declaratie reprezintă declararea sau definirea unei clase sau unei funcţii.

StackItem <Tip> *Next) {Data = new Tip(Data). delete temp. Tip *Data. #include <iostream..} Tip top() {return *(Data->Data). return result. }.. void main() {Stack <int> anIntegerStack.h> template <class Tip> class StackItem {public: StackItem *NextStack. 140 . // declaraţia funcţiei Să considerăm în continuare ca exemplu implementarea unei stive generice folosind template-uri.push(7). anIntegerStack.} private: StackItem <Tip> *Data.push(5). anIntegerStack.} }..} int isEmpty() {return Data == 0. template <class Tip> class Stack {public: Tip pop() {Tip result = *(Data->Data).} Stack() {Data = 0. StackItem <Tip> *temp = Data.} void push(T __Data) {Data = new StackItem <Tip>(Date. NextStack = Next. StackItem(Tip Date. Data = Data->NextStack. Date).

} Point(unsigned X. Point p) {output<<"(" << p.x <<".} void SetX(unsigned X) {x = X. care poate fi considerată ca fiind un "cursor" care străbate lista.y = Y.isEmpty()) cout << "Stiva goala" << endl.end() pe ultimul element al listei.} void SetY(unsigned Y) {y = Y. unsigned Y) {x = X.} unsigned GetY() {return y. else cout << anIntegerStack. Item <Tip> *__Next) 141 . } În exemplul următor a fost implementată o listă generică (List). List. public: Point() {x = 0.h> class Point {friend ostream& operator << (ostream& output. Saltul la următorul element al listei se face cu ajutorul operatorului ++ din clasa Iterator. Ca elemente a listei s-au folosit obiecte de tip Point.y = 0.y << ")". Funcţia List.if(anIntegerStack.begin() returnează un iterator poziţionat pe primul element al listei. Tip *Data. Pentru parcurgerea uşoară a listei a fost implementată o clasă de tip "iterator".} template <class Tip> class Item {public: Item *Next.pop() << endl.}}. y. ostream& operator << (ostream& output. return output. Item(Tip __Data.} ~Point() {} unsigned GetX() {return x. protected: unsigned x. cout << anIntegerStack. #include <iostream.top() << endl. " << p. Point p).

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

Ce reprezintă un template? Exemple de funcţie template. funcţia respectivă poate fi supraîncărcată pentru tipul dorit. cout << endl. else for(.end(). – o clasă sau funcţie template. – o clasă sau funcţie template avînd tipul specificat. anPointList. index != end.return Iterator(temp). 14)).push_front(Point(1. index++) cout << *index << " ".begin(). Trebuie remarcat de asemenea că în cazul în care o clasă template conţine membri statici.} private: Item <Tip> *Data. end. } Clasele template pot avea trei tipuri de prieteni (friends): – o clasă sau funcţie care nu este de tip template. if(anPointList.empty()) cout << "Lista vida" << endl.push_front(Point(3. Dacă este necesară particularizarea unei funcţii template sau a unei funcţii membre a unei clase template pentru un anumit tip. index = anPointList. anPointList. 143 . fiecare instanţă a template-ului în cauză va conţine propriile date statice. }. void main() {List <Point> anPointList. end = anPointList. List <Point>::Iterator index. Întrebări pentru verificarea cunoştinţelor: 1. 1)).

3. a=b.j). Pot oare fi redefinite funcţiile template? 4. 2. Care sînt ele? Exemple. Care este corelaţia dintre clase derivate şi clase template? 3. y=23.clasă template. Clasele template pot avea trei tipuri de prieteni (friends). Temele pentru acasă: 1. b=temp. у după schimb:”<<x<<' '<<у <<endl. float x=10.h> template <class data_t> class list { data_t data. public: list (data_t d). j după schimb:”<<i<<' '<<j<<endl. X &b) {X temp. Ce efectuează următoarea funcţie template? template <class X> void swap(X &a. Ce rezultate obţinem la rularea următorului program? #include<iostream. j=20. temp=a. cout<<”Valorile i. // schimb de variabile cout<<”Valorile i. swap(i.} void main() {int 1=10. } 2. swap (x. list *next. cout<<”Valorile х. j sînt:”<< i << ' '<< j << endl. 144 .y). у sînt”<< x<< ' '<< у <<endl. cout<<”Valorile х.1.

an. p=p->getnext(). Această problemă poartă denumirea de problemă de sortare sau de aranjare a consecutivităţii care poate fi aranjată şi în ordine descrescătoare: a1 ≥ a2 ≥ …≥ an.. …. El se va înlocui cu primul element din consecutivitate. last=p. i<26. Se dă consecutivitatea de obiecte a1. next-0.} return 0.void add(list *node) {node->next=this.} //tiparul listei p=&start.} } template <class data_t> list<data_t>::list(data_t d) {data=n. Sortare prin selecţie. dacă numerele două cîte două sînt diferite consecutivitatea poate fi aranjată în creştere sau descreştere. list<char>*p. } Temele pentru lucrări de laborator: 1. *last.} list *getnext() {return next. } void main() {list<char> start('a'). i++) {p=new list<char>(‘a’+1). apoi se procedează 145 . Să se afle elementul tabelului care are valoare minimală. Fie elementele consecutivităţii se rearanjează în aşa mod ca după sortare elementele consecutivităţii să fie aranjate în ordine crescătoare: a1 ≤ a2≤ …≤ an.} data_t getdataO {return data. // crearea listei last=&start. while(p) {cout<<p->getdata(). for(int i=l. next=0.

cu restul elementelor din consecutivitate începînd cu al doilea element ş. …. Următoarele analize se vor începe de la început micşorînd cantitatea de elemente cercetate cu o unitate. Această problemă poartă denumirea de problemă de sortare sau de aranjare a consecutivităţii care poate fi aranjată şi în ordine descrescătoare: a1 ≥ a2 ≥ …≥ an. dacă numerele două cîte două sînt diferite consecutivitatea poate fi aranjată în creştere sau descreştere. ai-1 .m. …. Prin analiza consecutivă a elementelor a1.d. Consecutivitatea va fi sortată după analiza elementelor . an.d. Acest loc se determină prin compararea consecutivă a elementului ai cu elementele sortate a1. Această problemă poartă denumirea de problemă de sortare sau de aranjare a consecutivităţii care poate fi aranjată şi în ordine descrescătoare: a1 ≥ a2 ≥ …≥ an. 2. Sortarea prin interclasare.a.. 3.a. ….m. Fie elementele consecutivităţii se rearanjează în aşa mod ca după sortare elementele consecutivităţii să fie aranjate în ordine crescătoare: a1 ≤ a2≤ …≤ an.. dacă numerele două cîte două sînt diferite consecutivitatea poate fi aranjată în creştere sau descreştere. …. să se reînceapă analiza consecutivităţii cu elementul ai+1 ş. Să se schimbe aii şi ai+1 cu locul. Sortarea prin inserţie. Prin aceste manipulării elementul cel mai mare se va mişca pe ultimul loc al consecutivităţii. …. 146 . în care au participat numai elementul unu şi doi. an. Se dă consecutivitatea de obiecte a1. ai-1. …. an să se afle cel mai mic i astfel ca ai > ai+1. Se dă consecutivitatea de obiecte a1. Să se cerceteze consecutiv a2. Fie elementele consecutivităţii se rearanjează în aşa mod ca după sortare elementele consecutivităţii să fie aranjate în ordine crescătoare: a1 ≤ a2≤ …≤ an. Să se efectueze această procedură. an şi fiecare element nou ai inclus la locul potrivit în consecutivitatea deja sortată a1.

În acelaşi timp şi numărul de comparări pentru algoritmul de sortare prin selectare şi pentru algoritmii de sortare prin interclasare şi prin inserţie în unele cazuri (de exemplu. Să se demonstreze că numărul de schimbări pentru algoritmul de sortare prin selectare este mărginit de o funcţie liniară de n. d) după descreşterea valorilor maximale ale elementelor liniilor matricei. an în procesul de utilizare a algoritmilor descrişi în varianta I. astfel ca după introducerea numărului b în acest loc consecutivitatea să rămînă sortată în aceeaşi ordine. Din definiţia problemei din punctul 1 rezultă că algoritmul de sortare prin selectare se deosebeşte de celălalţi algoritmi în acele cazuri cînd schimbarea locului elementului este cu mult mai dificilă decît compararea elementelor. c) după descreşterea valorilor minimale ale elementelor liniilor matricei. 5. dacă consecutivitatea iniţială este de tipul a1>a2>…>an) sînt funcţii pătratice de n.4. an. Pentru punctele b). d) să se utilizeze o consecutivitate auxiliară de numere a1. …. Să se cerceteze şi să se calculeze numărul de comparări şi de schimbări (adică permutaţii dintr–un loc în altul) ale elementelor a1. …. c). 6. Fie se dă în ordine descrescătoare o consecutivitate de numere întregi sau reale a1 ≤ a2≤ …≤ an şi fie se dă un număr b (respectiv întreg sau real). să se sorteze (să se schimbe cu locul) liniile matrice: а) după creşterea valorile primelor elemente ale liniilor matricii. Se dă un obiect matrice de numere reale de dimensiunea n× m. b) după descreşterea sumelor elementelor liniilor matricei. pentru care trebuie să-I căutăm locul între numerele a1. Să se utilizeze acest fapt la executarea următoarelor probleme. an. …. Dacă unele elemente sînt egale după valoare cu numărul b acesta poate fi introdus în diferite locuri mai aproape de începutul 147 .

unde s – partea întreagă mediei aritmetice a valorilor hotarelor. executarea algoritmului descris se finalizează cu rezultatul t.. ….. …. Pentru b sînt n+1 posibilităţi: b ≤ a1. pas cu pas se mişcă aceste hotare în modul următor: se compară b cu as. Se ia mai întîi 1 şi n + 1 în calitate de hotare de căutare a locului elementului. Să se introducă schimbări în descrierea algoritmului de împărţire în jumătăţi şi respectiv să se rezolve problema a). m). iar hotarul de sus se schimbă cu s... Să se obţină numerele naturale k1. atunci se înlocuieşte hotarul de jos precedent cu s+1. iar cel de sus rămîne fără schimbare. an. bm (a1 ≤ a2≤ …≤ an). b). … an-1 <b ≤ an. …. Pentru rezolvarea acestei probleme va fi util următorul algoritm care se numeşte algoritmul împărţirii în jumătăţi (algoritmul căutării binare). …. Fie locul pentru careva număr b printre cele ale consecutivităţii sortate crescător a1... în caz contrar se lasă fără nici o schimbare hotarul de jos. a1<b≤ a2. . c). bm. an < b şi Ca soluţie a acestei probleme de căutare a locului elementului b va fi corespunzător unul din numerele 1. …. fiind egale cu careva număr t. n)). pn (pi este cîştigul pentru numărul ai (i = 1. Această problemă poartă numele de problemă de căutare a locului elementului. . km astfel încît ki – să fie soluţia problemei căutării locului bi printre a1..consecutivităţii. …. Să se utilizeze algoritmul căutării binare. an şi tabela de cîştiguri în lei p1.. dacă аs<b. an (i= 1. (Numărul de comparări necesare pentru acest algoritm nu excedă [log2 (n +1)]+1) a) Sunt date numere reale a1. b1. apoi pînă cînd valorile hotarelor nu coincid. …. Tabela de cîştiguri a loteriei este reprezentată prin tabela de numere cîştigătoare a1. 148 . n+1. Să se utilizeze algoritmul căutării binare... …. an se alege ca cel mai îndepărtat loc de la începutul consecutivităţii neafectînd legitatea sortării crescătoare. Să se determine suma cîştigurilor pentru biletele cu numerele b1. . Cînd valorile hotarelor coincid.

apoi. Se presupune că numerele a1. Ca să se obţină max(a1. an sînt egale între ele. c) să se afle max (a1. 9.Consecutivitatea se organizează în formă de listă dublu lănţuită. an) se subînţelege că nu toate numerele a1. ai+1). a2l+2).. an sînt egale între ele. an). …. min(a1. an sînt elementele unui fişier. a2l+2) cu min(a1. …. an după ce se elimină din ea a) un element cu valoarea mах (a1. …. …. …. a2l). ….n]. Fie n este un număr par.7. a2l) (l=1. an) şi min (a1. Să se folosească algoritmul de sortare a vectorului T[1.. …. …. Să se rezolve următoarele probleme: să se afle valoarea maximală din consecutivitatea dată a1. ai). Căutare bînară. Să se determine locul lui x în T. an) utilizînd următorul algoritm. ai+1). dacă ai+1 < max(a1. sortîndu-i pe aceştia şi apoi interclasîndu-i (amestecîndu-i). an). mai întîi se compară între ele a2l+1. min(a1. ai). ….. Fie T[1. …. a2l+2).n} un vector sortat crescător şi x un obiect care se află printre obiectele lui T. an după ce se elimină din ea a) un element cu valoarea mах (a1. se compară ai+1 cu max(a1. Valoarea n nu se cunoaşte. n). ai). an) se subînţelege că nu toate numerele a1. 8. Pas cu pas să se obţină perechile max(a1. suplimentar se compară ai+1 cu min(a1. divizîndul în doi vectori F şi V. ai). …. …. an) şi min (a1. b) toate elementele cu valoarea max(a1. …. Să se rezolve următoarele probleme: să se afle valoarea maximală din consecutivitatea dată a1. a2l+2 şi max(a2l+1. ….. …. k). adică n = 2k.. a2l).. …. …. …. an sînt elementele unui fişier. …. …. …. …. iar min(a2l+1. . …. Atunci pas după pas să se obţină max (a1. min(a1.. Se presupune că numerele a1. …. c) să se afle max (a1. a2l+2) se compară cu mах(a1.. …. ai) (i= 1. a2l) Dacă n este un număr 149 . Ca să se obţină max(a1. …. min(a1. Valoarea n nu se cunoaşte. an) utilizînd următorul algoritm. b) toate elementele cu valoarea max(a1. …..

an.4). …. cu min(a1. trebuie de cercetat următorul ai+1 fără schimbarea consecutivităţii a1. În procesul de sortare să se elimine elementele care se repetă. ak (k < i) se va depista că printre a1. posibil. Pentru sortate la etapele intermediare se utilizează 150 . Algoritmul de insertare simplă poate fi schimbat astfel. …. Algoritmul fon Newmann de sortare a tabelei a1. Apoi grupele se măresc prin aceeaşi metodă pînă cînd obţinem o grupă care conţine toate elementele consecutivităţii iniţiale de acum sortate. an-1). …. Să se scrie programul care realizează acest algoritm. an. Pentru aceasta să se folosească algoritmul de sortare prin insertare binară. …. …. ak. posibil. an în ordine nedescrescătoare (algoritm de sortare prin unire) se bazează pe unirile de mai multe ori ale grupelor de elemente ale tabelei deja sortate. cu excepţia ultimei grupe nu s-a găsit pereche). Acest algoritm necesită aproximativ n⋅ log2n comparări ale elementelor consecutivităţii. …. 11. …. Sunt date obiectele a1. Unirea grupelor vecine ne dau noi grupe deja sortate care conţin cîte două elemente (poate. Să se obţină în ordine crescătoare toate numerele diferite ce intră în a1. an.impar. ak este deja un element egal cu ai. se determină prin algoritmul împărţirii în jumătăţi (p. Mai întîi tabela este privită ca o totalitate de grupe sortate ce conţin cîte un element. se va efectua încă un pas suplimentar: compararea ultimului element an cu max(a1. Locul unde trebuie insertat şi deja în consecutivitatea sortată a1. Dacă în urma căutării locului ai în consecutivitatea deja sortată a1. 10. Se obţine un nou algoritm de sortate care se numeşte algoritm de sortate prin insertare binară (îmbinarea de cuvinte “insertarea binară” se subînţelege ca “ insertare prin împărţirea în jumătăţi”). …. …. an-1) şi. 12.

este suficient de a produce nu mai mult de p+q comparări. cum se unesc două etape a tabele ia1. bn reprezentîndu-le în formă de segmente care sînt împărţite în părţi cu grupe de elemente sortate. …. Însă algoritmul lui fon Newmann se deosebeşte de cel menţionat prin aceea că necesită mai puţine permutări ale elementelor a1. 13 Fie se dă tabela de obiecte a1. Rezultă că pentru o singură etapă de este suficient de a efectua nu mai mult de n comparări. an astfel ca la începutul tabelei să fie grupa de elemente care componentele obiectului după valoare sînt mai mari ca elementul de pe primul loc. Figura 1 ne ilustrează Fig. care conţine corespunzător р şi q elemente. an. Tot atîtea permutări trebuiesc efectuate. bn). dar şi una auxiliară b1. …. De scris programul care realizează algoritmul lui fon Newmann. an (cu toate că necesită o tabelă suplimentară b1. Spre deosebite de alţi algoritmi de sortare numai algoritmul de insertare binară necesita acelaşi număr de comparări. …. bn va conţine o grupă de elemente deja sortate. apoi primul element al consecutivităţii date şi pe urmă elementele mai mici sau egale cu 151 . …. ….nu numai tabela a1. …. Să se rearanjeze elementele tabelei a1. aт. Numărul de grupe sortate se micşorează. an şi b1. Deci se va obţine acel moment cînd tabela a1. …. Demonstraţi că algoritmul lui fon Newmann necesită aproximativ nlog2 n comparări şi tot atîte permutări. Aceasta înseamnă că tabela este sortată Pentru unirea a două grupe sortate. …. bn. …. an sau b1. ….1.

încît g şi h să fie corelate 2k-sortate. Fig.primul element.sortate. Să se realizeze acest algoritm în formă de program.2 ilustrează acest proces la primele uniri a grupelor sortate. b) numărul grupelor sortate a fişierului a se deosebesc de numărul grupelor sortate a fişierului b nu mai mult decît cu o unitate. cînd în fişierul h. 152 . Să se finalizeze descrierea acestui algoritm cercetînd etapa finală. k este un număr natural. Fie a şi b sînt fişiere. Aceasta se efectuează prin intermediul unirii grupelor sortate. dar numai un singur fişier poate avea o grupă necompletă. Numărul de comparări fiecare în parte nu trebuie să întreacă n – 1. Să se demonstreze că fişierele g şi h întradevăr vor fi corelate 2k. Rezultatele unirii se aranjează alternativ cînd în fişierul g. Vom spune că fişierele а şi b sînt corelate k-sortate. c) dacă într-un fişier numărul grupelor sortate sînt mai puţin cu o unitate decît în celălalt fişier. componentele cărora se prezintă ca grupe sortate cu numărul determinat de componente. Fişierele se prezintă în formă de segmente. ultima grupă a componentelor din fişier (tot sortată) poate conţine mai puţin de k elemente. grupul incomplet de elemente (mai puţin de k elemente) poate fi numai în fişierul cu mai multe componente. d) Componentele a două fişiere а şi b corelate k-sortate pot fi alocate în fişierele g şi h astfel. dacă a) în fiecare din fişierele a şi b primele k componente. următoarele după ele k componente etc. 14. formează grupe sortate.

Pentru cel mai rău caz numărul de comparări poate ajunge la n(n—1)/2. Acest algoritm nu utilizează tabelă suplimentară şi necesită aproximativ nlog2 n comparări ăi tot atîtea permutări de elemente. Dacă tabela conţine numai mult de un element tabela este sortată. …. Fie dată tabela a1. …. apoi fără schimbare acel element care împarte grupa întîi şi doi.2. Acestea sînt caracteristicele numerice medii. după care urmează cele mai mici sau egale cu el. Trebuie de permutat elementele a1. sortată cu ajutorul algoritmului de sortare rapidă. an astfel ca la început de tabelă să fie grupa de elemente mai mari decît elementul care se află pe primul loc în tabela iniţială.1. Să se scrie un algoritm care realizează algoritmul de sortare rapidă.10 şi determinăm rezultatele de utilizare a algoritmului de sortare rapidă pentru tabela a1. …. 153 . după care urmează grupa doi de element. 15. Numărul de comparări şi permutări nu trebuie să întreacă n . în afară de aceasta acest algoritm necesită recursie. an. Algoritmul recursiv de sortate (algoritmul de sortare rapidă) care se bazează pe transformările descrise mai sus este următorul. an în felul următor: mai întîi se formează prima grupă sortată cu ajutorul algoritmului de sortare rapidă. apoi însăşi acest element. În caz contrar efectuăm transformarea descrisă în p.Fig.

se tastează condiţia de apariţie a excepţiei. Trebuie însă înţeleasă diferenţa dintre erori (bug-uri) şi excepţii. adică testarea. şi în caz pozitiv se semnalează apariţia excepţiei prin intermediul cuvîntului cheie throw. lucrul cu blocul try{…} throw () catch()… Consideraţiile teoretice necesare: Tratarea excepţiilor Este un fenomen "natural" ca în programe să se strecoare erori. In cadrul acestui bloc. o excepţie este un obiect a cărui adresa este trimisă dinspre zona de cod unde a apărut problema către o zona de cod care trebuie s-o rezolve.Lucrarea de laborator nr. Excepţiile sînt situaţiile neaşteptate apărute în cadrul sistemului care rulează un program. Activitatea de programare implica şi acţiuni mai putini plăcute. Astfel. 154 . Programele trebuie să fie pregătite pentru a trata aceste situaţii excepţionale. 7. de diverse naturi. depanarea şi corectarea erorilor. Prelucrarea excepţiilor. Paşii care trebuiesc în general urmaţi în vederea tratării excepţiilor în cadrul programelor C++ sînt: – se identifică acele zone din program în care se efectuează o operaţie despre care se cunoaşte că ar putea genera o excepţie şi se marchează în cadrul unui bloc de tip try. Blocul try{…} throw() catch()… Scopul lucrării: familiarizarea studenţilor cu prelucrarea excepţiilor. Costurile de îndepărtare a erorilor creşte de obicei direct proporţional cu întîrzierea din cadrul procesului de dezvoltare cînd sînt descoperite. In C++ s-a realizat un mecanism facil de tratare a excepţiilor.

este apelata rutina predefinită. putînd fi declarat ca: class TipExcepţie {}. programul continuă cu instrucţiunea imediat următoare blocului try. Blocurile catch urmează un bloc try. } Sintaxa pentru throw: throw TipExcepţie. Sintaxa pentru try: try { // cod throw TipExcepţie. Sintaxa pentru catch: catch(TipExcepţie) { // cod tratare excepţie } Dacă TipExcepţie este ". #include <iostream.". În continuare vom prezenta un exemplu de program care utilizează tratarea excepţiilor. este captată orice excepţie apărută.h> #define MAXX 80 #define MAXY 25 155 .. Dacă nu există definită nici o rutină de tratare a excepţiei. TipExcepţie nu este altceva decît instanţierea unei clase vide (care determină tipul excepţiei). care încheie execuţia programului în curs.. în cadrul cărora sînt tratate excepţiile. După un bloc try.– se realizează blocuri de tip catch pentru a capta excepţiile atunci cînd acestea sînt întîlnite. După ce rutina este executată. Dacă excepţia corespunde cu una din declaraţiile de tratare a excepţiilor. pot urma unul sau mai multe blocuri catch. aceasta este apelată.

Point(unsigned x1. try { p. }.SetX(100). } unsigned GetY() { return y.GetX()<<".class Point { public: class xZero {}. } void SetX(unsigned x1) { if(x1 > 0) if(x1 < = MAXX) x =x1. unsigned y1) { x =x1. } protected: int x.SetX(5). cout<<"p. } unsigned GetX() { return x. y. else throw xZero(). y =y1. else throw xZero(). } catch(Point::xZero) 156 . else throw xOutOfScreenBounds(). 1). void main() { Point p(1. p. else throw xOutOfScreenBounds(). } void SetY(unsigned y1) { if( y1 > 0) if( y1 < = MAXY) y =y1. class xOutOfScreenBounds {}."<< endl.x successfully set to "<<p.

.) { cout << Unknown exception!\n". } catch (int i) { cout<<”Este prelucrată eroarea: ”. situaţii care trebuie evitate.{ cout << "Zero value!\n". try{ // inceputul blocului try cout<<”interiorul blocului try \n”. } 157 . } cout << “Sfirsit”. } catch(. Trebuie avută însă în vedere posibilitatea de apariţie a unor excepţii chiar în cadrul codului de tratare a unei excepţii. Temele pentru acasă: 1. Cum sînt realizate excepţiile în cadrul programelor C++ ? Exemple. // generarea erorii cout<<”Aceasta nu se va executa”. cout<<I<<”\n”.. throw 10. } } Datorită faptului că excepţia este instanţierea unei clase. } catch(Point::xOutOfScreenBounds) { cout << "Out of screen bounds!\n". Întrebări pentru verificarea cunoştinţelor: 1. prin derivare pot fi realizate adevărate ierarhii de tratare a excepţiilor. Ce rezultate vor fi obţinute la rularea următorului program: void main() { cout<<”Inceput”.

10.Temele pentru lucrări de laborator: 1.după ultimul cuvînt se pune punct. 8. 3. 9. Scrieţi un program care din trei fişiere se selectează în unul nou mai întîi numerele negative. Scrieţi un program care tipăreşte toate cuvintele diferite de ultimul cuvînt dintr-un fişier. 4. zerourile. Scrieţi un program care va tipări în ordine inversă subconsecutivitatea de numere dintre valoarea minimă şi maximă ale unei consecutivităţi de numere citită dintr-un fişier. int ani} înregistrare. Scrieţi un program care calculează numărul de elemente ale unui fişier care sînt mai mici ca valoarea medie aritmetică a tuturor elementelor acestui fişier. Scrieţi un program care compară două fişiere date. Scrieţi un program care din 100 de numere aleatoare dintr-un fişier se determină numărul maximal şi cel minimal. Subconsecutivitatea de elemente dintre numărul maximal şi cel minimal determinat să se înregistreze într-un nou fişier. Scrieţi un program care ordonează lexicografic o consecutivitate de înregistrări (dintr-un fişier) de tipul struct { char nume [30]. iar. apoi numerele pozitive pare de pe locuri impare. 6. Scrieţi un program care sortează lexicografic cuvintele dintr-un text. 7. la 5 şi la 7. Rezultatul ordonării să se înscrie într-un nou fişier. Scrieţi un program care din trei fişiere se selectează în unul nou mai întîi numerele divizibile la 3. 158 . 2. 5. Pentru fiecare element sortat să se indice numărul de repetări ale cuvîntului în textul dat. apoi numerele pozitive. Cuvintele din fişier sînt separate prin virgulă. Scrieţi un program care din două fişiere ordonate descrescător se vor uni în unul nou în care se va păstra ordinea descrescătoare de sortare.

Д. Болски. Радио и cвязь. Scrieţi un program care formează un fişier nou din cel dat după următoarea legitate: elementele fişierului nou se obţine din inversul numerelor din fişierul dat. Н Джехани. Scrieţi un program care din două fişiere ordonate crescător se vor uni în unul nou în care se va păstra ordinea crescătoare de sortare. 14. Să se determine elementele mai mari ca cel minimal şi mai mici ca numărul maximal. Т.1990. 4. 12.Р Булатова. А Юлин. Lungimea rîndului de caractere în fişierul nou are lungimea de 60 de caractere. restul rîndului din fişierul dat se scrie din rînd nou.– Москва.– Москва. Программирование на языке Си. Dacă în rîndul dat se depistează punctul. М. Б. И. Приглашение к Си.. Уэйт. 2. Scrieţi un program care efectuează reformatarea unui fişier textual în felul următor. 3. В. Справочник:. 15. с англ Москва. 13. Bibliografia 1. 5.1988.– Пер.– Москва. Керниган.– Минск. 16. 159 .1985. Ритчи. 1988. 1988. Радио и cвязь. с англ. Язык Си (руководство для начинающих). Пер. Язык программрования Си. Scrieţi un program care calculează suma înmulţirii numerelor vecine pe pe locuri pare dintr-un fişier dat. М. Выcшая школа. Язык программирования Cи. Scrieţi un program care determină numărul maximal şi cel minimal din numerele unui fişier dat.11. Valorile elementelor sînt cuprinse între 1 şi 100. Scrieţi un program care determină frecvenţa cu care a fost generat fiecare element în fişierului creat.

– Bucureşti. – Москва.Трой. Programarea obiect orientata.– Москва. 18. M. Costea. Teora. Радио и cвязь. Petrion. Turturea. 2000. 20. C++ limbaj de programare. C++. Mateescu. D. – Москва.Teora. – Cluj. Радио и cвязь. ed. G. ed. 21. 1993.– Москва. I. А. –Bucureşti. 1994. 7. M. Программирование на языке Cи для персонального компьютера IBM PC. Iniţiere în limbajul C. BHV. D. ed. Tehnologia orientată pe obiecte. Голуб С и С++ . 11. 1996. Шилдт. Бьерн Страустрап. ed.A. ed. 1994.А. Cojocaru. Tănase. Negrescu. Teora. Язык Программирования С++. I. Программируем на Си. Маслов. 1997. D. Крис. Р. Н. 1996. Радио и cвязь. 15. –Bucureşti. Tehnologia orientată pe obiecte. 2002.М. Catrina. 1992. 9. Санкт Петербург. – Bucureşti. 1991. Popovici. Учимся программировать на языке С++. Teora. O. Г.BINOM. Somnea. 1995. 1998. Popovici. M. Aplicaţii. 19. Радио и связь. Стариков. D. M.. 12. Самоучитель С++. Teora. 1997. I. 16. Popovici. Мобильность программ и особенности реализаций языка Си. Popovici. Introducere în C++. –Пер. D. Дж. 6. Programarea aplicatiilor Windows în limbajul C. 10. Turturea. Aplicaţii. Turbo C++. D. Радио и cвязь. C++. 13. L. 160 . Правила программирования. Tănase. 1996. – Москва. Teora. 1991. – Bucureşti.– Москва.Дуглас. – Bucureşti. L. А. I. 8. Введение в язык программирования Си. Джонс. 1996 17. C++. 14. D. Iniţiere în limbajul C.– Bucureşti. Ю. с англ. A. Радио и cвязь. ed. ed.

Sign up to vote on this title
UsefulNot useful