curs program are c++

208
CPP Cuprins Cap. 1 Reprezentarea informaţiilor 1.1 Reprezentarea numerelor întregi în sistemul binar 1.2 Deplasarea numeral binare cu semn 1.3 Reprezentarea numerelor reale 1.4 Reprezentarea caracterelor Cap. 2 Constante, variabile şi expresii 2.1 Tipuri fundamentale 2.2 Variabile 2.3 Modificatori de tip 2.4 Operatorul typedef 2.5 Constante 2.6 Constante cu nume 2.7 Expresii aritmetice 2.8 Tablouri 2.9 Instrucţiunea de atribuire 2.10 Prototipuri de funcţii 2.11 Operaţii de intrare / ieşire 2.12 Funcţia main 2.13 Execuţia unui program 2.14 Operatorii ++ şi - - 2.15 Operaţii cu numere întregi la nivel de bit 2.15.1 Operatori de depasare 2.15.2 Operaţii logice la nivel de bit Cap. 3 Structuri de control fundamentale 3.1 Algoritme 3.2 Expresii relaţionale 3.3 Expresii booleene 3.4 Operatorul do-while 3.5 Operatorul while 3.6 Operatorul for 3.7 Operatorul if 3.8 Operatorul ? 3.9 Operatorul switch 3.10 Operatorul , Cap. 4 Funcţii

Upload: andrei-dumitrescu

Post on 04-Jul-2015

230 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: Curs Program Are C++

CPP

CuprinsCap. 1 Reprezentarea informaţiilor

1.1 Reprezentarea numerelor întregi în sistemul binar1.2 Deplasarea numeral binare cu semn1.3 Reprezentarea numerelor reale1.4 Reprezentarea caracterelor

Cap. 2 Constante, variabile şi expresii2.1 Tipuri fundamentale2.2 Variabile2.3 Modificatori de tip2.4 Operatorul typedef2.5 Constante2.6 Constante cu nume2.7 Expresii aritmetice2.8 Tablouri2.9 Instrucţiunea de atribuire2.10 Prototipuri de funcţii2.11 Operaţii de intrare / ieşire2.12 Funcţia main2.13 Execuţia unui program2.14 Operatorii ++ şi - -2.15 Operaţii cu numere întregi la nivel de bit

2.15.1 Operatori de depasare2.15.2 Operaţii logice la nivel de bit

Cap. 3 Structuri de control fundamentale3.1 Algoritme3.2 Expresii relaţionale3.3 Expresii booleene3.4 Operatorul do-while3.5 Operatorul while3.6 Operatorul for3.7 Operatorul if3.8 Operatorul ?3.9 Operatorul switch3.10 Operatorul ,

Cap. 4 Funcţii4.1 Definirea funcţiilor4.2 Pasarea parametrilor funcţiilor4.3 Recursivitatea4.4 Sabloane de funcţii

Cap. 5 Pointeri şi referinţe5.1 Pointeri5.2 Referinţe5.3 Parametri funcţiilor

Page 2: Curs Program Are C++

5.4 Pointeri la funcţii5.5 Declalararea variabilelor tip pointer la funcţie5.6 Pointeri şi tablouri unidimensionale5.7 Siruri tip C5.8 Pointeri şi tablouri multidimensionale 5.9 Parametrii funcţiei main

Cap. 6 Fişiere tip C6.1 Fişiere text

6.1.1 Funcţii intrare / ieşire cu format6.1.2 Funcţii intrare / ieşire tip character

6.2 Fişiere binareCap. 7 Structuri tip CCap. 8 Clase

8.1 Definirea unei clase8.1.1 Definirea unei clase8.1.2 Pointerul this8.1.3 Spaţii de nume

8.2 Constructori şi destructori8.3 Funcţii prietene8.4 Fişiere standard în C++

Cap. 9 Supraîncărcarea operatorilor9.1 Supraîncărcarea operatorului de atribuire9.2 Supraîncărcarea operatorilor aritmetici9.3 Supraîncărcarea operatorilor << şi >>

Cap. 10 Moştenirea10.1 Pointeri la obiecte10.2 Moştenirea10.3 Funcţii virtuale. Polimorfism10.4 Date şi funcţii statice

Cap. 11 Fişiere tip CPP11.1 Fişiere text

11.1.1 Funcţii intrare / ieşire cu format11.1.2 Funcţii intrare / ieşire tip caracter

11.2 Fişiere binareCap. 12 Siruri tip C++Cap. 13 Tratarea excepţiilor

13.1 Excepţii13.2 Excepţii lansate de funcţii13.3 Excepţii standard

Cap. 14 Aplicaţii14.1 Funcţii de timp14.2 Fire de execuţie

Cap. 15 Biblioteca de şabloane standard15.1 Funcţii generice15.2 Vectori15.3 Liste

Page 3: Curs Program Are C++

Cap 1. Reprezentarea informaţiilor

1.1 Reprezentarea numerelor întregi în sistemul binar

Un număr natural se reprezintǎ ca o colecţie de cifre. In sistemul poziţional, poziţia unei cifre determină ponderea cifrei în mărimea numărului. Fie numărul

Mărimea numărului natural corespunzător este:

unde: r>1 este baza, n este numărul de cifre, iar este cifra de pe poziţia i. Avem totdeauna . In sistemul zecimal cifrele utilizate sunt 0, 1, 2, …, 9. In sistemul binar cifrele utilizate sunt 0 şi 1, în sistemul octal 0, 1, 2, …, 7, iar în sistemul hexazecimal cifrele utilizate sunt: 0, 1, 2, …, 9,A,B,C,D,E,F unde: A=10, B=11, C=12, D=13, E=14 şi F=15. Cifrele sistemului binar se numesc biţi.

Conversia zecimal-binarǎ

Fie un numǎr natural reprezentat în sistemul binar

In partea dreaptă avem un polinom de puteri ale lui 2. Coeficientul este 0 sau 1. Din expresia de mai sus a numǎrului se observǎ cǎ cifrele , , etc se pot obţine ca resturile impǎrţirilor repetate ale numǎrului N cu 2. Vom nota:

şi vom scrie:

de unde deducem:

Vom scrie

de unde deducem:

Dupǎ n astfel de operaţii vom avea:

unde:

După cum se observǎ, resturile obţinute reprezintǎ chiar cifrele numǎrului binar.Exemplu. Sǎ convertim numarul 14 din baza 10 în baza 2.

Page 4: Curs Program Are C++

Avem deci:

Avem:

de unde obţinem:

In final:

de unde obţinem:

deci reprezentarea numǎrului 14 în binar este:

In acelaşi fel obţinem:

Algoritmul de conversie a unui numǎr din baza 10 în baza 2 este urmǎtorul:1.2.3. cât timp {

}Resturile obţinute sunt cifrele numǎrului binar, primul rest fiind cifra cea mai puţin semnificativǎ.Conversia din baza 10 în baza 8 sau 16 se face prin împǎrţiri repetate cu 8 şi respectiv 16.Unitatea de bază a informaţiei în calculator este un octet sau byte, ce cuprinde 8 cifre binare (biţi). Numerele întregi se reprezintǎ în calculator pe 8, 16, 32 sau 64 de biţi. Având un număr în baza 2, pentru reprezentarea sa în baza 16 se grupează câte 4 cifre binare.Exemplu.

Reprezentarea în baza 16 este importantǎ deoarece un octet poate fi reprezentat prin două cifre hexazecimale.Având un numǎr în baza 2, pentru reprezentarea în baza 8 se grupează câte 3 cifre binare.Exemplu.

Page 5: Curs Program Are C++

Pentru verificare

Conversia unui număr din baza 16 în baza 2 se face reprezentând fiecare cifrǎ hexazecimalǎ prin 4 cifre binare.Conversia unui numǎr din baza 8 în baza 2 se face convertind fiecare cifrǎ octalǎ prin 3 cifre binare. Pentru conversii de numere între bazele 2, 8, 10 şi 16 şi operaţii cu numere în aceste baze se poate folosi aplicaţia Calculator a sistemului de operare Windows.

Reprezentarea numerelor binare cu semn

In cazul numerelor binare cu semn, bitul cel mai semnificativ este bitul de semn. El este 0 pentru numere pozitive şi 1 pentru numere negative. Există trei reprezentǎri ale numerelor binare cu semn.

Reprezentarea în mǎrime şi semn

Numǎrul pozitiv X se reprezintǎ ca:

Numǎrul negativ X se reprezintǎ ca:

Exemple. Vom considera numere întregi reprezentate pe 8 biţi, un bit de semn şi 7 biţi ai numǎrului:

Număr zecimal Reprezentare binarǎ Reprezentare hexazecimalǎ13 0000 1101 0D-13 1000 1101 8D25 0001 1001 19-7 1000 0111 87

127 0111 1111 7F-127 1111 1111 FF

Gama numerelor întregi reprezentabile pe un octet în mǎrime şi semn este [-127, 127].Putem scrie formula de reprezentare a numerelor binare în mǎrime şi semn ca:

unde coeficientul are valoarea 0 sau 1. Primul bit va fi interpretat ca şi coeficientul lui .

Reprezentarea în complement faţǎ de 1

Numǎrul pozitiv X se reprezintǎ în complement faţǎ de 1 ca:

Page 6: Curs Program Are C++

Numǎrul negativ X se reprezintǎ ca:

unde: . Pentru a reprezenta un numǎr negativ în complement faţǎ de 1 complementǎm toate cifrele lui.Exemple de numere reprezentate în complement faţǎ de 1 pe un octet.

Număr zecimal Reprezentare binarǎ Reprezentare hexazecimalǎ15 0000 1111 0F-15 1111 0000 F0-19 1000 1100 8C19 0001 0011 13

Reprezentarea numerelor binare în complement faţǎ de 2

Numerele pozitive se reprezintǎ în complement faţǎ de 2 ca:

Numerele negative se reprezintǎ în complement faţǎ de 2 ca:

unde: .Exemple de numere reprezentate în complement faţǎ de 2 pe un octet.

Număr zecimal Reprezentare binarǎ Reprezentare hexazecimalǎ13 0000 1101 0D-13 1111 0011 F3-7 1111 1001 F9

127 0111 1111 7F-127 1000 0001 81

Menţionǎm cǎ în calculatoare numerele întregi se reprezintǎ în general în complement faţǎ de 2. Considerăm formula ce reprezintǎ un număr negativ în complement faţǎ de 2. Avem relaţia:

In consecinţǎ, putem scrie:

1.2 Deplasarea numerelor binare cu semn

Page 7: Curs Program Are C++

Inmulţirea unui numǎr binar cu semn cu sau este echivalentǎ cu deplasarea numǎrului binar la stânga sau la dreapta cu o cifra. La deplasarea numărului binar bitul de semn rǎmane neschimbat.

Cazul numerelor pozitive.

Se deplaseazǎ toate cifrele numărului, iar cifrele adaugate sunt zerouri. Regula este evidentǎ din formula de reprezentare a numerelor pozitive. Fie numărul pozitiv

şi fie numărul înmulţit cu 2

de unde se deduce:

Am presupus cǎ prin înmulţirea cu 2 a numǎrului X nu apare depǎsire. In acelaşi fel se aratǎ cǎ regula este valabilǎ pentru împǎrţirea cu 2.Exemple. Să deplasǎm numărul 28 cu o cifrǎ binarǎ la dreapta şi la stânga.

Număr zecimal Număr binar Număr hexazecimalǎ28 0001 1100 1C14 0000 1110 0E56 0011 1000 38

Numere negative reprezentate în complement faţǎ de 2

Regula de înmulţire a acestor numere cu sau este urmǎtoarea:

la deplasarea la stânga cifrele adǎugate sunt zerouri, la deplasarea la dreapta cifrele adaugate sunt unuri, reamintim ca bitul de semn rǎmâne neschimbat.

Exemplu.

Numǎr zecimal Numǎr binar Numǎr hexazecimal-28 1110 0100 E4-14 1111 0010 F2-56 1100 1000 C8

1.3 Reprezentarea numerelor reale

Numerele reale se reprezintǎ în calculator în virgulǎ mobilǎ. Numarul real R este pus sub forma

Page 8: Curs Program Are C++

unde: f este un număr subunitar (mantisa), b este baza iar e este exponent sau caracteristicǎ. Pentru exemplificare vom considera baza zece.Exemple de reprezentare a numerelor reale.

Mantisa f are totdeauna un număr finit de cifre şi în plus:

In continuare, pentru exemplificare vom considera cǎ mantisa are 8 cifre. Deoarece mantisa are un numǎr finit de cifre, în unele cazuri se pierd cifrele mai puţin semnificative din numǎr.Exemplu. Numǎrul

se reprezintǎ ca:

Pentru mǎrirea preciziei calculelor se cautǎ ca prima cifră din mantisǎ sǎ fie diferitǎ de zero (vezi exemplul de mai sus). Numerele în această formă se numesc normalizate.

Efectuarea operaţiilor cu numere în virgulǎ mobilǎ

Pentru adunare numerele se aduc la acelaşi exponent (numǎrul mai mic la exponentul celui mai mare) şi apoi se adunǎ mantisele. Aducerea numǎrului mai mic la exponentul celui mai mare poate duce la pierderea cifrelor cel mai puţin semnificative din mantisǎ.Exemplu. Sǎ efectuăm adunarea:

12.48+0.35Primul numǎr este reprezentat ca:

iar al doilea

Pentru adunare se aduc numerele la acelaşi exponent0.1248 20.035 2

şi se adunǎ mantisele.0.1283 2

Vom prezenta un exemplu în care se pierd cifrele cele mai puţin semnificative din rezultat. Sǎ adunǎm numerele:

1237.55 + 0.000425 Numerele sunt reprezentate astfel:

0.3245 2

-0.35 -1

0.1248 2

0.35 0

0.123755 4

0.425 -3

Page 9: Curs Program Are C++

La adunare mantisa celui de-al doilea numǎr este deplasatǎ la dreapta cu 7 cifre. Considerând lungimea mantisei de 8 cifre, se pierd doua cifre semnificative din al doilea numǎr la aducerea la acelaşi exponent.

0.123755 4

0.00000004 4Rezultatul adunǎrii este:

0.12375504 4Observǎm că s-au pierdut douǎ cifre, cele mai puţin semnificative. Operaţia de scǎdere se face aducând numerele la acelaşi exponent şi scǎzând mantisele.La înmulţire se adunǎ exponenţii şi se înmulţesc mantisele. La împǎrţire se scad exponenţii şi se împart mantisele. Dupǎ efectuarea unei operaţii aritmetice se cautǎ ca prima cifra din mantisǎ sǎ fie diferită de zero, pentru o precizie maximǎ a rezultatului (normalizare).Pentru creşterea preciziei operaţiilor, în unitatea de calcul mantisa mai are încǎ o cifrǎ. Operaţiile se fac deci cu o mantisǎ mai lungǎ, dupǎ care se reţin opt cifre din rezultat.Exemplu. Fie de efectuat operaţia de scădere:

103.45678 – 23.456789Numerele se reprezintǎ astfel:

0.10345678 3

0.23456789 2Dacǎ mantisa are doar opt cifre semnificative, când deplasǎm la dreapta cu o cifrǎ, mantisa celui de-al doilea număr, pierdem o cifră semnificativǎ.

0.02345678 3Rezultatul scăderii celor douǎ numere este:

0.08 3şi dupǎ normalizare obtinem rezultatul 80. Presupunem acum că în unitatea de calcul se lucreazǎ cu o mantisǎ cu nouǎ cifre semnificative. In acest caz cele douǎ numere sunt

0.103456780 3

0.023456789 3După scǎderea celor două numere obţinem:

0.079999991 3iar dupǎ normalizare obţinem rezultatul corect:

0.79999991 2Mentionǎm cǎ exponentul poate fi pozitiv sau negativ. Pentru a nu avea douǎ semne, unul pentru numǎr şi altul pentru exponent, se adaugǎ un numǎr constant la exponent astfel ca sǎ fie totdeauna pozitiv. De exemplu, dacǎ gama exponentului este [-63, 63] se adaugǎ valoarea 63 astfel încât gama exponentului va fi [0, 126].In calculatoarele personale numerele reale se reprezintǎ în virgulǎ mobilǎ în felul urmǎtor

R = semn * 1.mantisa * 2 exponent

In reprezentarea în virgulǎ mobilă scurtǎ (simpla precizie) numǎrul se reprezinta pe 32 biţi. Domeniul exponentului este [-127, 127]. La exponent se adaugǎ totdeauna valoarea 127 astfel încat exponentul are domeniul [0,254]. Bitul de semn are valoarea 0 pentru numere pozitive şi 1 pentru cele negative. Bitul de semn ocupă bitul 31, exponentul

Page 10: Curs Program Are C++

ocupǎ biţii 23-30 iar mantisa biţii 0-22. Gama numerelor reale ce se pot reprezenta în acest format este

iar mantisa corespunde la opt cifre zecimale.Exemple. Fie numărul . Bitul de semn este zero iar exponentul 0+127. In consecinţă, primii nouă biti din număr sunt

001111111Numărul R va fi reprezentat în hexazecimal ca 3F800000.Fir numărul . Bitul de semn este zero iar exponentul este -1+127=126. Primii nouă biţi din număr sunt

001111110Numărul se reprezintă în hexazecimal ca 3F000000.Menţionǎm cǎ dacǎ exponentul este 255 iar mantisa este 0, numărul este Dacǎ exponentul este 255 şi mantisa este diferitǎ de zero numărul este NaN (not a number). Valoarea NaN apare atunci când efectuǎm urmatoarele operaţii

sau când un operand este NaN.In reprezentarea în virgulǎ mobilă lungǎ (dublǎ precizie) numǎrul se reprezintă pe 64 biţi. Domeniul exponentului este [-1023, 1023]. La exponent se adaugǎ cantitatea 1023, deci exponentul are domeniul [0, 2046]. Bitul de semn este bitul 63, exponentul ocupǎ biţii 52-62 iar mantisa ocupă biţii 0-51. Gama numerelor reale ce se pot reprezenta este

1.4 Reprezentarea caracterelor

Unul dintre sistemele de reprezentare a caracterelor este codul ASCII în care fiecare caracter este reprezentat pe un octet. De exemplu caracterul A are codul hexazecimal 41, caracterul a are codul hexazecimal 61, cifra 0 are codul hexazecimal 30, etc. Codurile ASCII ale caracterelor sunt prezentate în tabela de mai jos.

y\x 0 1 2 3 4 5 6 70 Nul Dle 0 P p1 Soh dc1 ! 1 A Q a q2 Stx dc2 “ 2 B R b r3 Etx dc3 # 3 C S c s4 Eot dc4 $ 4 D T d t5 Enq Nak % 5 E U e u6 Ack Syn & 6 F V f v

Page 11: Curs Program Are C++

7 Bel Elb ‘ 7 G W g w8 Bs Can ( 8 H X h x9 Ht Em ) 9 I Y i y10 Lf Sub * : J Z j z11 Vt Esc + : K [ k {12 Ff Fs , < L \ l |13 Cr Gs - = M ] m }14 So Rs . > N ^ n ~15 Si Us / ? O _ o del

Tabelul 1.1 Codul ASCII

Codul zecimal unui caracter este 16*x+y. De exemplu caracterul A are codul zecimal 65 sau, echivalent, codul hexazecimal 41.Un alt sistem de reprezentare a caracterelor este UNICODE. în varianta UNICODE-16 fiecare caracter este reprezentat pe 2 octeţi.

Cap 2. Constante variabile şi expresii

2.1 Tipuri fundamentale

In matematică variabilele se clasifică după tipul lor. Tipul unei variabile este mulţimea valorilor pe care le poate lua acea variabilă şi operaţiile ce se pot efectua cu acele valori. Tipul unei variabile este făcut explicit cu o declaraţie de tip. Tipurile predefinite în limbajul C++ sunt cele din tabelul 1.

Page 12: Curs Program Are C++

Tabelul 1. Tipuri de bază ale limbajului C++

Tip Dimensiune în octeţi

Domeniu de valori

Operaţii

int Numere întregi 4 ]122[ 3131 +-*/%

float Numere reale în virgulă mobilă scurtă

4 +-*/

double Numere reale în virgulă mobilă lungă

8 +-*/

char Caractere în cod ASCII şi numere întregi pe un octet

1 +-*/%

wchar_t Numere pozitive şi caractere UNICODE pe doi octeţi

2 +-*/%

bool Valoare booleană 1 false,true and, or, not

Pentru fiecare tip există constante predefinite ce dau limitele minima şi maximă ale domeniului de valori.Menţionăm că, pentru tipurile char şi int, primul bit este utilizat pentru semn, ceilalţi biţi sunt biţi ai numărului. In cazul tipului bool valoarea false este reprezentată prin zero iar true prin unu.

2.2 Variabile

Numele unei variabile este format din cifre, litere şi caracterul _ (underscore), şi începe totdeauna cu o literă sau caracterul _. Literele mari sunt diferite de cele mici. Instrucţiunea de declarare a tipului are forma

tip listă de variabile;Lista de variabile este formată din nume de variabile separate de virgule. Diagrama sintactică este cea de mai jos.

De exemplu, instrucţiunileint a;char b;

declară o variabilă a de tipul int şi o variabilă b de tipul char. Conform instrucţiunii de declarare a tipului de mai sus, mai multe variabile de acelaşi tip pot fi declarate cu o singură instrucţiune, scriind numele lor separate de virgule. De exemplu, instrucţiunea

float f1, f2, f3;

Page 13: Curs Program Are C++

declară trei variabile de tipul float. O variabilă poate fi iniţializată la declararea tipului ei, conform următoarei diagrame sintactice

De exemplu, instrucţiuneadouble d = 1 + sin(2.4);

declară o variabilă de tip double pe care o iniţializează la valoarea 1+sin(2.4).Menţionăm că tipul bool nu este definit în limbajul C.

2.3 Modificatori de tip

Modificatorii de tip schimbă domeniul de valori pe care le poate lua o variabilă de tip int sau char. Aceşti modificatori sunt:

unsigned. La utilizarea acestui modificator, valorile variabilelor sunt pozitive. Toţi biţii sunt biţi ai numărului. Tipul unsigned int are domeniul de valori

, iar tipul unsigned char are domeniul de valori signed. Valorile variabilelor sunt numere cu semn. Primul bit este bit de semn. short. Se aplică tipului int. De exemplu, tipul short int are domeniul de valori

, iar tipul unsigned short int are domeniul de valori . Tipul wchar_t este tipul unsigned short int predefinit.

long

2.4 Operatorul typedef

Operatorul typedef permite să definim tipuri de date bazate pe tipuri existente. Forma instrucţiunii este

typedef tip-existent tip-nouExemple. Instructiunea

typedef float REAL32defineşte tipul REAL32 ce poate fi utilizat în locul tipului float.In limbajul C nu există tipul predefinit bool. Putem să definim tipul bool folosind tipul char

typedef char bool

2.5 Constante

Tipurile de constante din limbaj corespund tipurilor de date. constantele întregi sunt numere întregi, pozitive sau negative. Ele nu pot începe cu

cifra 0. Exemple de constante întregi sunt275, -325, +12

Page 14: Curs Program Are C++

constante hexazecimale sunt numere întregi reprezentate în baza 16. Ele încep cu 0x sau 0X. Exemple de constante hexazecimale

0xE sau 0xe sau 0Xe sau 0XE care reprezintă valoarea zecimală 14-0xf sau -0xF, reprezintă valoarea zecimală -15.

constante octale sunt numere întregi reprezentate în baza 8. Ele încep obligatoriu cu 0. Exemple. Constanta octală 016 reprezintă numărul zecimal 14 (deoarece

) iar constanta octală -014 reprezintă numărul zecimal -12 (deoarece )

constante reale în virgulă mobilă scurtă. Partea subunitară este separată de cea întreagă prin punct zecimal. Exemple.

1.5 -4.23 27.0 Constantele reale pot avea exponent, un număr întreg ce reprezintă puterea lui zece cu care se înmulţeşte constanta. De exemplu numărul se scrie ca

2.31e1 sau 2.31e+1 sau +2.31E1Numărul real se scrie ca şi constantă reală

-41.2e-1 sau -4.12 constante reale în virgulă mobilă lungă. Se scriu ca şi constantele reale în virgulă

mobilă scurtă şi cu sufixul L sau l. De exemplu, numărul real 1,5 se scrie ca şi constantă în virgulă mobilă lungă

1.5L sau 15e-1L constante de tip character reprezintă un character al codului ASCII scris între

apostrofuri. De exemplu, caracterul ASCII a se scrie‘a’ sau echivalent, ca şi constante întregi, 0x61 sau 97

Caracterul ASCII A se scrie ‘A’ sau echivalent, 0x41 sau 65.

Un alt mod de a defini constante tip character este de a le scrie sub forma ‘\xhh’ unde h este o cifră hexazecimală. Numărul hexazecimal hh reprezintă echivalentul hexazecimal al caracterului în codul ASCII. De exemplu, caracterele ‘a’ şi ‘A’ se reprezintă ca şi ‘\x61’ şi respective ‘\x41’. Caracterele speciale ale codului ASCII se reprezintă folosind secvenţa de evitare

‘\n’ CR‘\r’ LF‘\t’ tab orizontal‘\”’ ghilimele‘\’’ apostrof‘\\’ backslash

constante tip şir de caractere. Ele reprezintă un şir de caractere ASCII scris între ghilimele. De exemplu

“abcd”Un şir de caractere este reprezentat de codurile ASCII ale caracterelor pe câte un octet, urmate de un octet ce conţine valoarea 0. Caracterele speciale se reprezintă în interiorul unui şir folosind secvenţa de evitare. De exemplu

“ab\”c”“a\\bc”“abc\n”

Page 15: Curs Program Are C++

Menţionăm diferenţa între constanta tip caracter ‘a’ şi constanta tip şir de caractere “a”. Constanta tip caracter ‘a’ se reprezintă pe un singur octet, constanta “a” se reprezintă pe doi octeţi.

constante tip caracter UNICODE corespund tipului wchar_t. Ele se definesc sub forma L’\xhhhh’ unde h reprezintă o cifră hexazecimală.

constante booleene sunt true şi false şi se reprezintă prin valorile unu şi respective zero.

2.6 Constante cu nume

In program putem defini constante cu nume în următoarele feluri utilizarea directivei define . Compilatoarele limbajelor C şi C++ au un

preprocessor care modifică programul sursă înainte de compilare. Preprocesorul citeşte diversele directive şi modifică programul sursă corespunzător acestora. Directiva define ce defineşte o constantă are forma

# define nume valoareunde valoare este interpretată de preprocessor ca un şir de caractere. De exemplu, directiva

# define PI 3.14are ca efect înlocuirea numelui PI în program cu şirul de caractere 3.14

enumerări. O enumerare defineşte un tip întreg şi valorile asociate acelui tip. Instrucţiunea enum de declarare a unui tip are forma

enum numetip {listă de nume};Diagrama sintactică corespunzătoare este următoarea

De exemplu,enum CLRX {aaa, bbb, ccc};

defineşte trei constante aaa, bbb, ccc, prima are valoarea zero şi fiecare constantă următoare are o valoare mărită cu unu.

aaa = 0bbb = 1ccc = 2

Page 16: Curs Program Are C++

In program constantele sunt înlocuite cu valorile lor. Valoarea fiecărei constante poate fi specificată printr-o expresie întreagă constantă. De exemplu

enum cstval {ppp = 2, dd, cc = -1, nn = 2 + 3 * 4};defineşte constantele

ppp = 2dd = 3cc = -1nn = 14

Putem defini variabile corespunzând tipului definit prin enum. Aceste variabile pot primi ca valori doar constantele definite în instrucţiunea enum. Fie de exemplu tipul enumx definit mai jos

enum enumx = {ena, enb, enc};Instrucţiunea următoare defineşte variabila z de tipul enumx

enumx z;Putem atribui o valoare variabilei z astfel

z = ena;Instrucţiunea enum are şi o formă mai simplă

enum {listă de nume};care defineşte doar constante şi nu un tip.

utilizarea cuvantului cheie const. O asemenea instrucţiune are formaconst tip nume = valoare;

unde tip este un tip al limbajului iar valoare este o expresie constantă. De exemplu, instrucţiunile

const int a = 7;const char b = ‘a’;const float c = 2.5e-2 * 4.14;const float x = sin(0.2) + cos(1.5);const float z = log(12.5) / 2.3;

definesc constantele a, b, c, x şi z, ce au valorile specificate în instrucţiune.

2.7 Expresii aritmetice

Expresiile aritmetice sunt formate din constante, variabile şi funcţii. Operatorii sunt +, - * şi /, şi în cazul operanzilor de tip întreg, şi % (restul împărţirii a două numere întregi). Restul împărţirii a două numere întregi, a şi b, se defineşte astfel

a % b = a – (a / b) * bDe exemplu,

15 % 4 = 311 % 5 = 2

Pentru gruparea termenilor se folosesc paranteze rotunde ( şi ). De exemplu, expresia

se scrie ca(a – b) / (a + b)

Page 17: Curs Program Are C++

iar expresia

se scrie ca(a + b * x + c * x * x) / (2 + m*m)

Funcţiile matematice standard uzuale ale limbajelor C şi C++ sunt cele de mai jos

acos cos exp ceil fabs powasin sin log floor sqrtatan tan log10

Funcţia floor(x) calculează valoarea (cel mai mare număr întreg cuprins în x), iar funcţia ceil(x) calculează valoarea (cel mai mic număr întreg mai mare ca x). Toate funcţiile de mai sus au argumente de tip double şi rezultatul de tip double. Funcţia pow are prototipul

double pow(double a, double b)şi calculează expresia . Apelarea unei funcţii se face scriind numele funcţiei ca termen într-o expresie urmat în paranteze de parametrii actuali. Exemple de expresii aritmetice şi scrierea lor sunt prezentate mai jos. Vom presupune că variabilele din aceste expresii au fost declarate în prealabil de tip double şi au primit valori.

(a*cos(x)*cos(x)+b*sin(x))/(2.75+fabs(x))

(exp(x)+exp(-2*x))/5

log10(fabs(x)+2)+log(fabs(1+cos(x)))Menţionăm că în expresii putem folosi orice fel de constante întregi, reale sau character. De exemplu, expresia a + 27 se poate scrie

a + 27sau

a + 0x1bsau

a + 033Constantele hexazecimală 0x1b şi octală 033 reprezintă valoarea zecimală 27. Expresia x + 100 se poate scrie

x + 100sau

x + 0x64sau

x + ‘d’Codul ASCII al caracterului d este 0x64. La evaluarea unei expresii constantele hexazecimale, octale sau character sunt convertite în numărul întreg corespunzător. Evaluarea expresiilor aritmetice se face ţinand cont de priorităţile operatorilor şi de asociativitatea lor.

Page 18: Curs Program Are C++

Prioritatea operatorilor Asociativitate+ unar, - unar* / % Asociativ la stânga+ - Asociativ la stânga

Conform tabelei de mai sus operatorii *, / şi % au o prioritate mai mare decât + şi -Asociativitatea la stânga a operatorilor înseamnă următorul mod de execuţie. Expresia

a / b / c este interpretată ca

(a / b) / cReamintim că, pentru a modifica ordinea de execuţie o operaţiilor, se utilizează paranteze rotunde.Deoarece în expresii intervin operanzi de diverse tipuri, se fac conversii. Regulile după care se fac aceste conversii sunt următoarele:

Tipul float se converteşte la double Tipurile char şi short int se convertesc la int Tipurile unsigned char şi unsigned short int se convertesc la unsigned int Dacă în expresie există un operand de tipul long int, tipul int şi unsigned int se

convertesc la long int. Tipul expresiei se determină conform tabelei de mai jos

int long doubleint int long doublelong long long doubledouble double double double

Menţionăm în final că tipurile char, short int şi variantele lor unsigned char, unsigned short int şi float sunt doar pentru memorare. Calculele se efectuează doar cu variabile de tip int, long int şi double.

Valoarea unei expresii poate fi convertită într-un tip diferit dacă este nevoie. Diagrama sintactică este

(tip) expresieDe exemplu, dacă i este o variabilă întreagă cu valoarea 7 şi f este o variabilă de tip double cu valoarea 3.47, expresia

(i + f) % 2nu este corectă deoarece are tipul double. Expresia

(int)(i + f) % 2are tipul int şi este corectă, rezultatul ei este 5. In limbajul C++ este posibilă încă o formă de convertire a expresiilor, cu diagrama sintactică

tip(expresie)De exemplu, conversia expresiei anterioare la tipul int se poate scrie

int(i + f) Menţionăm că se pot face conversii în toate tipurile standard existente în limbaje.

2.8 Tablouri

Page 19: Curs Program Are C++

Un tablou este o mulţime de elemente de acelaşi tip. Tablourile sunt tipuri structurate simple. Instrucţiunea de declarare a unui tablou cu o dimensiune este următoarea

tip nume [ întreg] ;unde întreg reprezintă numărul de elemente ale tabloului. Elementele tabloului sunt nume[0], nume[1], …, nume[întreg – 1]. De exemplu instrucţiunea

int a[10];declară un vector cu zece elemente de tip int. Elementele vectorului sunt a[0], a[1], …, a[9]. Un tablou poate fi iniţializat la declararea sa scriind valorile elementelor între accolade, {}, şi separate de virgule. Exemple de instrucţiuni ce declară tablouri şi le atribuie valori

float x[3] = {1.32, -2.15, 4.45};char c[4] = {‘a’, ‘b’, ‘c’, ‘x’};

In cazul în care lista de valori este mai scurtă decat numărul de elemente declarate, ultimele elemente sunt iniţializate cu zero. In cazul iniţializării unui tablou, la declararea lui putem omite numărul de elemente al tabloului. De exemplu, putem scrie

char x[] = {‘#’, ‘>’, ‘m’};Compilatorul calculează dimensiunea tabloului din numărul de valori utilizate pentru iniţializare. Instrucţiunea precedentă este echivalentă cu instrucţiunea

char x[3] = {‘#’, ‘>’, ‘m’};Menţionăm că un vector de caractere poate fi iniţializat cu o constantă tip şir de caractere. De exemplu, putem scrie

char s[] = “abc”;Reamintim că o constantă şir de caractere este terminată printr-un octet 0, deci vectorul declarat are 4 componente, ‘a’, ‘b’, ‘c’ şi 0. Instrucţiunea precedentă este echivalentă cu

char s[4] = “abc”;sau cu instrucţiunea

char s[4] = {‘a’, ‘b’, ‘c’ , ‘\0’};Un tablou poate avea oricâte dimensiuni. Diagrama sintactică a declaraţiei unui tablou este următoarea

De exemplu, instrucţiuneafloat b[7][3]

declară o matrice cu şapte linii şi trei coloane. Elementele matricei sunt b[0][0] b[0][1] b[0][2]… … …b[6][0] b[6][1] b[6][2]

Un tablou cu mai multe dimensiuni poate fi de asemenea iniţializat la declararea sa. Fie de definit matricea cu elemente întregi

Instrucţiunea corespunzătoare esteint m[2][3] = {{1, 2, 5},{3, 7, -3}};

Page 20: Curs Program Are C++

La utilizarea într-o expresie, indicii elementelor tablourilor pot fi orice expresii întregi. Presupunem următoarele declaraţii de tablouri

double a[10], b;int i, j, y[3][4];

Exemple de expresii ce conţin elemente de tablouri.

(a[i]+b*y[i][j])/(cos(b)-sin(b))

exp(cos(b)) + y[i][j + 1]

La utilizarea elementelor unui tablou, [] este un operator de selecţie ce are doi operanzi: un nume de tablou şi un indice. El se aplică asupra unui nume de tablou şi selectează un element al acelui tablou. De exemplu a[0] selectează primul element al tabloului a, iar b[0] selectează prima linie a tabloului b. Aplicând încă o dată operatorul de selecţie asupra lui b[0], de exemplu b[0][0] selectează primul element din prima linie a lui b. Operatorul [] este asociativ la stânga.In acelaşi mod, operatorul de apelare a unei funcţii (), aplicat asupra unui nume de funcţie returnează valoarea calculată de funcţie. De exemplu cos(1.2) reprezintă aplicarea operatorului () asupra numelui funcţiei cos.

Priorităţile şi asociativitatea operatorilor

Operator Asociativitate [] () Asociativi la stânga+ unar, - unar* / % Asociativi la stânga+ - Asociativi la stânga

2.9 Instrucţiunea de atribuire

Operatorul de atribuire = atribuie o valoare unei variabile. Forma instrucţiunii de atribuire este

variabilă = expresie;De exemplu, următoarele instrucţiuni atribuie valori variabilelor

float x, y;x = -1.34;y = sin(x) + cos(x * x);

Limbajele C şi C++ au operatori speciali pentru scriera prescurtată a instrucţiunilor de atribuire, +=, -=, *=, /= şi %=. Aceşti operatori se definesc astfel: Considerăm o variabilă x şi o expresie e.

x op= eeste echivalent cu

x = x op eTabelul următor prezintă aceşti operatori

Operatori de atribuire

Page 21: Curs Program Are C++

Instrucţiune Forma prescurtatăx = x + e x += ex = x - e x -= ex = x * e x *= ex = x / e x /= ex = x % e x %= e

Alţi operatori de acest tip vor fi prezentaţi ulterior. Menţionăm în final că un operator de atribuire are doi operanzi şi ca rezultat valoarea operandului din stânga. Operanzii de atribuire sunt asociativi la dreapta (se execută de la dreapta la stânga). Fie de exemplu declaraţia

int x, y, z = 1;Instrucţiunea

x = y = z;atribuie variabilelor x şi y valoarea 1. Instrucţiunea

x += y += z;atribuie variabilei y valoarea 2 şi variabilei z valoarea 3. De ce?

2.10 Prototipuri de funcţii. Biblioteci de prototipuri

Atunci când compilatorul întâlneşte un apel la funcţie, el trebuie să poată verifica concordanţa între parametrii actuali şi cei formali şi are nevoie de tipul rezultatului funcţiei pentru a face conversiile necesare evaluării expresiei. Pentru aceasta el are nevoie de o definiţie a funcţiei în care apar tipurile parametrilor şi tipul rezultatului. Această definiţie se numeşte şablon sau prototip şi are forma

tip nume(tip, tip, …, );De exemplu, prototipul funcţiei sin este

double sin(double);Limbajele C şi C++ au biblioteci standard cu prototipurile funcţiilor limbajului. Aceste biblioteci sunt semnalate compilatorului cu directiva include

# include <nume_bibliotecă>Aceste biblioteci sunt fişiere cu extensia h şi se numesc fişiere header. De exemplu, biblioteca cu prototipurile funcţiilor matematice este <math.h>. Pentru a fi semnalată compilatorului vom scrie instrucţiunea

# include <math.h>Biblioteca cu funcţii intrare/ieşire tip C este <stdio.h>, biblioteca cu funcţii de prelucrat şiruri tip C este <string.h>, biblioteca cu funcţii intrare/ieşire tip C++ este <iostream.h>, etc. Putem defini propriile biblioteci cu prototipuri pe care să le semnalăm compilatorului cu directive include. Numele propriilor biblioteci cu prototipuri sunt scrise între ghilimele.

In limbajul C++ este posibil şi un nou stil de includere a bibliotecilor standard. Bibliotecile specifice limbajului C sunt redefinite ca <cstdio>, <cmath>, <cstring>, etc. In plus, toate funcţiile standard ale limbajelor C şi C++ sunt grupate într-un spaţiu de nume denumit std. In consecinţă, putem semnala compilatorului bibliotecile standard ca

# include <nume_biblioteca>

Page 22: Curs Program Are C++

using namespace std;unde în directive include avem bibliotecile specifice limbajului C++.

2.11 Operaţii de intrare / ieşire

Orice aplicaţie are un director curent asociat şi fişiere standard de intrare şi ieşire. Fişierul standard de intrare este tastatura iar cel de ieşire este ecranul. In program orice fişier este asociat unui obiect numit stream. Există două tipuri de fişiere: text şi binare. Fişierul text este este compus dintr-un şir de caractere grupate în linii. Liniile constau din zero sau mai multe caractere plus un caracter ‘\n’. Fişierele standard sunt de tipul text. Streamul de intrare asociat tastaturii are denumirea cin, streamul de ieşire asociat ecranului se numeşte cout. Mai există alte doua streamuri cerr şi clog pentru scrierea mesajelor de eroare. Operatorul de scriere este <<. El inserează date în streamul cout. De exemplu, secvenţa de instrucţiuni

int i = 123;cout << “i = ” << i;

afişază pe ecran i = 123

Pentru a despărţi textul afişat în linii, trebuie să scriem caracterul ‘\n’, care este predefinit ca endl. De exemplu, pentru a afişa cele de mai sus pe două linii, vom scrie

cout << “i = “ << endl << i << endl;sau

cout << “i = “ << ‘\n’ << i << ‘\n’;sau, echivalent

cout << “i =” << endlcout << i << endl;

Operatorul << poate scrie orice tip predefinit de date: int, float, şiruri de caractere, etc.Operatorul de citire dintr-un stream este >>. El extrage date din stream. Operatorul >> este urmat de numele variabilei ce va memora valoarea citită. De exemplu, secvenţa de instrucţiuni

int a;cin >> a;

va citi valoarea introdusă de la tastatură şi o va atribui variabilei a. Se pot citi oricâte date din streamul cin. Instrucţiunea

cin >> a >> b;este echivalent cu

cin >> a;cin >> b;

Cele două valori introduce pentru exemplul de mai sus trebuie separate de spaţii , \t sau \n (acesta din urmă este generat la apăsarea tastei Return).Biblioteca de prototipuri pentru streamurile cin, cout, etc are numele <iostream.h>. Pentru a modifica formatul de scriere sai citire a datelor putem utilize manipulatori. Pentru scrierea sau citirea unui număr întreg în instrucţiunile cout sau cin în diferite baze se utilizează manipulatorii

hex pentru baza 16

Page 23: Curs Program Are C++

dec pentru baza 10 oct pentru baza 8

La începerea execuţiei unui program implicită este baza 10. Pentru scrierea bazei se uitlizează manipulatorii

showbase pentru scrierea bazei noshowbase pentru a nu scrie baza

Exemplu. Fie instrucţiuneaint k = 20;

Tabloul următor prezintă exemple de utilizare a manipulatorilor.

cout << k; 20cout << hex << k; 14cout << oct << k; 24cout << showbase << hex << k; 0x14cout << showbase << oct << k; 024

In cazul numerelor reale avem manipulatorii fixed numărul este scris fără exponent scientific numărul este scris cu exponent

Exemplu. Fie instrucţiuneafloat x = 122.63;

Tabloul următor prezintă exemple de utilizare a manipulatorilor.

cout << x; 122.63cout << fixed << x; 122.63cout << scientific << x; 1.226300e+002

Funcţiile următoare sunt apelate de operatorul de scriere << setbase(int); setfill(char); setprecision(int); setw(int);

Funcţia setbase(int) indică baza în care va fi afişat un număr întreg, 8, 10 sau 16. Funcţia setfill(char) indică un caracter cu care se umplu spaţiile unui camp. Valoarea implicită a acestui manipulator este spaţiul. Funcţia setprecision(int) dă numărul de cifre cu care este scris un număr real. Funcţia setw(int) dă dimensiunea campului în caractere pe care este scris un număr. Valoarea implicită a acestui manipulator este 0. Dacă dimensiunea campului nu este specificată, sau este prea mică, numărul este scris pe câte caractere este necesar.Exemplu. Fie instrucţiunea

int x = 23; Tabloul următor prezintă exemple de utilizare a funcţiilor.

cout << setw(5) << x; 23cout << x; 23cout << setbase(16) << x: 17

Page 24: Curs Program Are C++

cout << dec << setw(3) << setfill(‘*’) << x; *23

Exemplu. Fie instrucţiuneafloat z = 12.64;

Tabloul următor prezintă exemple de utilizare a funcţiilor.

cout << setprecision(3) << z; 12.6cout << setw(8) << z; 12.64cout << setw(8) << setfill(‘*’) << z; ***12.64cout << setw(15) << scientific << z; 1.264000e+001

Menţionăm că dimensiunea campului prescrisă de funcţia setw(int) se aplică doar următorului număr de scris. Ceilalţi manipulatori răman la valoarea prescrisă pană sunt modificaţi.

2.12 Funcţia main

Orice program scris în limbajele C sau C++ se compune din funcţii care se apelează unele pe altele. Definiţia unei funcţii este

tip nume (lista de parametri){

instrucţiuni}

Una din funcţii are numele main iar execuţia programului începe cu această funcţie. Prototipul acestei funcţii este

int main();Semnificaţia prototipului este următoarea. Funcţia main are ca rezultat o valoare întreagă care este codul de terminare a programului şi nu are parametri. Corpul funcţiei este o instrucţiune compusă, adică o secvenţă de instrucţiuni scrise între accolade, { şi }. Programul poate conţine comentarii. Comentariile plasate între delimitatorii /* şi */ se pot întinde pe mai multe linii. Comentariile ce încep cu caracterele // se întind pe o singură linie. Putem scrie acum primul program care citeşte o valoare întreagă de la tastatură şi afişează pătratul ei.

# include <iostream.h>int main(){

int i, j;cout << “introduceti o valoare intreaga” << endl;/* se citeste o valoare intreaga */cin >> i;cout << “valoarea introdusa este “ << i << endl;/* se calculeaza patratul valorii citite */j = i * i;/* se scrie valoarea calculata */cout << “patratul valorii este “ << j << endl;

Page 25: Curs Program Are C++

return 0;}

Menţionăm că funcţia main() returnează valoarea 0 cu instrucţiunea return 0;

Putem include biblioteca cu prototipurile funcţiilor intrare/ieşie ca# include <iostream>using namespace std;

In biblioteca <iostream> prototipurile funcţiilor intrare/ieşire sunt grupate in spaţiul de nume std.

2.13 Execuţia unui program

Prima etapă este compilarea programului. In această etapă programul este verificat pentru erori sintactice. Dacă programul nu conţine erori sintactice compilatorul generează un program obiect traducând fiecare instrucţiune a programului într-o serie de instrucţiuni elementare ale calculatorului.

Etapa a doua este editarea legăturilor. In această etapă sunt ataşate programului funcţiile din biblioteci. Atunci când compilatorul întalneşte un apel de funcţie (de exemplu sin, cos, etc.), sau o operaţie intrare/ieşire, el generează doar o secvenţă de apel la funcţie. Funcţiile respective sunt compilate în biblioteci speciale. Programul editor de legături ataşează aceste funcţii programului obiect. Programul generat de editorul de legături se numeşte program executabil. El este un fişier cu extensia exe.

In etapa a treia programul excutabil este încărcat în memorie şi executat.

2.14 Operatorul sizeof

Operatorul sizeof se aplică asupra unei expresii sau asupra unui tip şi are ca rezultat numărul de octeţi de memorie utilizaţi. De exemplu, expresia

sizeof(int) are ca rezultat valoarea 4. In cazul unui tablou rezultatul este numărul total de octeţi ocupat de tablou.Exemplu. Vom scrie un program care să afişeze numărul de octeţi utilizaţi pentru memorarea tipurilor fundamentale. Pentru a afişa datele deplasate la stânga cu un număr de spaţii vom scrie un caracter tab, ‘\t’.

# include <iostream.h>int main(){

float a[10], b;cout << “Numarul de octeti utilizati:” << endl;

// scrie dimensiunea tipurilor standardcout << ‘\tint: ’ << sizeof(int) << endl;cout << ‘\tchar: ’ << sizeof(char) << endl;cout << ‘\tfloat: ’ << sizeof(float) << endl;

Page 26: Curs Program Are C++

cout << ‘\tdouble: ’ << sizeof(double) << endl; // scrie dimensiunea unui vector

cout << ‘\tvectorul float a[10]: ’ << sizeof(a) << endl; // scrie dimensiunea rezultatului unei expresii

cout << ‘\t expresie’ << sizeof(a[0]+ b) << ‘\n’;}

Menţionăm că este echivalent dacă în instrucţiunea cout utilizăm caracterul ‘\n’, sau endl, sau şirul de caractere “\n”.

2.14 Operatorii ++ şi - -

Operatorul ++ incrementează o variabilă întreagă cu unu, operatorul - - decrementează o variabilă întreagă cu unu. Aceşti operatori pot fi prefix, adică putem scrie

++x, --xsau postfix

x++, x- - Cazul operatorilor prefix, ++x, --x. Se incrementează sau decrementează valoarea

variabilei cu unu, valoarea incrementată sau decrementată fiind şi rezultatul expresiei.Exemplu. Fie declaraţia de variabile

int i = 1, x;Instrucţiunea

x = ++i;reprezintă scrierea prescurtată a secvenţei de instrucţiuni

i = i + 1;x = i;

După execuţia instrucţiunii x = ++i variabilele au valorile i = 2 şi x = 2.Exemplu. Fie declaraţia de variabile

int j = 3, k;Instrucţiunea

k = --j;reprezintă scrierea prescurtată a secvenţei de instrucţiuni

j = j – 1;k = j;

După execuţia instrucţiunii variabilele au valorile j = 2 şi k = 2.Exemplu. Fie următoarele declaraţii.

float a[4] = {1.2, -5, 8, 3};float r;int i = 1, k = 1;

Secvenţa de instrucţiunii = i +1;r = a[i];

se poate scrier = a[++i];

Expresia ++i este evaluată la 2, iar r primeşte valoarea a[2] = 8.

Page 27: Curs Program Are C++

Secvenţa de instrucţiunik = k – 1;r = a[k];

se poate scrier = a[--k];

. Expresia --k este evaluată la 0 iar r primeşte valoarea a[0] = 1.2 Cazul operatorilor postfix, x++, x--. Valoarea expresiei este chiar valoarea

variabilei (neincrementate sau decrementate). Se incrementează / decrementează apoi variabila. Exemplu. Fie cele două secvenţe de instrucţiuni de mai jos

int a, b;b = 3;a = b++;

Expresia a = b++;

corespunde secvenţei de instrucţiunia = b;b = b + 1;

In consecinţă, expresia b++ are valoarea 3 (valoarea neincrementată a variabilei) şi apoi se incrementează b. Avem rezultatul

a = 3b = 4

Exemplu. Fie cele două secvenţe de instrucţiuni de mai josint i; int j;double x[4] = {2.4e1, 14.4, -3.1 0}; double x[4] = {2.4e1, 14.4, -3.1 0};double d; double d;i = 1; j = 1;d = x[i++]; d = x[++j];In primul caz instrucţiunea

d = x[i++];corespunde secvenţei de instrucţiuni

d = x[i];i = i + 1;

Expresia i++ are valoarea 1 şi apoi se incrementează i. Avem decid = 14.4

şii = 2

In al doilea caz instrucţiunea d = x[++j];

corespunde secvenţei de instrucţiunij = j + 1;d = x[j];

In consecinţă expresia ++j are valoarea 2 şi avemd = -3.1

şij = 2

Page 28: Curs Program Are C++

Alte exemple de utilizare a acestor operatori vor fi prezentate ulterior.

2.14 Operaţii cu numere întregi la nivel de bit

2.14.1 Operatori de depasare

Deplasarea la stânga, respectiv dreapta, a unui întreg cu un bit reprezintă înmulţirea, respectiv împărţirea, acelui număr cu doi.Operatorii de deplasare a unui număr întreg cu un număr de biţi sunt << pentru depasare la stânga şi >> pentru depasare la dreapta. Expresia de deplasare a unui număr întreg are forma

Rezultatul expresiei din stânga este configuraţia de biţi ce va fi deplasată. Expresia din dreapta dă numărul de biţi cu care se face deplasarea. Deplasarea se face după regulile deplasării numerelor binare cu semn. (La deplasarea la dreapta se propagă bitul de semn, la deplasarea la stânga se adaugă zerouri). Dacă expresia de deplasat este de tipul unsigned biţii adăugaţi sunt zerouri.Exemple. Fie instrucţiunile de mai jos

int a = 0xff; int a = 0xff;int b; int b;b = a << 4; b = a >> 4;

Numărul a reprezentat pe patru octeţi are valoarea hexazecimalăa = 000000ff

Numărul deplasat la stânga cu 4 biţi este00000ff0

iar deplasat la dreapta cu 4 biţi este0000000f

Numărul 0xff convertit în zecimal este 255. Care este valoarea numerelor 0xf şi 0xff0 în zecimal?.Ca un alt exemplu, să definim constantele X, Y, Z şi R care să aibe valorile 1, 2, 4, 8, folosind instrucţiunea enum.

enum {X = 1, Y = X << 1, Z = Y << 1, R = X << 3};Limbajele C şi C++ au operatorii de atribuire <<= şi >>= care realizează operaţiile următoare. Instrucţiunile

x = x << nşi

x = x >> nse scriu

x <<= nşi respectiv

x >> n

Page 29: Curs Program Are C++

Vom încheia acest paragraf cu un program care deplasează numere întregi şi le afişază valoarea in zecimal şi hexazecimal. Pentru scrierea sau citirea unui număr întreg în instrucţiunile cout sau cin în diferite baze se utilizează manipulatorii

hex pentru baza 16 dec pentru baza 10 oct pentru baza 8

La începerea execuţiei unui program implicită este baza 10. Vom exemplifica utilizarea operatorilor de deplasare şi vom afişa rezultatele în baza 16.

# include <iostream.h>/* uitlizarea operatori de deplasare */int main(){

int a = 10;cout << “a = “ << dec << a << “ “ << hex << a << endl;

// deplasează variabila a la stanga cu un bita = a << 1;cout << “a deplasat la stanga cu 1 bit = “

<< dec << a << “ “ << hex << a << endl;int b = 50;cout << “b = “ << dec << b << “ “ << hex << b << endl;

// deplasează variabila b la dreapta cu 3 pozitiib = b >> 3;cout << “b deplasat la dreapta cu 3 biti = “

<< dec << b << “ “ << hex << b << endl;return 0;

}

2.14.2 Operaţii logice la nivel de bit

Limbajele C şi C++ au următorii operatori pentru operaţii logice la nivel de bitşi logic (and) notat &sau logic (or) notat |sau excusiv (xor) notat ^complement faţă de unu notat ~

Aceşti operatori se definesc cu tabelele următoare

a b a&b a|b a^b0 0 0 0 00 1 0 1 11 0 0 1 11 1 1 1 0

a ~a0 1

Page 30: Curs Program Are C++

1 0

Operatorii &, | ^ sunt operatori binary, ~ este operator unar. Expresiile cu operatori logici la nivel de bit au formele următoare

Exemple. Fie următoarea secvenţă de programint a, b, c, d, e;a = 0xf000;b = 0xabcd;c = a & b;d = a | b;e = a ^ b;

Rezultatele suntc = 0xa000d = 0xfbcde = 0x5bcd

In primul caz, pentru a calcula c = a & b avem de calculat0xf & 0xa = 0xa

conform schemei de mai jos.

Pentru a calcula e = a ^ b trebuie să calculăm 0xf ^ 0xa = 0x5

conform schemei de mai jos

Expresia ~a are valoarea 0x0fff. De ce?Limbajele C şi C++ au operatorii &=, |= şi ^= pentru scrierea prescurtată a instrucţiunilor de atribuire. De exemplu expresia

x = x & aSe scrie prescurtat

x &= aMenţionăm că operatorii <<, >>, &, | şi ~ sunt operatori aritmetici.

Page 31: Curs Program Are C++

Exemplu. Fie variabila întreagă a = 0x6dbc. Vrem ca în variabila întreagă b să selectăm ultimul octet al variabilei a şi apoi ultimii 10 biţi ai variabilei a. Selectarea unui număr de biţi dintr-un şablon se face definind un şir de biţi numit mască şi efectuând operaţia & între variabila iniţială şi mască.

# include <iostream.h>int main(){

int a = 0x6dbc, b;cout << “a = “ << hex << a << endl;// selecteaza ultimul octet b = a & 0xff;cout << “b = “ << hex << b << endl;b = a & 0x3ff;// selecteaza ultimii 10 biticout << “b = “ << hex << b << endl;return 0;

}

Să se explice de ce în cazul al doilea masca este 0x3ff;

Cap. 3 Structuri de control fundamentale

3.1 Algoritme

Programele reprezintă formulări concrete ale unor algoritme ce prelucrează structuri de date. Un algoritm este format dintr-un şir finit de acţiuni bine definite şi neambigue pentru rezolvarea unei probleme. Fiecare acţiune trebuie să poată fi executată într-un interval finit de timp.Orice algoritm se poate descompune într-un număr finit de etape. El poate fi reprezentat printr-un şir de acţiuni astfel încat efectul general al acestor acţiuni să conducă la rezultatul dorit al calculului. In cazul cel mai simplu un algoritm poate fi descompus într-un număr de acţiuni secvenţiale care se reprezintă în felul următor

s1; s2; … sn;Pentru executarea unei acţiuni în funcţie de îndeplinirea unei condiţii avem operatori condiţionali

Operatorul if cu formeleif condiţie s1;

Page 32: Curs Program Are C++

sauif condiţie s1; else s2;

condiţie este o expresie booleană care are valoarea advărat sau fals. Acţiunea s1 se execută când condiţie are valoarea adevărat.

Operatorul switch este generalizarea operatorului ifswitch(i)

i = 1: s1;i = 2: s2;………..i = n: sn;

In funcţie de valoarea lui i se execută una dintre acţiunile s1, s2, …, sn.Aceşti operatori se caracterizează prin faptul că au o singură intrare şi o singură ieşire. Fiecare operator este interpretat în şirul de calcule ca o singură acţiune, indifferent de conţinutul său. Operatorii anteriori sunt suficienţi pentru a descrie clasele de calcule ce se pot descompune într-un număr cunoscut de acţiuni.Pentru a descrie calculele repetate când numărul de acţiuni nu este cunoscut există operatorii while şi do.

Operatorul while are formawhile condiţie

s;Acţiunea s se execută atata timp cat expresia booleană condiţie are valoarea adevărat.

Operatorul do are formado

s; while condiţie

Si în acest caz acţiunea s se execută atâta timp cat condiţie are valoarea adevărat.

Menţionăm că în cazul operatorului do acţiunea s se execută cel puţin o dată, în timp ce pentru operatorul while este posibil ca acţiunea s să nu se execute niciodată.

Operatorul for se utilizează atunci când numărul de execuţii ale unei acţiuni este dinainte cunoscut. Operatorul are o variabilă de control ce se poate modifica la fiecare iteraţie. Forma acestui operator este

for i = instrucţiune1; condiţie; instrucţiune2 s;

Operatorul include o instrucţiune1 ce specifică valoarea iniţială a variabilei de control, şi o instrucţiune2 ce poate modifica valoarea variabilei de control după fiecare iteraţie. Acţiunea s se execută atât timp cât expresia booleană condiţie are valoarea adevărat. Operatorul for este echivalent cu următoarele instrucţiuni

instrucţiune1;while(condiţie)

s;instrucţiune2;

Menţionăm că testul condiţiei se face înainte de execuţia acţiunii s.

Page 33: Curs Program Are C++

Operatorii prezentaţi se numesc structuri de control fundamentale. In construcţia unui program structurile de control şi structurile de date sunt inseparabile. Structurile fundamentale de date sunt

Structuri simple: numere întregi, reale, caractere. Structuri complexe tablouri, fişiere.

Tablourile au un număr cunoscut de elemente. Ele se prelucrează de regulă cu operatorul for. Fişierele secvenţiale au un număr de elemente necunoscut în avans. Ele se prelucrează de regulă cu operatorul while.

3.2 Expresii relaţionale

Operatorii relaţionali ai limbajelor C şi C++ sunt<, <=, >, >= = =, !=

O expresie relaţională are următoarea formă

Rezultatul evaluării unei expresii relaţionale este fals sau adevărat. Priorităţile operatorilor aritmetici sunt mai mari decăt ale operatorilor relaţionali.Exemple. Fie următoarele instrucţiuni de atribuire

int a = 2, b = 3, c = 6; In tabela de mai jos sunt prezentate exemple de expresii relaţionale şi rezultatul evaluării lor.

Expresie relaţională Valoarea*b >= c Trueb+2 > a *c Falsea+b = = 3 Falsea != b Trueb/a = = 1 True

Reamintim că în limbajul C++ valoarea true este reprezentată prin 1 iar valoarea false prin 0. Fie următoarea instrucţiune de declarare a unei variabile de tip bool

bool r;Putem avea următoarele instrucţiuni de atribuire

r = a + b = = c;r = a – b >= 2;r = a * a != -b;

Variabila r poate avea valori booleene, true sau false.

3.3 Expresii booleene

Operatorii booleeni ai limbajelor C şi C++ sunt

Page 34: Curs Program Are C++

!&&| |

care reprezintă operatorii not, and şi respectiv or. Operatorul not este operator unar. Aceşti operatori se definesc folosind tabelele de adevăr.

x y x && y x | | yfalse false false falsefalse true false Truetrue false false Truetrue true false True

x !xfalse truetrue false

Rezultatul evaluării unei expresii booleene este true sau false. In cursurile de logică operatorii booleeni se notează astfel

Exemple de expresii booleene şi scrierea lor sunt prezentate mai jos.

Pentru scrierea simplă a expresiilor booleene sunt importante două teoreme numite legile lui DeMorgan

care se demonstrează cu ajutorul tabelelor de adevăr.In final menţionăm că operatorii booleeni and şi or sunt

Comutativi

asociativi la stânga

distributivi

In final prezentăm prioritatea (precedenţa) şi asociativitatea operatorilor

Page 35: Curs Program Are C++

Operator asociativitate[ ] ( ) stânga++ -- + - ! ~ sizeof dreapta* / % stânga+ - stânga<< >> stânga< <= > >= stânga= = != stânga& stânga^ stânga| stânga&& stânga| | stânga= += -= *= /= %= stânga

3.4 Operatorul do-while

Acest operator este definit de diagrama sintactică

do instrucţiune

while(expresie);Instrucţiunea se execută atâta timp cât expresia este diferită de zero. Instrucţiunea din diagrama sintactică este o secvenţă de instrucţiuni. Ca exemplu vom calcula valoarea 5!.

Vom utiliza o variabilă n pusă iniţial la valoarea 1 şi vom executa repetat instrucţiunea n = n * i

pentru i luând valori de la 1 la 5. Programul pseudocod este următorul.

n = 1;i = 1do

n = n * i;i = i + 1;

while(i < 6)

Programul corespunzător este următorul

# include <iostream.h>/* calculul valorii 5! */int main()

Page 36: Curs Program Are C++

{int i, n;i = 1;n = 1;do

n = n * i;i = i + 1;

while(i < 6);cout << “5! are valoarea “ << n << endl;return 0;

}

Putem rescrie partea de calcul din program astfel

int i = 1, n = 1;do

n *= i;i++;

while(i < 6);

sau

int i = 1, n = 1;do

n *=i++;while(i < 6);

Reamintim că instrucţiunean *= i++;

este echivalentă cu instrucţiunilen *= i;i++;

conform modului de execuţie a operatorului postfix ++ : valoarea expresiei este chiar valoarea variabilei i neincrementate, după care variabila i este incrementată.

3.5 Operatorul while

Forma operatorului while este cea din diagrama de mai joswhile(expresie)

instrucţiuneinstrucţiunea se execută atâta timp cât expresia este diferită de zero. Instrucţiunea din diagrama sintactică poate fi o instrucţiune compusă, formată dintr-o secvenţă de instrucţiuni cuprinse între acolade, { şi }.Vom exemplifica utilizarea acestui operator calculând suma elementelor unui vector a cu 4 componente. Elementele vectorului a au indicii 0, 1, 2 şi 3.

Page 37: Curs Program Are C++

Vom utilize o variabilă f de tip float pentru a calcula suma, care va fi pusă iniţial la valoarea zero şi vom executa repetat instrucţiunea

pentru i luând valori de la 0 la 3. Programul pseudocod este următorul.s = 0;i = 0;while(i < 4)

s = s + a[i];i = i + 1;

Programul este următorul

#include <iostream.h>/* calculul sumei componentelor unui vector */int main(){

float a[4] = {2.34, -7.32, 2.5e-1, 73};int i;float s;s = 0;i = 0;while(i < 4){

s = s + a[i];i = i + 1;

}cout << “suma componentelor este “ << s << endl;return 0;

}

Putem rescrie partea de calcul din program astfel

s = 0;i = 0;while(i < 4){

s += a[i];i++;

}

saus = 0;i = 0;while(i < 4){

Page 38: Curs Program Are C++

s += a[i++];}

3.6 Operatorul for

Operatorul for are formafor(expresie1; expresie2; expresie3)

instrucţiuneOperatorul poate conţine variabile de control ce sunt modificate după fiecare iteraţie. Operatorul for execută repetat o instrucţiune, atât timp cât expresie2 este diferită de zero. expresie1 are rolul de a iniţializa variabile de control. expresie3 are rolul de a modifica valoarea variabilelor de control. Instrucţiunea din diagrama sintactică poate fi o instrucţiune compusă constand dintr-o secvenţă de instrucţiuni între acolade, { şi }.Ca exemplu vom calcula valoarea expresiei pentru x cuprins între unu şi doi cu pasul 0.2. Programul pseudocod este următorul

for i = 0; i <6; i = i + 1x = 1 + 0.2 * iy =

Programul este următorul

#include <iostream.h># include <math.h>int main(){

int i;float x, ycout << “x” << ‘\t’ << “y” << endl;for(i = 0; i < 6; i = i + 1){

x = 1 + 0.2 * i;y = exp(x) + 2 * x + sqrt(x);cout << x << ‘\t’ << y << endl;

}return 0;

}

Vrem ca rezultatul să fie afişat în două coloane cu antetul x şi y. Pentru aceasta utilizăm instrucţiunea

<< “x” << ‘\t’ << “y” << endl;care scrie caracterele x şi y separate de caracterul ‘\t’ (tab). In program rezultatele sunt scrise de asemenea separate de tab.Menţionăm că instrucţiunea for se putea scrie astfel

for(i = 0; i < 6; i++)sau

Page 39: Curs Program Are C++

for(i = 0; i < 6; ++i)Un alt mod de a calcula valorile expresiei de mai sus este următorul.

# include <iostream># include <math.h>int main(){

float x, ycout << “x” << ‘\t’ << “y” << endl;for(x = 1.0; x <= 2.0; x = x + 0.2){

y = exp(x) + 2 * x + sqrt(x);cout << x << ‘\t’ << y << endl;

}return 0;

}

Vom încheia acest paragraf cu un program care să calculeze suma a doi vectori.

# include <iostream.h>/* calculul sumei a doi vectori */int main(){

float x[3] = {1.1e+1, -2.57, 13.2};float y[3] = {-2.12, 13.5, 3.41};float z[3];for(int i = 0; i < 3; i++)

z[i] = x[i] + y[i];for(int i = 0; i < 3; i++)

cout << z[i] << ‘\t’;cout << ‘\n’;return 0;

}

In final menţionăm instrucţiunile break şi continue. Instrucţiunea break produce ieşirea imediată din instrucţiunile for, while sau do-while. Instrucţiunea continue trece la următoarea iteraţie a instrucţiunii for, while sau do-while.

3.7 Operatorul if

Acest operator execută o anumită instrucţiune în funcţie dacă o anumită condiţie este îndeplinită. Forma operatorului if este

if(expresie)instrucţiune1;

elseinstrucţiune2;

Page 40: Curs Program Are C++

Dacă expresie este diferită de zero se execută instrucţiune1 altfel instrucţiune2. Instrucţiunile din diagrama sintactică pot fi instrucţiuni compuse.Vom exemplifica utilizarea acestui operator calculând suma componentelor pare şi impare ale unui vector x cu şase elemente. Programul pseudocod este următorul

s1 = 0;s2 = 0;for i = 0; i < 6; i=i+1

if i % 2 = = 0s1 = s1 +

elses2 = s2 +

Programul este următorul

#include <iostream.h>/* calculul sumei componentelor pare si impare ale unui vector */int main(){

float s1 = 0, s2 = 0;int i;float x[10] = {1, -12, 23.2, 0.44, 3.4, -2.3};for(i = 0; i < 6; i = i + 1)

if(i % 2 = = 0)s1 = s1 + x[i];

elses2 = s2 + x[i];

cout << “suma elementelor pare este “ << s1 << endl << “suma elementelor impare este “ << s2 << endl;return 0;

}

Menţionăm că o instrucţiune if poate conţine alte instrucţiuni if. De exemplu putem avea instrucţiunea compusă

if (e1) if (e2)s1

elses2

else if(e3)s3

else s4

unde e1, e2, e3 sunt expresii booleene. Dacă expresia booleană e1 are valoarea adevărat, (este diferită se zero), se execută instrucţiunea if(e2), în caz contrar se execută instrucţiunea if(e3).

Page 41: Curs Program Are C++

3.8 Operatorul ?

Acest operator este o formă simplificată a operatorului if. Forma lui esteexpresie ? instrucţiune1 : instrucţiune2;

Dacă expresie are o valoare diferită de zero se execută instrucţiune1 altfel instrucţiune2.Vom rescrie programul anterior astfel

#include <iostream.h>/* calculul sumei componentelor pare si impare ale unui vector */int main(){

float s1 = 0, s2 = 0;int i;float x[10] = {1, -12, 23.2, 0.44, 3.4, -2.3};for(i = 0; i < 6; i = i + 1)

i % 2 = = 0 ? s1 = s1 + x[i] : s2 = s2 + x[i];cout << “suma elementelor pare este “ << s1 << endl << “suma elementelor impare este “ << s2 << endl;return 0;

}

3.9 Operatorul switch

Acest operator execută o instrucţiune din mai multe posibile. Fiecare instrucţiune este etichetată cu o constantă întreagă. Instrucţiunea conţine o expresie întreagă ce determină ce etichetă trebuie aleasă.

switch(expresie){

case expint: instructiunecase expint: instructiune….case expint: instructiune

}Una dintre etichete poate fi default. In acest caz, dacă expresia din instrucţiunea switch nu are nici una din valorile etichetelor din instrucţiunile case, se trece la execuţia instrucţiunilor cu eticheta default.Instrucţiunea break este utilizată pentru ieşirea din instrucţiunea switch.Exemplu. Vom citi două numere întregi şi un operator +, -, *, / sau % şi vom calcula rezultatul operaţiei corespunzătoare.

#include <iostream.h>int main(){

int x, y;char oper;

Page 42: Curs Program Are C++

cout << “intoduceti doi intregi : ”;cin >> x >> y;cout << “introduceti un operator : “;cin >> oper;switch(oper){

case ‘+’ : cout << x + y; break;

case ‘-‘ : cout << x – y; break;

case ‘*’ : cout << x * y; break;

case ‘/’ : cout << x / y; break;

case ‘%’ : cout << x % y; break;

default : cout << “eroare”; break;

}return 0;

}

Menţionăm că instrucţiunile corespunzătoare etichetelor sunt urmate de instrucţiunea break ce asigură ieşirea din operatorul switch.

3.10 Operatorul ,

Operatorul , are următoarea formăexpresie1, expresie2

Rezultatul operatorului , este expresie2, rezultatul evaluării expresie1 se neglijază. Operatorul , se poate utiliza oriunde limbajul admite o singură expresie, dar sunt necesare două expresii.

Page 43: Curs Program Are C++

Cap. 4 Funcţii

4.1 Definirea funcţiilor

Funcţiile se definesc într-un program atunci când un grup de instrucţiuni se utilizează în mai multe locuri din program. Prototipul definiţiei unei funcţii este

tip nume (lista de parametri)instrucţiune

Instrucţiunea este o instrucţiune compusă formată dintr-o secvenţă de instrucţiuni între acolade. Lista de parametri are forma

tip1 arg1, tip2 arg2, …, tipn argnunde tip1, tip2, …, tipn reprezintă tipurile parametrilor arg1, arg2, …, argn. Parametrii din definiţia funcţiei se numesc parametri formali. O parte din parametri sunt variabile ce vor fi prelucrate de funcţie (aceşti parametri se numesc parametri de intrare). O funcţie poate calcula o valoare asociată numelui şi mai multe valori asociate unor parametri (aceşti parametri se numesc parametri de ieşire). Tipul valorii asociate numelui este şi tipul funcţiei. Dacă o funcţie nu are o valoare asociată numelui, tipul ei este void. Asocierea unei valori calculate numelui funcţiei se face cu instrucţiunea return cu forma următoare

return expresie;unde expresie este valoarea calculată. Când tipul funcţiei este void, se utilizează instrucţiunea

return;

Page 44: Curs Program Are C++

Funcţiile sunt recursive, adică o funcţie se poate apela pe ea însăşi.Apelarea unei funcţii se face utilizănd-o ca termen într-o expresie. Parametri de apelare a funcţiei se numesc parametri actuali. Parametrii de intrare actuali ai unei funcţii pot fi orice constante, variabile sau expresii. Parametrii actuali de ieşire sunt variabile.Exemplu. Vom scrie o funcţie care să calculeze suma componentelor unui vector cu elemente numere reale. Parametrii funcţiei vor fi vectorul şi dimensiunea sa. Rezultatul este o valoare reală ataşată numelui funcţiei.

#include <iostream.h>/* funcţie ce calculeaza suma componentelor unui vector

Parametri de intrarea – vectorn – dimensiunea vectorului a

*/float suma(float a[], int n){

int i;float s = 0;

// calculeaza suma componentelorfor (i = 0; i < n; i = i++)

s = s + a[i];return s;

}int main(){

float x[5] = {11.3, -2.67, 0.34, -2.5, 14};float z;

// calculeaza suma componentelorz = suma(x, 5);

// scrie componentele vectoruluicout << “suma componentelor vectorului “ << endl;for(i = 0, i < 5; i++)

cout << x[i] << “\t”;cout << endl;

// scrie rezultatulcout << “este “ << z << endl;

}

Componentele vectorului sunt scrise separate de caracterul tab pe un rând iar rezultatul pe rândul următor. In exemplul anterior definiţia funcţiei este scrisă înainte de utilizarea ei în funcţia main. Menţionăm că este posibil de a scrie definiţia unei funcţii după ce ea este utilizată. In acest caz trebuie să scriem înainte de prima utilizare un şablon al funcţiei. Şablonul unei funcţii are următoarea definiţie

tip nume(lista de parametri);Pentru exemplul anterior şablonul poate fi

Page 45: Curs Program Are C++

float suma(float a[], int n);sau

float suma(float [], int);In şablon putem să identificăm doar tipurile parametrilor.Intr-un program putem avea mai multe funcţii cu acelaşi nume dar cu parametri diferiţi sau cu un număr de parametri diferit. Acest lucru se numeşte supraîncărcarea funcţiilor.

4.2 Pasarea parametrilor funcţiilor

La apelarea unei funcţii, parametrii actuali şi variabilele locale ale funcţiei sunt memorate într-o stivă. Există două moduri de a pasa paremetrii unei funcţii: prin valoare şi prin referinţă (adresă). In cazul pasării unui parametru prin valoare, în stivă se pune chiar valoarea parametrului, în cazul pasării prin adresă în stivă se pune adresa parametrului.

Cazul parametrilor pasaţi prin valoare. Dacă în corpul funcţiei modificăm aceşti parametri, valoarea lor se modifică doar în stivă şi nu în programul apelant. In consecinţă, parametrii pasaţi prin valoare nu pot fi parametri de ieşire ai funcţiei.

Cazul parametrilor pasaţi prin referinţă. Dacă în corpul funcţiei modificăm aceşti parametri, valoarea lor se modifică în programul apelant (în stivă este adresa acestor parametri). In consecinţă, pentru ca un parametru al unei funcţii să fie parametru de ieşire, el trebuie pasat prin adresă

Pentru a vedea care este diferenţa între cele două moduri considerăm o funcţiei care să permute valoarea a două variabile de tip întreg. O variantă necorectă este următoarea

void perm(int a, int b){

int c;c = a;a = b;b = c;return;

}

int main(){

int x = 7, y = 12;cout << “valoarile initiale x = “ << x << “ y = “ << y << endl;perm(x, y);cout << “valoarile permutate x = “ << x << “ y = “ << y << endl;

}

In ambele cazuri se tipăreşte aceeaşi valoare, x = 7 şi y = 12. Motivul este acela că parametrii fiind pasaţi prin valoare, în funcţie instrucţiunile

a= b;b = c;

Page 46: Curs Program Are C++

modifică valoarea lor doar în stiva şi nu în programul apelant. In consecinţă, parametrii pasaţi prin valoare nu pot fi parametri de ieşire ai funcţiei. Pentru ca un parametru al unei funcţii să fie parametru de ieşire, el trebuie pasat prin referinţă (adresă). Există două moduri de a pasa parametri prin adresă: utilizarea parametrilor tip referinţă şi tip pointer. Referinţele şi variabilele tip pointer vor fi prezentate pe larg în paragraful următor. Vom prezenta acum doar parametrii tip referinţă. Fie un parametru de un tip T al unei funcţii. Referinţa la un astfel de parametru are tipul T&. Dacă un parametru este tip referinţă, la apelarea funcţiei în stivă se va pune adresa acestui parametru, şi atunci când este modificat în corpul funcţiei el este modificat în programul apelant (este parametru de ieşire).Varianta corectă a funcţiei care permută valorile a două variabile este următoarea

/* functie ce permute valoarea a doua variabile */void perm(int& a, int& b){

int c;c = a;a = b;b = c;return;

}

Apelarea acestei funcţii se face astfel

int main(){

int x = 7, y = 12;cout << “valoarile initiale x = “ << x << “ y = “ << y << endl;perm(x, y);cout << “valoarile permutate x = “ << x << “ y = “ << y << endl;

}

In stivă se pun de această dată adresele variabilor x şi y deoarece parametri funcţiei sunt de tip referinţă. Menţionăm că apelarea funcţiei se face utilizând pentru parametrul referinţă o variabilă din programul apelant (nu o constantă sau expresie), în cazul nostru

perm(x,y)Vom recapitula acum diferenţele între parametrii pasaţi prin valoare şi cei pasaţi prin adresă (referinţă).

Un parametru pasat prin valoare este un parametru de intrare. Valoarea lui actuală este rezultatul evaluării unei constante, variabile sau expresii în programul apelant.

Un parametru tip referinţă este un parametru de ieşire. Argumentul pasat este o variabilă din programul apelant.

Menţionăm că în cazul în care un parametru este un tablou, în stivă se pune adresa primului element al tabloului. In consecinţă, parametri formali tablouri pot fi parametri de ieşire.

Page 47: Curs Program Are C++

Exemplu. Vo defini o funcţie ce calculează suma a doi vectori x şi y de numere reale de dimensiune n. Tipul funcţiei este void. Vectorul sumă va fi z.

/*Calculul sumei a doi vectori

Parametri de intrarex – vectory – vectorn – dimensiunea vectorilor x, y, z

Parametri de iesirez – vector, z = x + y

PreconditiiParametri de intrare sunt initializati

*/void sumvect(double x[], double y[], double z[], int n){

int i;for(i = 0; i < n; i++)

z[i] = x[i] + y[i];}

Vom încheia acest paragraf definind o funcţie care să calculeze media şi dispersia unui şir de numere . Valoarea medie a elementelor şirului este

iar dispersia

Tipul funcţiei va fi void. Media şi dispersia vor fi asociate unor parametri de ieşire ai funcţiei. Prototipul funcţiei va fi

void md(float x[], int n, float& m, float& d);unde x este vectorul de numere de dimensiune n pentru care calculăm media şi dispersia.

#include <iostream.h>/*

Calculul mediei si dispersiei componentelor unui vectorParametri de intrare

x – vectorn – dimensiunea vectorului x

Parametri de iesirem – media componentelor lui xd – dispersia componentelor lui x

PreconditiiParametrii de intrare sunt initializati

*/

Page 48: Curs Program Are C++

void md(float x[], int n, float& m, float& d){

float s = 0;int i;

// calculeaza mediafor(i = 0; i < n; i++)

s = s + x[i];m = s / n;

// calculeaza dispersiafloat d = 0;for(i = 0; i < n; i++)

d = d + (x[i] – m) * (x[i] – m);d = d / (n – 1);return;

}

int main(){

float a[4] = {1.2e-1, -2.34, 1.5, 3.33};float media, dispersia;md(a, 4, media, dispersia);cout << “ media = “ << media << “ dispersia = “ << dispersia << endl;

}

4.3 Recursivitatea

Funcţiile limbajelor C şi C++ sunt recursive. O funcţie se poate apela pe ea însăşi. Vom exemplifica acest lucru cu o funcţie care să calculeze valoarea n! în variantă recursivă şi nerecursivă.

Varianta nerecursivă Varianta recursivă

// calculul factorialului // calculul factorialuluiint fact(int n) int fact(int n){ {

int s = 1; int s;for(int i = 1; i <= n; ++n) if(n = = 1)

s = s * i; s = 1;return i; else

Page 49: Curs Program Are C++

} s = n * fact(n – 1);return s;

}

Vom prezenta stiva la apelarea variantei recursive pentru n = 3. Reamintim că parametrul n şi variabila locală s sunt memoraţi în stivă. După apelul

fact(3)stiva este

N = 3 S = ?

Deoarece n este diferit de unu, se execută instrucţiunea S = n * fact(n – 1)

adică se apelează încă o dată funcţia, fact(2). Stiva este

N=3 S=? N=2 S=?

Se apelează încă o dată funcţia, fact(1), şi stiva este

N=3 S=? N=2 S=? N=1 S=1

După această ultimă apelare se ajunge la înstrucţiunea return, după care variabilele corespunzând ultimei apelări a funcţiei se sterg din stivă. Stiva devine

N=3 S=? N=2 S=2

Din nou se ajunge la instrucţiunea return, variabilele corespunzând acestei apelări se strerg din stivă

N=3 S=6 După care se obţine rezultatul final, valoarea 6.

4.4 Şabloane de funcţii

Şabloanele ne permit să definim funcţii generice în care putem defini tipuri generale ale parametrilor şi a valorii returnate de funcţie. Definirea unui şablon de funcţie se face cu instrucţiunea

template <typename identificator, … > declaraţie de funcţieSă definim de exemplu o funcţie generică pentru calculul maximului a două variabile

/* functie generica ce calculeaza maximul a doua variabile */template <typename T>T maxval(T a, T b){

return (a > b ? a : b);}

Page 50: Curs Program Are C++

Apelarea unui şablon de funcţie se face astfelnume_funcţie <tip>(parametri);

De exemplu, putem calcula maximul a două numere întregi sau reale astfel.

int main(){

// maximul a doua numere intregiint a = 15, x = 20, c;c = maxval<int>(a, x);cout << “ maximul dintre “ << a << “ si “ << b << “ este “ << c << endl;// maximul a doua numere realefloat fa = 3.43, fb = -9.3;cout << “maximul dintre “ << fa << “ si “ << fb << “ este “

<< maxval<float>(fa, fb) << endl;}

Cap. 5 Pointeri şi referinţe

In orice program compilatorul alocă oricărei variabile definite în program o zonă de memorie egală cu numărul de octeţi corespunzând tipului variabilei. In cazul unui tablou se alocă memorie fiecărui component al tabloului. Fie de exemplu instrucţiunea

int a, b, x[3];Compilatorul alocă zone de memorie variabilelor ca mai jos

Compilatorul crează o tabelă cu numele variabilelor şi adresele lor în memorie, de exemplu

a 1000b 1004x 1008

Page 51: Curs Program Are C++

O instrucţiune de atribuire de formaa = b;

este executată astfel: se ia valoarea de la adresa variabilei a (în cazul nostru adresa 1000) şi se pune la adresa variabilei b (în cazul nostru adresa 1004). Limbajele C şi C++ permit definirea unor variabile ce conţin adrese ale altor variabile. Acestea au tipul pointer şi referinţă. După cum vom vedea, aceste variabile se utilizează la transmiterea parametrilor funcţiilor prin adresă, care pot fi parametric de ieşire ai funcţiilor. Pointerii sunt utili şi la prelucrarea tablourilor.

5.1 Pointeri

Un pointer este o variabilă ce conţine adresa unei alte variabile. De exemplu, dacă n este o variabilă de tip int, ce are valoarea 3, iar pn este o variabilă de tipul pointer la int, ce conţine adresa lui n, putem reprezenta cele două variabile astfel

O variabilă de tip pointer se defineşte cu instrucţiuneatip * nume;

De exempluint * pn;

defineşte o variabilă tip pointer la int ce poate conţine adresa unei variabile de tip int. Pentru a calcula adresa unei variabile se utilizează operatorul &. Dacă x este o variabilă oarecare, expresia &x este adresa lui x. Variabilele tip pointer pot fi iniţializate doar cu adrese. De exemplu putem scrie

int n;int * pn; // pn este de tipul pointer la intpn = &n; // initializeaza pn cu adresa lui n

Pentru a obţine valoarea variabilei indicate de un pointer se utilizează operatorul * (numit şi operator de adresare indirectă). Fie de exemplu instrucţiunile

int k, n= 14;int * pn, *pk;

Instrucţiunilek = n;

şipn = &n;k = *pn;

sunt echivalente, k primeşte valoarea 14. (Variabila pn a fost iniţializată în prealabil cu adresa lui n). Instrucţiunile

k = n;şi

pk = &k;

Page 52: Curs Program Are C++

*pk = n;sunt echivalente. In final, instrucţiunile

k = n;şi

pk = &k;pn = &n;*pk = *pn;

sunt echivalente;Exemplu. Fie următoarea secvenţă de instrucţiuni

float x;float * y;

A doua instrucţiune declară că y poate conţine adresa unei variabile de tip float. Instrucţiunile

x = 3.5;şi

y = &x;*y = 3.5;

sunt echivalente.Exemplu. Vom scrie o funcţie care să permute valorile a două variabile ce vor fi parametri de ieşire ai funcţiei. De aceea aceşti parametri vor fi variabile tip pointer.

void perm(int* a, int* b){

int c;c = *a;*a = *b;*b = c;

}

Utilizarea acestei funcţii se face astfel

int main(){

int x = 3, z = -4; // scrie variabilele inainte de permutare

cout << “x = “ << x << “ y = “ << y << endl;perm(&x, &y);

// scrie variabilele dupa permutarecout << “x = “ << x << “ y = “ << y << endl;

}

Limbajul C nu are referinţe. In consecinţă, singurul mod de a declara parametri de ieşire ai unei funcţii în limbajul C, este ca aceştia să fie de tip pointer.Operatorii * şi & sunt asociativi la dreapta. Operatorii * şi & sunt inverşi. Exemplu. Fie instrucţiunile

int a, b;

Page 53: Curs Program Are C++

a = 7;Instrucţiunile

b = a;şi

b = *&a;sunt echivalente.Exemplu. Vom afişa valoarea unor variabile tip pointer.

#include <iostream.h>int main(){

int a, b; // definim variabile tip pointer

int *pa, *pb;

pa = &a;pb = &b;

// se scrie adresa variabilei acout << “ adresa variabilei a “ << &a << “ “ << pa << endl;

// se scrie valoarea variabilei bb = 15;cout << “ valoarea variabilei b “ << b << “ “ << *pb << endl;

}

Prima instrucţiune cout va afişa de două ori acceaşi adresă. A doua instrucţiune cout va afişa de două ori aceeaşi valoare, 15.Menţionăm că numele unui tablou este, prin definiţie, un pointer la primul element al tabloului. Fie de exemplu instrucţiunile

float x[7], * pf;Un pointer la tabloul x are valoarea &x[0] sau x . Putem deci scrie

pf = &x[0];sau, echivalent,

pf = x;Dacă scriem

pf = &x[3];pf va conţine adresa celui de al patrulea element al tabloului. Legătura între tablouri şi variabile tip pointer va fi prezentată pe larg în paragrafele următoare.Exemplu. Să afişăm adresele elementelor unui vector de întregi.

# include <iostream.h>int main(){

int x[5];for(int i = 0; i < 5; i++)cout << “indice “ << i << “ adresa “ << &x[i] << endl;return 0;

Page 54: Curs Program Are C++

}

Exemplu. Fie instrucţiunileint x, y;int * a;int ** b;

Instrucţiunea a doua declară că a poate conţine adresa unei variabile de tip int. A treia instrucţiune declară că b poate conţine adresa unei variabile de tip int * (pointer la int). Fie instrucţiunea

x = 10;Instrucţiunile

y = x;şi

a = &x;y = * a;

şi respectiva = &x;b = &a;y = ** b;

sunt echivalente. Pentru a arăta aceasta, considerăm alocarea variabilelor x, y, a, b în memorie. Adresele acestor variabile pot fi cele din tabela de mai jos

Variabila Adresa în memorie Conţinutx 1000 Valoarea 10y 1004 Valoarea 10a 1008 Adresa 1000b 1012 Adresa 1008

In cazul instrucţiunii y = x;

la execuţia programului valoarea de la adresa variabilei x se depune la adresa variabilei y. Ea este echivalentă cu instrucţiunile

a = &x;y = * a;

Valoarea de la adresa a indică adresa unei alte variabile (x în cazul nostru, deoarece s-a executat instrucţiunea a = &x). Se aplică operatorul * asupra variabilei a şi se obţine conţinutul acestei variabile care este adresa lui x. Conţinutul de la această adresă se pune la adresa lui y. Reamintim că operatorul * este asociativ la dreapta.Instrucţiunea anterioară este echivalentă şi cu instrucţiunile

a = &x;b = &a;y = **b;

După execuţia instrucţiuniia = &x;

variabila a conţine adresa lui x după cum s-a explicat mai înainte. După excuţia instrucţiunii

Page 55: Curs Program Are C++

b = &a;variabila b conţine adresa lui a. Instrucţiunea y = **b se execută aplicând operatorul * asupra variabilei b, rezultatul fiind valoarea variabilei b, adică adresa variabilei a. Se aplică operatorul * asupra acestei valori (ceea ce este achivalent cu *a) şi se obţine conţinutul lui a care este adresa variabilei x. Valoarea de la adresa lui x se depune în y.Ca un ultim exemplu considerăm următoarele instrucţiuni

int x, y;int *a;int **aa;int ***aaa;int ****aaaa;

Aceste instrucţiuni declară că a poate conţine o adresă de variabilă tip int, aa poate conţine o adresă de tip int *, aaa poate conţine o adresă de tip int **, etc. Vom iniţializa pe x şi variabilele tip pointer astfel:

x = 10;a = &x;aa = &a;aaa = &aa;aaaa = &aaa;

Instrucţiuniley = x;y = *a;y = **aa;y = ***aaa;y = ****aaa;

produc acelaşi rezultat, adicăy = 10;

Instrucţiunile cout << x cout << *a cout << **aa cout << ***aaa cout << ****aaaa;

produc afişarea aceleiaşi valorix = 10

La tipărirea valorilor variabilelor de tip pointer, se tipăresc adresele conţinute de acestea în hexazecimal. Instrucţiunile

cout << &x << ‘\t’ << a << endl;cout << &a << ‘\t’ << aa << endl;cout << &aa << ‘\t’ << aaa << endl;cout << &aaa << ‘\t’ << aaaa << endl;

afişază respectiv următoarele valori:adresa lui xadresa lui aadresa lui aaadresa lui aaa

Page 56: Curs Program Are C++

Considerăm următoarele instrucţiuniint x, y = 4;x = y;

Operatorul = aplicat asupra celor doi operanzi, în cazul nostru x şi y, ia valoarea de la adresa corespunzând variabilei din stânga şi o memorează şi o memorează la adresa corespunzătoare variabilei din dreapta. In consecinţă, în partea stângă a semnului egal putem utiliza valorile unor variabile tip pointer.Considerăm următoarele instrucţiuni

int x, y;int * a;a = &y;x = 10;*a = x;

Instrucţiuneacout << “x = “ << x << “ y = “ << y << endl;

va afişa valorilex = 10 y = 10

Modul de excuţie a acestor instrucţiuni este următorul: instrucţiuneaa = &y;

memorează în variabila a adresa lui y. Instrucţiunea *a = x;

se execută în felul următor: operatorul * are o prioritate mai mare decât operatorul =, în consecinţă se aplică operatorul * asupra variabilei a şi se obţine adresa lui y. Se aplică apoi operatorul = asupra adresi lui y (şi asupra adresei lui x) şi y primeşte valoarea 10. In consecinţă, instrucţiunile

a = &y;*a = x;

sunt echivalente cu instrucţiuneay = x;

Fie de exemplu următoarele instrucţiuni:float x, y; float * a;float ** aa;float *** aaa;float **** aaaa;

Aceste instrucţiuni declară că a poate conţine adresa unei variabile tip float, aa poate conţine adresa unei variabile de tip float *, etc. Fie instrucţiunile

a = &x;aa = &a;aaa = &aa;aaaa = &aaa;

Fie acum instrucţiunea y = 3.45e-1;

Instrucţiunile*a = y;**aa = y;

Page 57: Curs Program Are C++

***aaa = y;****aaaa = y;

au acelaşi effect, variabila x primeşte valoarea variabilei y. De exemplu**aa = y;

aplică de două ori operatorul *. Ea se execută astfel:se aplică operatorul * asupra variabilei aa şi se obţine conţinutul acesteia, adică adresa variabilei a. Se aplică încă o dată operatorul * asupra rezultatului şi se obţine adresa variabilei x. In final operatorul = atribuie variabilei x valoarea lui y.Orice variabilă tip pointer trebuie iniţializată înainte de a fi utilizată. De exemplu, următoarea secvenţă de instrucţiuni nu este corectă

int *pn;*pn = 5;

deoarece variabila pn nu a fost iniţializată. O secvenţă corectă esteint *pn;int x;pn = &x;*pn = 5;

In acest caz variabila pn a fost iniţializată cu adresa unei variabile de tip întreg.

5.2 Referinţe

O referinţă (sau adresă) este un alt nume pentru o variabilă. Instrucţiunea de declarare a unei referinţe este

Tip& nume_referinţă = nume_variabilăunde

Tip este tipul variabilei nume_variabilă nume_referinţă este numele variabilei referinţă (adresă).

Variabila nume_variabilă trebuie să fie declarată înainte şi să aibe tipul Tip. De exemplu

int& rx = x;declară pe rx ca fiind o referinţă a lui x (variabila x de tip întreg a fost declarată anterior). Numele x şi rx sunt două nume diferite pentru aceeaşi variabilă. Ele au totdeauna aceeaşi valoare. Pentru a verifica acest lucru vom considera următorul exemplu.

int main(){

float f = 12.8;float& fr = f;cout << “f = “ << f << “ fr = “ << fr << endl;fr = fr + 25.3;cout << “f = “ << f << “ fr = “ << fr << endl;f = f – 3.23;cout << “f = “ << f << “ fr = “ << fr << endl;

}

Page 58: Curs Program Are C++

După fiecare instrucţiune de scriere, valorile f şi fr vor fi aceleaşi. După cum vom vedea în paragraful următor, dacă un parametru formal al unei funcţii este de tip referinţă, el poate fi un parametru de ieşire. O referinţă nu este o variabilă separată. Fie următorul program în care scriem adresa unei variabile şi a referinţei la variabilă.

int main(){

double dbl = 23.44.double& dbref = dbl;// tipareste adresele lui dbl si dblrefcout << “adresa lui dbl = “ << &dbl

<< “ adresa lui dblref = “ << &dblref << endl;double& db2 = dbl;double& db3 = dbref;cout << “&dbl = “ << &dbl << “ &db2 = “ << db2

<< “ &db3 = “ << db3 << “ & dbref = “ << dbref << endl;}

In toate cazurile se va tipări aceeaşi valoare. Vom încheia acest paragraf definind o funcţie care să calculeze valoarea medie şi dispersia unui şir de numere reale. Formulele corespunzătoare au fost prezentate anterior. Funcţia va avea ca parametri de intrare vectorul x şi dimensiunea kui n, iar ca parametri de ieşire valorile calculate, media şi dispersia. Funcţia va avea tipul void.

void md(float x[], int n, float& m, float& d){

// calculeaza suma componentelorfloat s = 0;int i;for(i = 0; i < n; i++)

s = s + x[i];s = s / n;m = s;// calculeaza dispersiafloat ds = 0;for(i = 0; i < n; i++)

ds = ds + (x[i] – s) * (x[i] – s);d = s / (n – 1);

}

int main(){

float a[3] = {1.22, -3.76, 16.02};float med, disp;md(a, 3, med, disp);

Page 59: Curs Program Are C++

cout << “media = “ << med << endl << “dispersia = “ << disp << endl;

}

5.3 Parametri funcţiilor

La apelarea unei funcţii, parametrii acesteia se pun într-o stivă, împreună cu variabilele locale ale funcţiei. După tipul parametrului, în stivă se pune valoarea sau adresa parametrului. Considerăm de exemplu următoarea funcţie care adună două numere reale:

float funad(float a, float b){

float c;c = a + b;return c;

}

Considerăm o instrucţiune ce apelează această funcţiex = funad(10.3, 20.44);

La apelarea funcţiei în stivă se alocă spaţiu pentru parametrii funcţiei, a şi b, şi pentru variabila locală c

In acest caz, dacă modificăm un parametru, de exemplu a, valoarea parametrului se modifică doar în stivă, şi nu în programul apelant. In acest caz parametrii funcţiei sunt doar parametri de intrare. Pentru ca parametri unei funcţii să fie parametri de ieşire, ei trebuie să fie de tip pointer sau referinţă. In acest caz dacă se modifică valoarea parametrului în funcţie, modificarea valorii se face în programul apelant. Vom rescrie funcţia anterioară astefel încât rezultatul să fie asociat unui parametru. Nu vom mai avea o valoare calculată asociată numelui, deci tipul funcţiei va fi void.

void funad1(float a, float b, float * c){

float r;r = a + b;*c = r;return;

}

Apelarea acestei funcţii se face astfel:float x, y, rez;x = 1.23;y = -34.56;funad1(x, y, &rez);

O altă posibilitate este de a defini o variabilă tip pointer care să conţină adresa variabilei rez.

a=10.3 b=20.44 c

Page 60: Curs Program Are C++

float * m;m = &rez;funad1(x, y, m);

In acest caz stiva va fi următoarea.

A = 1.23 B = -34.56 Adresa lui c

5.4 Pointeri la funcţii

O funcţie are un punct de intrare care este linia ei de definiţie. Acest punct de intrare este o adresă de memorie care poate fi atribuită unei variabile tip pointer şi care poate fi utilizată la apelarea funcţiei. In programul care apelează funcţia trebuie să definim o variabilă pointer de tipul funcţiei respective. Considerăm şablonul definiţiei unei funcţii

Tip nume(lista de parametri);Tipul unei variabile pointer la această funcţie este

Tip (*ident) (lista de parametri);unde tip şi lista de parametri din variabila pointer corespund cu tip şi lista de parametri din şablonul definiţia funcţiei.Exemplu. Fie o funcţie f ce calculează suma a două numere întregi x, y.

int f(int x, int y){

return x + y;}

Şablonul acestei funcţii este int f(int, int);

Menţionăm că în şablon putem specifica şi numele parametrilor. Numele parametrilor din şablon pot fi diferite de cele din definiţia funcţiei. Putem declara ca şablon pentru funcţia anterioară

int f(int a, int b);Tipul unei variabile pointer la această funcţie este

int (*ident)(int , int);unde ident este numele variabilei tip pointer. Apelarea unei funcţii folosind o variabilă de tip pointer se face atribuind variabilei pointer ca valoare numele funcţiei şi aplicând opoeratorul () asupra variabilei tip pointer.Vom apela acum funcţia f de mai sus direct şi folosind o variabilă tip pointer.

int main(){

int a = 5, b = 7, sm; // apelarea directa a funcţiei

sm = f(a, b);cout << “suma numerelor “ << a << “ si “ << b << “ este “ sm << endl;

// prt este o variabila pointer la functia fint (*ptr)(int, int);

Page 61: Curs Program Are C++

// se initializeaza ptrprt = f;

// se apeleaza functia f prin pointersm = (*ptr)(a, b);cout << “suma numerelor “ << a << “ si “ << b << “ este “ sm << endl;return 0;

}

In consecinţă, apelarea unei funcţii se poate face astfel scriind numele funcţiei urmat în paranteze de parametri actuali ca termen într-o

expresie. (aplicând asupra numelui funcţiei operatorul ()). Atribuind unei variabile tip pointer ca valoare adresa punctului de intrare în

funcţie. Adresa punctului de intrare este chiar numele funcţiei. Apelarea se face aplicând operatorul * asupra variabilei tip pointer.

5.5 Declararea variabilelor tip pointer la funcţie

O instrucţiune de declarare a unui tip pointer la funcţie poate conţine următorii operatori, scrişi aici în ordinea priorităţilor

1. [], ()2. *, const3. tipuri de date

Operatorii [] şi () sunt asociativi la stânga. Operatorii * şi const sunt asociativi la dreapta. Instrucţiunea poate conţine şi un identificator care este numele variabilei. Interpretarea şabloanelor se face considerând operatorii în ordinea priorităţii lor, [] reprezintă tablouri, () reprezintă funcţii, * reprezintă un pointer, etc.Exemplu. Fie şablonul

float *f();Interpretarea lui se face astfel:

float *f() f estefloat * () funcţie care returnează float * pointer la o valoarefloat tip float

f este o funcţie ce returnează un pointer de tip float.Exemplu. Fie şablonul

float (*pf) ();Interpretarea lui se face astfel:

pf este pointer la o funcţie ce returnează o valoare de tip float.

float (*pf)() pf estefloat (*) () pointer lafloat () funcţie care returnează o valoarefloat tip float

Page 62: Curs Program Are C++

Exemplu.int (*r)(int, int);

r este un pointer la o funcţie cu doi parametri de tip int, int şi care returnează o valoare de tip int.Exemplu. Fie următoarea instrucţiune

const char * pc = “abcd”;Reamintim că şirul de caractere “abcd” este un vector cu 5 componente tip caracter, ‘a’, ‘b’, ‘c’, ‘d’ şi ‘\0’ (şirurile sunt terminate prin zero). Prin această instrucţiune compilatorul generează un vector cu 5 componente tip character şi atribuie acestui vector variabiler tip pointer pc. Interpretarea tipului variabilei pc se face astfel:

const char * pc pc esteconst char * pointer laconst char constantchar char

Această instrucţiune declară un pointer la un vector constant de caractere (am ţinut cont că operatorii * şi const se citesc de la dreapta la stânga). In consecinţă, nu putem scrie

pc[3] = ‘c’;deoarece şirul de caractere este constant, dar putem scrie

pc = “ghij”;adică pointerul poate fi iniţializat cu o altă valoare.Exemplu. Fie acum instrucţiunea

char * const cp = “abcd”;Interpretarea ei se face astfel:

char * const cp cp estechar * const constantchar * pointer lachar char

Instrucţiunea declară un pointer constant la un vector de caractere. In consecinţă putem modifica vectorul, de exemplu putem scrie

cp[3] = ‘c’;dar nu putem scrie

cp = “ghij”;deoarece pointerul este o constantă.Exemplu. Considerăm şablonul

char (*(*f())[])();Interpretarea lui se face astfel:

char (*(*f())[])() f estechar (*(*())[])() funcţie ce returneazăchar (*(*)[])() pointer lachar (*[])() Vector de char (*)() pointeri la

Page 63: Curs Program Are C++

char () funcţie ce returneaza o valoarechar tip char

f este o funcţie ce returnează un pointer la un vector de pointeri la o funcţie ce returnează o valoare de tip char.

5.6 Pointeri şi tablouri unidimensionale

Prin definiţie, o variabilă tip tablou conţine adresa primului element al tabloului (elementul cu indicele zero al tabloului). Variabila este un pointer constant la primul element al tabloului. Fie de exemplu instrucţiunea

char buffer[20];Variabila buffer conţine adresa primului element al tabloului (adresa elementului buffer[0]). In consecinţă, această valoare poate fi atribuită unei variabile tip pointer la tipul elementelor tabloului. Dacă scriem

char * px;px = buffer;

variabila px conţine adresa variabilei buffer[0] şi, în consecinţă,*px

este valoarea variabilei buffer[0].La execuţia unui program, numele unui tablou este convertit la un pointer la primul element al tabloului. Prin definiţie, în cazul unui tablou x, scrierile

x[i]şi

*(x + i)sunt echivalente. Exemplu. Fie un vector cu 5 componente tip float şi un vector z cu 3 componente întregi. Se cere să scriem componentele vectorilor. Un program posibil este următorul

#include <iostream.h>int main (){

float x[5] = {1.23, 6.89, -8.5e2, 0, 9.01};int z[3] = {-11, 2, 4};int i;

// scrie componentele vectorului xfor(i = 0; i < 5; i++)

cout << ‘\t’ << x[i];cout << endl;

// scrie componentele vectorului zfor(i = 0; i < 3; i++)

cout << ‘\t’ << *(z + i);cout << endl;

}

Page 64: Curs Program Are C++

Programul poate fi rescris astfel. Definim o variabilă tip pointer la tipul float ce va conţine pe rand adresele componentelor x vectorului şi o variabilă tip pointer la tipul int pentru vectorul z. Variabilele vor fi initializate cu adresele vectorilor.

# include <iostream.h>int main(){

float x[5] = {1.23, 6.89, -8.5e2, 0, 9.01};int z[3] = {-11, 2, 4};int i;float * px;int * pz;px = x;pz = z;

// scrie componentele vectorului x for(i = 0; i < 5; i++)

cout << ‘\t’ << *(px + i)cout << endl;

// scrie componentele vectorului zfor(i = 0; i < 5; i++)

cout << ‘\t’ << pz[i];cout << endl;return 0;

}

Operaţiile cu variabilele tip pointer sunt adunarea şi scăderea unor valori întregi. La adunarea sau scăderea unei valori întregi dintr-o variabilă tip pointer, la variabilă se adună sau se scade valoarea respectivă înmulţită cu dimensiunea în octeţi a tipului variabilei (unu pentru tipul char, patru pentru tipul int sau float, etc.) astfel încât variabila conţine adresa unui alt element de tablou. Fie din nou instrucţiunile

char buffer[20];char * px;px = buffer;

Expresia px + 1 este adresa variabilei buffer[1], iar *(px + 1) este chiar valoarea componentei buffer[1], px + i este adresa variabilei buffer[i] iar *(px + i) este chiar valoarea componentei buffer[i]. Putem deci să rescriem programul precedent astfel

# include <iostream.h>int main(){

float x[5] = {1.23, 6.89, -8.5e2, 0, 9.01};int i;float * px;px = x;

// scrie componentele vectorului xfor(i = 0; i < 5; i++)

Page 65: Curs Program Are C++

{cout << ‘\t’ << *px;px = px + 1;

}cout << endl;return 0;

}

Echivalent, instrucţiunea de scriere poate fi

for(i = 0; i < 5; i++){

cout << ‘\t’ << *px++;}

Pentru a vedea că ultima formă este corectă reamintim modul de execuţie a operatorului postfix ++. Instrucţiunea

*px++este echivalentă cu instrucţiunile

*px;px++;

In expresie se utilizează valoarea neincrementată a variabilei, după care variabila este incrementată. In consecinţă, se utilizează expresia *px după care variabila px este incrementată. Vom incheia acest paragraph cu un program care să scrie adresele componentelor unui vector utilizand o variabilă tip pointer

# include <iostream.h>int main(){

float x[5] = {1.23, 6.89, -8.5e2, 0, 9.01};int i;float * px;px = x;

// scrie adresele componentele vectorului xcout << “adresele componentelor vectorului” << endl;for(i = 0; i < 5; i++){

cout << i << ‘\t’ << px << endl;px = px + 1;

}return 0;

}

5.7 Siruri tip C

Page 66: Curs Program Are C++

Şirurile tip C sunt vectori de caractere ASCII la care ultimul caracter este ‘\0’. Bibliotecile standard ale limbajelor C şi C++ au numeroase funcţii pentru lucrul cu şiruri tip C. Câteva dintre aceste funcţii, ale căror prototipuri se află în biblioteca <string.h>, sunt prezentate în cele ce urmează.Funcţia

char * strcpy(char * s1, const char * s2);copiază şirul s2 în s1, inclusiv caracterul ‘\0’. Funcţia returnează adresa şirului s1. Parametrul s2 este declarat de tip const, adică funcţia nu modifică şirul s2.Funcţia

int strcmp(const char * s1, const char * s2);compară cele două şiruri, caracter cu caracter, până la primul caracter diferit. Rezultatul este un număr < 0, = 0, sau > 0, după cum sunt caracterele diferite comparate. Exemple de utilizare a funcţiei strcmp sunt prezentate în tabelul de mai jos

strcmp(“abc”, “bcd”) < 0 ‘a’ < ‘b’strcmp(“xyz”, “xyz”) = 0 şirurile sunt egalestrcmp(“abcd”, “abc”) > 0 şirul “abcd” este mai lung

Funcţiasize_t strlen(const char * s);

calculează lungimea şirului s (numărul de caractere ce preced ‘\0’).Funcţia

char * strchr(const char * s, int c);caută prima apariţie a caracterul c (convertit la char) în şirul s. Funcţia returnează un pointer la caracterul găsit sau NULL în caz contrar.Următoarele funcţii manipulează caractere tip ASCII. Prototipurile lor se află în biblioteca <ctype.h>.

Funcţie Descriereint isalnum (int c); Test dacă un caracter este alfanumericint isalpha (int c); Test dacă un caracter este alfabetic int isdigit (int c); Test dacă un caracter este o cifră zecimalăint isxdigit(int c); Test dacă un caracter este o cifră hexazecimalăint islower (int c); Test dacă un caracter este literă micăint isspace (int c); Test dacă un caracter este spaţiu (‘ ‘, ‘\n’, ‘\t’)int isupper (int c); Test dacă un caracter este literă mare

Funcţiile au un rezultat diferit de zero dacă argumentul este conform descrierii funcţiei. Următoarele funcţii convertesc literele mari în litere mici şi invers.

Funcţie Descriereint tolower(int c); Converteşte în litere miciint toupper(int c); Converteşte în litere mari

Vom exemplifica utilizarea acestor funcţii tipărind caracterele alfabetice, alfanumerice, cifrele, literele mici şi mari ale codului ASCII. Vom utiliza constanta predefinită

Page 67: Curs Program Are C++

UCHR_MAX din biblioteca <limits.h> care dă valoarea maximă a unui obiect de tipul unsigned char (această valoare este 255). Vom construi o funcţie care generează toate caracterele codului ASCII (toate numerele de la 0 la 255) şi le vom tipări în fiecare caz caracterele pentru care funcţiile de mai sus returnează o valoare pozitivă (valoarea adevărat). Funcţia pe care o vom construi va avea ca parametru un pointer de tipul funcţiilor de mai sus. Prototipul acestor funcţii este

int nume(int);iar un pointer la o asemenea funcţie are tipul

int (* fn)(int);

#include <stdlib.h>#include <iostream.h>void prtcars(const char * nume, int (*fn)(int)){

char c; // scrie numele functiei

cout << nume << “ : “; /* genereaza toate caracterele si scrie pe cele pentru

care functia are valoarea adevarat */for(c = 0; c < UCHR_MAX; ++c)

if((*fn)(c))cout << c;

cout << endl;}int main(){

ptrcars(“isdigit”, &isdigit);ptrcars(“islower”, &islower);ptrcars(“isupper”, &isupper);ptrcars(“isalpha”, &isalpha);ptrcars(“isalnum”, &isalnum);ptrcars(“isxdigit”, isxdigit);

}

Exerciţiu. Să se scrie un program care să convertească literele mici ale unui şir în litere mari.Vom exemplifica utilizarea pointerilor la tablouri unidimensionale construind o funcţie care copiază un şir în altul (ca şi funcţia standard strcpy). Prototipul ei va fi

void copy(char * s1, char * s2);unde şirul s2 este sursa iar şirul s1 este destinaţia.O primă variantă este

void copy(char * s1, char * s2){

int len = strlen(s2);for(i = 0; i < len + 1; i++)

Page 68: Curs Program Are C++

s1[i] = s2[i];

}

O altă variantă este

void copy(char * s1, char * s2){

int i;for(i = 0; (s1[i] = s2[i]) != ‘\0’; i++)

;}

Condiţia care se testează pentru execuţia instrucţiunii for este(s1[i]=s2[i]) != ‘\0’;

Ea este testată astfel. Se execută instrucţiunea de atribuires1[i] = s2[i]

după care rezultatul s1[i] este comparat cu valoarea ‘\0’, care este caracterul de terminare al şirului s2. Reamintim că operatorul de atribuire are ca rezultat valoarea atribuită.O altă variantă utilizează pointeri şi faptul că şirurile tip C sunt terminate prin zero.

void copy(char * s1, char * s2){

while(*s1 = *s2){

s1++;s2++;

}}

Expresia testată pentru execuţia instrucţiunii while este*s1 = *s2

Rezultatul evaluării expresiei este valoarea *s1 (un caracter copiat în şirul s1). Instrucţiunea while se execută pană când caracterul ‘\0’ al şirului s2 este copiat în s1.In final putem scrie varianta

void copy(char * s1, char * s2){

while(*s1++ = *s2++);

}

care este o scriere condensată a variantei anterioare.Alte funcţii ce prelucrează şiruri de caractere sunt

int atoi(char * s);double atof(char * s);

Page 69: Curs Program Are C++

care convertesc un şir de caractere într-un număr întreg şi respectiv real. Prototipurile acestor funcţii se află în biblioteca <stdlib.h>. Un exemplu de utilizare a acestor funcţii poate fi următorul. Fie două şiruri ce conţin numere. Vom converti aceste şiruri în numere şi vom efectua produsul lor.

# include <stdlib.h># include <iostream.h>int main(){

char * s1 = “-123”;char * s2 = “1.22e-1”;int x;double y;

// scrie sirurilecout << “sirul s1 : ” << s1 << endl << “sirul s2 : ” << s2 << endl;

// converteste sirurile in numerex = atoi(s1);y = atof(s2);

// scrie numerelecout << “x = “ << x << “ y = “ << y << endl;cout << “x * y = “ << x * y << endl;

}

5.8 Pointeri şi tablouri multidimensionale

Tablourile se definesc conform urmăroarei diagrame sintactice

Elementele tablourilor sunt memorate pe linii. De exemplu, tabloulint x[2][3];

este compus din elementelex[0][0], x[0][1], …, x[1][0], …x[1][2]

El este considerat ca un vector cu 2 elemente, x[0] şi x[1], fiecare element fiind un vector cu trei elemente de tip int. Operatorul [] selectează un element al unui tablou (operator de selecţie). El este un operator binar, operandul stâng fiind un tablou, operandul drept fiind un indice. De exemplu, x[0] selectează primul element din x care este vectorul

x[0][0], x[0][1], x[0][2]Aplicând operatorul de selecţie asupra lui x[0], selectăm un element al acestuia, de exemplu x[0][1]. Reamintim că operatorul de selecţie este asociativ la stânga. Indicii unui element de tablou pot fi orice expresii întregi pozitive. Exemplu. Să construim o funcţii care să citească matrice cu două linii şi două coloane. Funcţia poate fi următoarea

Page 70: Curs Program Are C++

void rdmat(float a[2][2]){

int i, j;for(i = 0; i < 2; i++)

for(j = 0; j < 2; j++)cin >> a[i][j];

}

Numele unui tablou este prin definiţie un pointer la primul element al tabloului. Elementele tabloului pot fi selectate folosind operatorul *. Fie de exemplu tabloul

double x[3];După cum am spus anterior, prin definiţie, expresiile

x[i]şi

*(x+i)sunt echivalente. Considerăm tabloul

float a[2][3];Expresia

a[i][j]este echivalentă cu

(a[i])[j]care, conform celor de mai sus, este echivalentă cu

*((a[i]) + j)care este echivalentă cu expresia

*(*(a + i) +j)Ca exemplu de utilizare a pointerilor la tablouri bidimensionale să construim o funcţie care să scrie elementele unei matrice cu două linii şi două coloane. Funcţia poate fi

void wrmat(float x[2][2]){

int i, j;for(i = 0; i < 2; i++){

for(j = 0; j < 2; j++)cout << a[i][j] << “ “;

cout << endl;}

}

Utilizand cele de mai sus putem rescrie funcţia ca

void wrmat(float x[2][2]){

Page 71: Curs Program Are C++

int i, j;for(i = 0; i < 2; i++){

for(j = 0; j < 2; j++)cout << *(*(a + i) +j) << “ “;

cout << endl;}

}

Reamintim căa[i]

selectează un element al tabloului a, aici o linie, deci *(a + i)

selectează o linie a tabloului a.

5.9 Parametrii funcţiei main. Parametrii liniei de comandă

Prototipul funcţiei main() utilizat pană acum esteInt main();

Funcţia main poate avea următoarul prototipint main(int argc, char * argv[]);

Considerăm al doilea parametru din această definiţie char * argv[]

char * argv [] argv este char * [] vector de char * pointeri lachar char

argv reprezină un vector de pointeri la character, iar argc este dimensiunea acesui vector. Pointerii sunt adresele unor şiruri de caractere ce corespund argumentelor liniei de comandă. Primul şir de caractere este chiar numele programului. Ca exemplu fie un program ce tipăreşte argumentele liniei de comandă.

int main(int argc, char * argv[]){

int i;for(i = 0; i < argc; ++i)

cout << argv[i] << endl;}

Un mod de a furniza parametri liniei de comandă este de a rula programul într-o fereastră DOS, de la linia de comandă

aplicatie.exe parametru1 parametru2 … parametrun

Page 72: Curs Program Are C++

Alt mod este de a introduce aceşti parametri într-o cutie de dialog a mediul de programare.

Cap. 6 Fişiere tip C

In limbajele C şi C++ fişierele sunt considerate ca un şir ordonat de octeţi. Există două tipuri de fişiere, text şi binare.

Un fişier tip text este un şir ordonat de caractere grupate în linii. Fiecare linie constă din zero sau mai multe caractere plus un caracter ‘\n’ (return).

Un fişier binar este un şir ordonat de octeţi.Menţionăm că fiecare fişier pe disc are o etichetă ce conţine numele fişierului, adresa de început şi adresa de sfarşit a fişierului.In program fiecare fişier este asociat unui stream. Un stream este asociat unui fişier prin operaţia de deschidere a fişierului. Pentru prelucrare, un fişier se deschide în citire. Când se crează un fişier el se deschide în scriere. La deschiderea unui fişier în scriere se crează o etichetă corespunzătoare acestui fişier şi se completeză numele fişierului şi adresa de început. La deschiderea în citire sistemul de operare caută pe disc fişierul cu numele specificat şi memorează adresele de început şi de sfarşit ale fişierului. Un stream este disociat de un fişier prin operaţia de închidere a fişierului. La închiderea unui fişier în creare se completează eticheta fişierului cu adresa ultimului octet al fişierului. Fişierele binare au un indicator de poziţie ce dă adresa următorului octet de citit sau scris. Acest indicator este iniţial pus la zero şi este actualizat de operaţiile de citire sau scriere.Un fişier poate avea o zonă de memorie asociată (buffer) în care se citesc date din fişier sau din care se scriu date în fişier.

Page 73: Curs Program Are C++

Limbajele C şi C++ au tipul de structură FILE ce poate înregistra toate informaţiile necesare pentru controlul unul stream, inclusiv:

Numele fişierului Indicatorul de poziţie al fişierului Indicator de eroare poziţionat dacă a apărut o eroare intrare/ieşire Indicatorul de sfârşit de fişier, eof, (end of file)

Prototipurile funcţiilor ce efectuează operaţii intrare/ieşire se află în biblioteca <stdio.h>. Funcţiile importante pentru prelucrarea fişierelor sunt următoarele.

FILE* fopen(const char * filename, const char * mode); Această funcţie deschide un fişier. Ea crează o structură tip FILE cu informaţii despre fişier. Parametrii acestei funcţii sunt

1. filename - un pointer la şirul de caractere ce dă numele fişierului.2. mode - un pointer la un şir de caractere ce dă modul de deschidere.

Parametrul mode poate avea următoarele valori: “r” – fişier text deschis în citire“w” - fişier text deschis în scriere“rb” – fişier binar deschis în citire“wb” - fişier binar deschis în scriere

int fclose(FILE * stream); Funcţia închide un fişier. Ea returnează valoarea zero dacă operaţia a avut succes.

int feof(FILE * stream); Funcţia returnează o valoare diferită de zero dacă s-a detectat sfârşitul unui fişier în timpul operaţiei de citire precedente.

int remove(char * filename); şterge fişierul cu filename. Funcţia returnează o valoare diferită de zero dacă operaţia avut succes.

La lansarea în execuţie a unui program există trei streamuri standard tip text deschise: stdin – fişierul standard de intrare asociat tastaturii stdout - fişierul standard de ieşire asociat ecranului stderr - fişierul standard de ieşire pentru scrierea mesajelor de eroare

Exemplu. La prelucrarea oricărui fişier text în citire vom avea următoarele instrucţiuni

FILE * fp;fp = fopen(“numefisier”, “r”);

// prelucreaza fisierulfclose(fp);

Menţionăm că la citirea secvenţială a unui fişier, după fiecare operaţie de citire trebuie să testăm indicatorul de sfarşit al fişierului. La întalnirea sfarşitului de fişier funcţiile de citire returnează o valoare specifică. Citirea secvenţială a unui fişier se face cu instrucţiunea while ce va testa acest indicator.

6.1 Fişiere tip text

In cazul fişierelor tip text avem două tipuri de funcţii pentru operaţiile de intrare / ieşire: funcţii pentru intrări / ieşiri cu format la care operaţiile de citire / scriere se fac

sub controlul unui format funcţii care citesc / scriu caractere

Page 74: Curs Program Are C++

6.1.1 Funcţii intrare / ieşire cu format

Aceste funcţii scriu sau citesc valori în sau din fişiere pe disc, fişierele standard sau şiruri de caractere. Pentru scriere, aceste funcţii sunt:

int fprintf(FILE * stream, const char * format, argumente);int printf( const char * format, argumente);int sprintf(char * s, const char * format, argumente);

Pentru citire, funcţiile suntint fscanf(FILE * stream, const char * format, argumente);int scanf( const char * format, argumente);int sscanf(char * s, const char * format, argumente);

Parametrul format este un şir de caractere compus din specificatori de conversie definiţi de % şi alte caractere. Specificatorii de conversie sunt

%d – întreg cu semn în baza 10%i – întreg cu semn%o – întreg în baza 8%u – întreg în baza 10 fară semn%x – întreg în baza 16 cu semn%c – caracter%s – şir de caractere%f – număr real %e – număr real cu exponent

După % poate urma un număr întreg care dă lungimea minimă a câmpului în cazul instrucţiunii printf şi lungimea maximă a campului în cazul instrucţiunii scanf. La scrierea numerelor cu specificatorul %f putem specifica şi numărul de cifre ale părţii subunitare. De exemplu specificatorul %7f descrie un număr real ce ocupă 7 poziţii, iar specificatorul %7.3f descrie un număr real ce ocupă 7 poziţii din care 3 sunt pentru partea subunitară.Exemplu. Vom scrie un program care calculează valoarea unei expresii

pentru valori ale lui x cuprinse între 1 şi 2 cu pasul 0.2. Valorile expresiei vor fi scrise pe ecran şi într-un fişier cu numele rez.txt. Specificatorii de format vor fi %4f pentru variabila x şi %6f pentru expresie. Valoarea lui x va ocupa 4 caractere iar cea a lui z 6 caractere. Valorile vor fi separate de un caracter tab, ‘\t’. Formatul se încheie cu un caracter ‘\n’.

#include <stdio.h>#include <math.h>int main void(){

FILE * fp;fp = fopen(“rez.txt”, “w”);int i;float x, z;

Page 75: Curs Program Are C++

// scrie antetulprintf(“x\tf(x) \n”);fprintf(fp, “x\tf(x) \n”);

// calculeaza valorile expresiei si scrie aceste valori in fisierefor(i = 0; i < 6; i++){

z = (1 + cos(2 * x) + 2 * sin(x)) / (x + log(fabs(x)));printf(“ %4f \t %6f \n”, x, z);fprintf(fp, “ %4.1f \t %6.2f \n”, x, z);x = x + 0.2;

}fclose(fp);return 0;

}

In programul anterior puteam defini un şir de caractere cu specificatorii de formatchar * format = “ %4f \t %6f \n”;

şi apoi instrucţiunile de scriere suntprintf(format, x, z);fprintf(fp, format, x, z);

Exemplu. Să citim un număr hexazecimal de la tastatură şi să-l scriem în format zecimal şi hexazecimal pe ecran.

#include<stdio.h>int main(){

int i;printf(“introduceti un numar hexazecimal\n”);scanf(“%x”, &i);printf(“\n numarul zecimal %d\n numarul hexazecimal%x\n”, i, i);

}

Menţionăm că parametrii funcţiei scanf sunt de tip pointer (sunt parametri de ieşire). In consecinţă, la apelarea funcţiei scanf în program utilizăm adresa variabilei i.

scanf(“%x”, &i);Exemplu. Consideram un vector cu 5 componente numere reale. Vom afişa componentele vectorului sub forma Element Valoare

x[0] ….. x[1] …..

# include <stdio.h>int main(){

double x[5] = {1.23, -23, 0.98, 4.12, 5.1};int i;

Page 76: Curs Program Are C++

printf(“Element\tValoare”); // scrie componentele vectorului

for(i = 0; i < 5; i++)printf(“x[%d] \t\t%d\t”, i, x[i]);

return 0;}

6.1.2 Funcţii intrare / ieşire tip caracter

Funcţiaint fgetc(FILE * stream);

citeşte un character din fişierul de intrare şi avansează indicatorul de poziţionare al fişierului cu valoarea unu. Dacă se întâlneşte sfârşitul de fişier, funcţia returnează valoarea EOF care este o constantă predefinită în biblioteca <stdio.h>.Funcţia

int getchar(); citeşte un character din streamul stdin. Ea este echivalentă cu funcţia fgetc(stdin);Funcţia

int fputc(int c, FILE * stream); scrie caracterul c în streamul specificat de stream. Funcţia returnează caracterul scris sau constanta EOF în caz de eroare.Funcţia

int putchar(int c); scrie caracterul c in streamul stdout.Funcţia

int ungetc(int c, FILE * stream); pune caracterul c în streamul de intrare.Următoarele funcţii citesc sau scriu linii din fişiere tip text.Funcţia

char * fgets(char * s, int n, FILE * stream);citeşte cel mult n – 1 caractere în vectorul s. Ea se opreşte dacă întâlneşte caracterul ‘\n’ sau sfârşitul de fişier. După ultimul caracter citit se adaugă caracterul ‘\0’ în vectorul s. Funcţia returnează adresa vectorului s. La întâlnirea sfârşitului de fişier sau la o eroare funcţia retuurnează valoarea NULL. Funcţia

int fputs(const char * s, FILE * stream);scrie şirul de caractere s (terminat cu ‘\0’) în fluxul stream. Caracterul ‘\0’ nu este scris.Funcţia

int puts(char * s);scrie şirul de caractere s (terminat cu ‘\0’) în fluxul stdout. Caracterul ‘\0’ nu este scris. Funcţia scrie şi caracterul ‘\n’ după ce a scris şirul s.Reamintim că funcţia fputs nu scrie caracterul ‘\n’ în fişier, în timp ce funcţia puts scrie caracterul ‘\n’ în fişierul stdout după şir. In consecinţă, pentru a afişa câte un şir de caractere pe rand, la scrierea în fişierul stdout cu funcţia fputs vom scrie

fputs(x, stdout);

Page 77: Curs Program Are C++

fputs(“\n”, stdout);

Schema principială de citire a unui fişier secvenţial este următoarea. Presupunem că utilizăm o funcţie read(file) ce citeşte câte un bloc de date din fişier. Operaţia de citire poziţionează indicatorul de sfarşit de fişier la valoarea true dacă în cursul operaţiei de citire s-a detectat sfarştul fişierului.

read(file)while(! eof(file)){

prelucreaza blocul cititread(file)

}

In cazul citirii caracterelor de la tastatură, sfarşitul de fişier este indicat prin CTRL+Z.Exemplu. Vom exemplifica utilizarea acestor funcţii cu un program care să calculeze dimensiunea în octeţi a unui fişier. Programul va citi numele unui fişier de la tastatură, şi va deschide fişierul în citire. Apoi se va citi câte un caracter pană la întalnirea sfarşitului de fişier şi se vor număra octeţii citiţi. Citirea secvenţială a fişierului se va face cu instrucţiunea while.

/* calculul dimensiunii unui fisier */#include <stdio.h>int main(){

char name[64];FILE* file;char car; int nb = 0;

printf("introduceti numele fisierului\n"); scanf("%s", name); // deschide fisierul in citire file = fopen(name, "r"); // test daca fisierul exista

if(file == NULL){

printf("nume de fisier eronat\n");return 0;

} // citeste cate un caracter până la sfarsitul fisierului // citeste un caracter car = fgetc(file); while(car != EOF) { // numara caracterul citit

Page 78: Curs Program Are C++

nb = nb + 1; // citeste un caracter

car = fgetc(file); } fclose(file); printf("fisierul %s contine %d octeti\n", name, nb);

return 0;}

Exemplu. Vom face un program care să copieze un fişier existent în altul. Fie un fişier cu numele file1.txt în directorul curent. Vom copia acest fişier în fişierul cu numele file2.txt tot în directorul curent. Vom deschide cele două fişiere şi vom testa dacă operaţia a avut succes. Funcţia fopen are ca rezultat valoarea NULL dacă operaţia nu a avut succes. Apoi se citeşte câte un caracter din primul fişier şi se scrie în al doilea.

# include <stdio.h>int main (){

FILE * fin, * fout;char nume1[64], nume2[64];

// citeste numele primului fisierprintf(“introduceti numele fisierului ce va fi copiat\n”);scanf(“%s”, nume1);fin = fopen(nume1, "r");if(fin == NULL){

printf("fisierul %s nu exista\n", nume1);return 0;

} // citeste numele noului fisier

printf(“introduceti numele noului fisier\n”);scanf(“%s”, nume2);fout = fopen(nume2, "w");if(fout == NULL){

printf("fisierul %s nu se poate crea", nume2);return 0;

} // copiaza fisierul

int c;c = fgetc(fin);while(c != EOF){

fputc(c, fout);c = fgetc(fin);

}

Page 79: Curs Program Are C++

fclose(fin);fclose(fout);return 0;

}

Instrucţiunile ce citesc şi scriu din fişiere pot fi rescrise ca

int c;while((c = fgetc(fin)) != EOF)

fputc(c, fout);

6.2 Fişiere binare

Pentru scrierea şi citirea de date din fişiere binare există funcţiile fread şi fwrite. Aceste funcţii pot citi sau scrie unul sau mai multe blocuri de date.Funcţia

size_t fread(void * ptr, size_t size, size_t nmb, FILE * stream);citeşte în vectorul ptr cel mult nmb blocuri de dimensiune size din fişierul stream. Funcţia returnează numărul efectiv de blocuri citite. Indicatorul de poziţie al fişierului este avansat cu numărul de octeţi citiţi. In cazul întâlnirii sfarşitului de fişier, funcţia returnează valoarea 0.Funcţia

size_t fwrite(const void * ptr, size_t size, size_t nmb, FILE * stream);scrie în vectorul ptr cel mult nmb blocuri de dimensiune size în fişierul stream. Funcţia returnează numărul efectiv de blocuri scrise. Indicatorul de poziţie al fişierului este avansat cu numărul de octeţi scrişi. Pentru citirea elementelor în ordine aleatoare există posibilitatea de a modifica indicatorul de poziţie al fişierului cu funcţia

int fseek (FILE * stream, long int offset, int whence);Noua valoare a indicatorului fişierului este obţinută adăugând valoarea offset la poziţia specificată de whence care poate fi

SEEK_SET începutul fişieruluiSEEK_CUR poziţia curentă a indicatoruluiSEEK_END sfârşitul fişierului

După o instrucţiune fseek, următoarea instrucţiune poate fi o operaţie de citire sau scriere.Funcţia

long ftell(FILE* stream);are ca rezultat valoarea indicatorului de poziţie al fişierului.Exemplu. Vom crea un fişier binar pe care îl citim apoi secvenţial. Vom scrie în fişier 10 blocuri de câte 15 octeţi fiecare. Blocurile vor consta din şiruri de câte 14 caractere, primul bloc caractere ‘0’, al doilea bloc caractere ‘1’, etc. Citirea secvenţială se va face cu instrucţiunea while. După fiecare citire testăm rezultatul funcţiei fread. Un rezultat zero al acestei funcţii semnifică întâlnirea sfârşitului de fişier. Menţionăm că putem genera caracterele ‘0’, ‘1’, etc., o expresie de forma

‘0’ + iunde i ia valorile 0, 1, 2, …

Page 80: Curs Program Are C++

#include <stdio.h>int main(){

FILE * fil;int i, j;char x[15];

// deschide fisierul in crearefil = fopen(“fil.txt”, “wb”);if(fil == NULL){

printf(“fisierul nu se poate crea\n”);return 0;

}for(i = 0; i < 10; i++){

// creaza un blocfor(j = 0; j < 14; ++j)

x[j] = ‘0’ + i ;x[14] = 0;// scrie blocul in fisierfwrite(x, 15, 1, fil);

} // inchide fisierul

fclose(fil); // deschide fisierul in citire

fil = fopen(“fil.txt”, “rb”);if(fil == NULL){

printf(“fisierul nu se poate deschide\n”);return 0;

}int xct;

// citeste un sirxct = fread(x, 15, 1, fil);while(xct != 0){

// scrie sirul pe ecran (in streamul stdout)printf(“%s\n”, x);// citeste un sirxct = fread(x, 15, 1, fil);

} // inchide fisierul

fclose(fil);}

Page 81: Curs Program Are C++

Exemplu. Vom crea un fişier binar în care vom scrie 15 blocuri de câte 10 caractere şi apoi vom citi blocurile pare. Blocurile vor conţine şiruri de caractere de forma

abcde…bcdef…cdefg…

generate cu o expresie de forma‘a’ + i + j

unde i şi j iau valorile 0, 1, 2, …

#include <stdio.h>int main(){

FILE * fil;int i, j;char x[10];

// deschide fisierul in crearefil = fopen(“fil”, “wb”);if(fil == NULL){

printf("fisierul nu se poate crea\n");return 0;

}for(i = 0; i < 15; i++){

// creaza un blocfor(j = 0; j < 9; ++j)

x[j] = ‘a’ + i + j;x[9] = 0;// scrie blocul in fisierfwrite(x, 10, 1, fil);

// scrie sirul pe ecran (in fluxul stdout)puts(x);

} // inchide fisierul

fclose(fil); // deschide fisierul in citire

fil = fopen(“fil”, “rb”);if(fil == NULL){

printf("fisierul nu se poate citi\n");return 0;

}printf("fisierul citit\n");

// citeste sirurile parefor(i = 0; i < 15; i += 2){

Page 82: Curs Program Are C++

// pozitioneaza indicatorul fisieruluifseek(fil, (long)(i * 10), SEEK_SET);

// citeste un sirfread(x, 10, 1, fil);

// scrie sirul pe ecran (in fluxul stdout)fputs(x, stdout);fputs(“\n”, stdout);

} // inchide fisierul

fclose(fil);}

Menţionăm că puteam avansa indicatorul fişierului la sfarşitul ciclului for cu 10 octeţi.

// avanseaza indicatorul fisierului cu 10fseek(fil, (long)(10), SEEK_CUR);

Cap. 7 Structuri tip C

O structură este un tip de date definit de utilizator. O structură este o colecţie de date de tipuri diferite. Datele dintr-o structură vor fi numite componente sau câmpuri. Definirea unei structuri se face cu instrucţiunea

struct nume {liste de declaraţii de tip };unde nume este noul tip de date. De exemplu, o structură poate defini tipul numere complexe

struct complex {float real;float imag;

};Menţionăm că o structură poate conţine componente de orice tip, chiar şi alte structuri.Putem defini apoi variabile corespunzând acestui nou tip cu o instrucţiune

struct nume listă de variabile;De exemplu, într-un program putem defini numere complexe a şi b

struct complex a, b;O structură poate fi iniţializată la declarare. Putem scrie

complex n = {1.1, -2.34};Adresarea unui element al unei structuri se face cu operatorul .

nume.membruPutem da valori variabilelor definite anterior

a.real = 1.0;

Page 83: Curs Program Are C++

a.imag = -1.2 + sin(0.3);Putem utilize valorile componentelor unei structuri în expresii

float x;x = a.real * a.imag;

Pentru a defini funcţii ce au ca argumente sau rezultat structuri în limbajul C, definim un nou tip de date corespunzand structurii cu instrucţiunea typedef. Pentru exemplificare vom defini (cu instrucţiunea typedef) un tip numit vector ce va fi o structură ce conţine două numere reale.

# include <stdio.h>typedef struct {

float compa;float compb;

} vector;

Vom construi o funcţie care să adune doi vectori. Funcţia va avea ca parametri două structuri şi ca rezultat o structură. Vom declara variabile de acest tip în funcţia main şi în funcţia ce adună vectorii.

vector addcmp(vector x, vector y){

vector c;c.compa = x.compa + y.compa;c.compb = x.compb + y.compb;return c;

}

int main(){

vector cx, cy, cz;cx.compa = 1;cx.compb = 1;cy.compa = 0;cy.compb = 1;cz = addcmp(cx, cy);printf(“suma este (%f , %f) \n”, cz.compa ,cz.compb);

}

La fel ca şi în cazul variabilelor putem defini variabile tip pointer la o structură, ce vor conţine adresa unei structuri. Instrucţiunea de definire a unui pointer la o structură este

struct nume * identificator;sau

numetip * identificator;dacă am definit un tip de structură cu instrucţiunea typedef.unde nume este numele structurii iar identificator este numele variabilei tip pointer.

Page 84: Curs Program Are C++

De exemplu, struct complex * pc;

defineşte un pointer de tip struct complex. Variabila tip pointer poate fi iniţializată ca orice variabilă tip pointer, folosind operatorul de calcul al adresei &

pc = &a;In cazul unui pointer p la o structură, un camp al structurii este adresat astfel

(*p).membruPrin definiţie, această scriere se prescurtează

p->membruOperatorii . şi -> au aceeaşi prioritate ca operatorii () şi [].Exemplu. Fie instrucţiunile

complex y;struct complex * py;py = &y;

Putem iniţializa structura y, utilizand pointerul pypy->real = -23.4; py->imag = 1.2;

Putem iniţializa direct componentele structuriiy.imag = 1.24;y.real = 0.23;

Putem utiliza valorile variabileleor definite de structura y.float fm = pc->imag;

Sunt corecte şi scrierile(*pc).real = cos(1.23);double d = (*pc).imag + (*pc).real;

Exemplu. Vom defini un tip de structură numcmp ce descrie numerele complexe cu instrucţiunea typedef. Vom iniţializa o variabilă de acest tip şi vom scrie valorile ei pe ecran folosind un pointer la structură.

# include <stdio.h>typedef struct {

float real;float imag;

} numcmp;int main (){

numcmp cr = {1.1, -1.2};numcmp * pv;pv = &cr;printf("(%f , %f)\n", pv->real, pv->imag);

}

Vom prezenta un exemplu de utilizare a operatorilor de incrementare ++ în cazul pointerilor la structuri. Fie structura

struct strx {

Page 85: Curs Program Are C++

int c;float d;

};Fie o variabilă tip strx şi o variabilă tip pointer la structura strx;

struct strx a;struct strx *b;

Vom iniţializa pe b cu adresa lui a.b = &a;

şi elementul c al structurii la valoarea zero.b->c = 0;

Putem incrementa pe c astfel:b->c = b-> + 1;b->c += 1;(*b).c += 1;++(*b).c;++b->c;

Operatorul ++ are o prioritate mai mică decât -> şi . astfel încât ultimele expresii sunt++((*b).c);++(b->c);

Cap. 8 Clase

Pentru a rezolva o problemă trebuie să creăm un model al problemei. Procesul de modelare se numeste abstractizare. In acest proces trebuie să definim

datele afectate operaţiile identificate de problemă.

Abstractizarea este structurarea problemei în entităţi definind datele şi operaţiile asociate acestor entităţi. Tipurile de date abstracte pe care le definim au următoarele proprietăţi

definesc o structura a datelor datele sunt accesibile prin operaţii bine definite. Setul de operaţii definite se

numeşte interfaţă. Operaţiile interfeţei sunt singurul mecanism de acces la structura de date.

Definiţiile pe care le vom utilize sunt următoarele Clasa este o reprezentare a unui tip de date abstracte. Ea asigură detaliile de

implementare pentru structura de date şi pentru operaţii. Clasa defineşte atribute si metode care implementează structura de date şi operaţiile tipului abstract. Instanţele clasei se numesc obiecte. O clasa defineşte proprietăţile şi comportarea unei mulţimi de obiecte. Intr-o clasă există o parte de definire a datelor şi operaţiilor şi o parte de implementare. Partea de definire se numeşte interfaţă.

Obiect. Un obiect este o instanţa a unei clase (a unui tip abstract). El este unic identificat prin nume şi defineşte o stare reprezentată de valorile atributelor la un moment dat. Comportarea unui obiect este definită de metodele ce se pot aplica asupra lui.

Page 86: Curs Program Are C++

Mesaje. Un program este o multime de obiecte create, distruse şi care interacţionează. Interacţiunea este bazată pe mesaje trimise de la un obiect la altul cerand destinatarului sa aplice o metodă asupra lui (apelarea unei functii). Un mesaj este o cerere către un obiect ca el să invoce una din metodele lui. Mesajul conţine

- numele metodei- argumentele metodei

Metoda. O metoda este asociată unei clase. Un obiect invoca o metodă ca reacţie la primirea unui mesaj.

Clasa este un tip de date definit de utilizator. Clasa defineşte variabile şi funcţii ce prelucrează aceste variabile. Funcţiile definite de clasă se numesc metode sau funcţii membre ale clasei. Variabilele clasei se numesc câmpuri sau atribute sau proprietăţi. Definiţia unei clase se numeşte interfaţă.Există o notaţie pentru tipurile de date abstracte pe care le definim

In programe creăm variabile de tipul unor clase care se numesc obiecte. Vom spune că obiectele sunt instanţe ale unor clase. Obiectele unei clase sunt create şi iniţializate de funcţii membre ale clasei special definite pentru acest scop numite constructori. Obiectele sunt distruse când nu mai sunt necesare de funcţii membre ale clasei numite destructori. Valorile proprietăţilor dau starea obiectului la un moment dat.

Datele şi funcţiile membre ale clasei pot fi publice, private sau protejate. Membri publici ai clasei pot fi utilizaţi de orice funcţie din program. Membri privaţi ai clasei pot fi utilizaţi doar de funcţiile membre ale clasei. Membri protejaţi vor fi prezentaţi ulterior. Implicit, toate datele şi funcţiile unei clase sunt private.Principiile programării orientate obiect sunt următoarele

Primul principiu al programării orientate obiect este încapsularea. Acesta cere combinarea datelor şi metodelor într-o singură structură de date. Acest principiu cere ascunderea structurii de date şi asigurarea unei interfeţe pentru adresarea datelor. Exemplu. Numere complexe. Un număr complex este o pereche ordonată de numere reale. Pentru a înmulţi două numere complexe trebuie să adresăm structura şi să adunăm produse ale părţile reale si cele imaginare. Definind o operaţie mult încapsulăm detaliile şi înmulţim două numere complexe fără să ne intereseze cum se face operaţia în detaliu.

Moştenirea. Acest principiu cere definirea unei clase generale ce conţine caracteristicile comune ale mai multor elemente. Apoi această clasă este moştenită de alte clase particulare, fiecare adaugă doar elementele proprii. Clasa care este moştenită se numeşte clasă de bază, iar clasele care moştenesc se numesc clase derivate.

Page 87: Curs Program Are C++

Polimorfismul. Avem funcţii cu acelaşi nume în clasa de bază şi în clasele derivate. Funcţiile din clasele derivate redefinesc operaţiile necesare claselor derivate. Când apelăm funcţia respectivă se apelează versiunea corespunzătoare tipului obiectului.

8.1 Definirea unei clase

8.1.1 Definirea unei clase

Diagrama sintactică a definiţiei unei clase este

Clasele sunt definite utilizand cuvantul cheie class. Specificatorii de acces sunt public, protected şi private, identificator este numele clasei, iar membru este o declaraţie de funcţie membru sau dată a clasei. Implicit toţi membri clasei sunt privaţi. Ca exemplu vom defini o clasă care reprezintă numere raţionale ca un cât a două numere întregi.

Definiţia clasei este următoarea

class Ratio{

private:int num, den;

public:void assign(int, int);double convert();void invert();void print();

};

Numele clasei este Ratio. Definiţia clasei este inclusă între accolade { şi }, urmate de ;.

Page 88: Curs Program Are C++

Date membre ale clasei sunt num şi den şi ele vor memora numărătorul şi numitorul numărului raţional descris de clasă. Ele sunt de tip int şi sunt declarate private. In acest fel ele vor fi accesibile doar funcţiilor membre ale clasei. Interzicerea accesului din afara clasei este un principiu al programării orientate obiect numit ascunderea informaţiilor. Funcţiile membre ale clasei sunt assign(), ce atribuie valori numărului raţional, convert(), ce dă valoarea numărului, invert(), ce inversează numărul şi print(), ce scrie numărul ca un cât de două numere întregi. Ele sunt declarate publice şi vor putea fi utilizate de obiecte în afara clasei. Funcţiile membre pot fi definite în interiorul clasei sau în afara ei. Funcţiile definite în interiorul clasei sunt funcţii inline. Compilatorul generează direct textul lor în program, şi nu un apel la funcţie.In limbajul C++ avem posibilitatea de a grupa anumite nume în spaţii de nume. Prin definiţie, datele şi funcţiile unei clase constituie un spaţiu de nume ce are numele clasei. Operatorul de rezoluţie :: arată că un nume de dată sau de funcţie aparţine unui spaţiu de nume. Operatorul de rezoluţie :: se utilizează pentru a defini o funcţie membru a clasei în afara clasei.Implementarea clasei este următoarea

void Ratio::assign(int numval, int denumval){

num = numval;den = denval;

}

double Ratio::convert(){

return double(num) / den;}

void Ratio::invert(){

int temp;temp = num ;num = den;den = temp;

}

void Ratio::print(){

cout << num << “/” << den;}

Apelul unei funcţii a clasei de către un obiect se face conform următoarei diagrame sintactice

Page 89: Curs Program Are C++

Utilizarea unui câmp al un obiect se face conform următoarei diagrame sintactice

Vom exemplifica utilizarea clasei definind un număr rational şi calculând inversul lui.

int main(){ // creaza un obiect x de tipul Ratio

Ratio x; x.assign(2, 7); // scrie x

cout << “x = “ ;x.print();cout << “ = “ << x.convert() << endl;

// inverseaza numarulx.invert();cout << “1/x = “ <<;x.print();cout << endl;

}

Declararea unui obiect se face în acelaşi fel cu declararea tipurilor standard. Vom spune că un obiect este o instanţă a unei clase. Orice obiect are propriile variabile.In funcţia main(), x este declarat ca un obiect de tipul Ratio (x este o instanţă a clasei Ratio). El are cele două data membre num şi den şi poate apela funcţiile membre assign(), invert() şi print() care au fost declarate publice în definiţia clasei.

Acesta este un principiu al programării orientate obiect. Nu utilizăm variabile şi funcţii globale, ci fiecare obiect are propriile lui date şi funcţii.Menţionăm că în funcţia main() nu puteam scrie o instrucţiune

cout << x.num << “ “ << x.den << endl;deoarece variabilele clasei num şi den sunt declarate private. Ele pot fi utilizate doar de funcţiile clasei şi nu de obiecte de tipul clasei. Pentru a putea utiliza datele clasei de către obiecte ca mai sus, ele ar trebui declarate publice.

Page 90: Curs Program Are C++

Menţionăm că în limbajul C++ putem defini clase şi cu cuvântul cheie struct. In acest caz toţi membrii clasei sunt declaraţi implicit ca publici. Spre deosebire de limbajul C, în limbajul C++ cuvantul cheie struct defineşte clase.

8.1.2 Pointerul this

Considerăm instrucţiunile în care un obiect apelează o funcţie membru a clasei, de exemplu

x.print();Funcţiile clasei au un parametru implicit un pointer de tipul clasei. In acest fel aceste funcţii pot adresa variabilele obiectului (în cazul nostru ale obiectului x). Fiecare obiect are o variabilă implicită , numită this care este un pointer de tipul clasei ce conţine adresa obiectului. Atunci când un obiect apelează o funcţie a clasei, parametrul implicit al funcţiei primeşte ca valoare pointerul this. Pointerul this poate fi utilizat în orice funcţie membră a clasei pentru a apela variabile sau funcţii membre. Ca exemplu vom rescrie funcţia invert() ca să utilizeze pointerul this la apelarea variabilelor num şi den.

void Ratio::invert(){

int temp;temp = this->num;this->num = this->den;this->den = temp;

}

8.1.3 Spaţii de nume

In limbajul C++ putem defini spaţii de nume. Un spaţiu de nume ne permite să grupăm o mulţime de clase, obiecte şi funcţii globale sub un nume. Definiţia unei clase crează automat un spaţiu cu numele clasei ce cuprinde toate variabilele şi funcţiile membre ale clasei. Declararea unui spaţiu de nume se face astfel

namespace identificator{

// declaraţii de clase, funcţii, obiecte}

De exemplu, putem defini următorul spaţiu

namespace general{

int a, b;}

Pentru a adresa variabilele din afara spaţiului se utilizează operatorul de rezoluţie :: De exemplu, scriem

general::a

Page 91: Curs Program Are C++

Directiva using namespace asociază un spaţiu de nume cu un anumit nivel al programului, astfel încât obiectele şi funcţiile din acel spaţiu sunt accesibile direct ca şi când ar fi fost definite ca globale. Această directivă are forma

using namespace identificator;Toate clasele, obiectele şi funcţiile din bibliotecile C++ standard sunt definite în spaţiul std. In consecinţă, obiectul cout corespunzător streamului de ieşire standard poate fi utilizat astfel

Folosind directiva using namespace# include <iostream>using namespace std;

……………………………………cout << endl;

Utilizând numele spaţiului# include <iostream>

……………………………………cout std::cout << std::endl;

Utilizând stilul clasic tip C în care toate clasele, obiectele şi funcţiile din bibliotecile standard sunt definite într-un spaţiu global.

# include <iostream.h>……………………………………..

cout << endl;Menţionăm că la utilizarea stilului clasic tip C se utilizează bibliotecile standard cu extensia h, de exemplu <stdio.h>, <iostream.h>, <math.h>, etc. La utilizarea cu spaţii de nume se utilizează bibliotecile paralele, fără extensia h, de exemplu <cstdio>, <iostream>, <cmath>, etc.

8.2 Constructori şi destructori

8.2.1 Constructori

In general obiectele trebuie să iniţializeze variabilele lor la declararea obiectului. In exemplul precedent iniţializarea s-a făcut cu funcţia assign(). Este mai natural ca iniţializarea să se facă la declararea obiectului. Pentru aceasta se definesc funcţii constructor ale clasei. Un constructor este o funcţie membru a clasei apelată automat atunci când obiectul este declarat. Constructorul are acelaşi nume cu al clasei şi nu are nici un tip. Clasa precedentă poate avea un constructor definit ca mai jos.

#include <iostream>using namespace std;class Ratio{private:

int num, den;public:

void assign(int, int);

Page 92: Curs Program Are C++

double convert();void invert();void print();Ratio(int, int);

};

Vom prezenta în continuare doar definiţia constructorului, restul definiţiilor fiind cele anterioare.

Ratio::Ratio(int a, int b){

num = a;den = b;

}

Vom exemplifica utilizarea acestei clase definind două obiecte şi afişând valorile lor.

int main(){

Ratio x(-2, 12), y(4, 5);cout << “x = “;x.print();cout << endl;cout << “y = “;y.print();cout << endl;return 0;

}

Orice clasă are cel puţin doi constructori. Ei sunt identificaţi de declaraţia lorX(); // constructorul implicitX(const X&); // constructorul copiere

unde X numele clasei. Constructorul implicit nu are parametri. El este apelat ori de câte ori declarăm un obiect cu instrucţiunea

X obiect;Dacă nu definim niciun constructor, constructorul implicit este definit de compilator.Constructorul copiere este apelat atunci când un obiect este copiat. De exemplu instrucţiunea

X a(b);declară un obiect a de tipul X şi îi atribuie ca valoare obiectul b. Dacă nu definim un constructor copiere, el este definit de compilator. Menţionăm cuvântul cheie const din definiţia constructorului copiere. El interzice modificarea argumentului în timpul copierii.

Page 93: Curs Program Are C++

Orice clasă include un operator implicit de atribuire = care are ca parametru un obiect de tipul clasei.Exemplu. Fie din nou clasa Ratio în care definim un constructor copiere, un constructor fără parametri, şi un constructor cu parametri.

# include <iostream>using namespace std;class Ratio{

public:Ratio(int, int);Ratio(const Ratio& x);Ratio();void print();void invert();

private:int num;int den;

};

Vom prezenta doar definiţiile constructorilor, definiţiile funcţiilor print() şi invert() sunt cele anterioare.

Ratio::Ratio(const Ratio& x){

num = x.num;den = x.den;

}Ratio:: Ratio(){

num = 0;den = 1;

}Ratio::Ratio(int x, int y){

num = x;den = y;

}int main(){

Ratio x(12, 25);Ratio z;Ratio y(x); // se apeleaza constructorul copierex.print();y.print();z = x;

Page 94: Curs Program Are C++

z.print();}

Constructorul copiere este apelat ori de câte ori un obiect este copiat un obiect este pasat prin valoare unei funcţii (obiectul este copiat în stivă) un obiect este returnat ca valoare de o funcţie

Exemplu. Presupunem clasa Ratio definită anterior. Fie o funcţie globală f care are ca parametru un obiect de tip Ratio (un număr raţional) şi ca rezultat un obiect de tip Ratio care este inversul parametrului funcţiei. Definitia funcţiei este următoarea

Ratio f(Ratio r) // se apeleaza constructorul copiere{

Ratio s = r; // se apeleaza constructorul copieres.invert();return s; // se apeleaza constructorul copiere

}int main(){

Ratio x(11, 22);Ratio y(x);Ratio z;

// scrie numarul xx.print();z = f(y);

// scrie numărul zz.print();return 0;

}

Constructorul copiere este apelat 1) atunci când se declară obiectul

Ratio y(x);2) când se apelează funcţia

z = f(y);Parametrul funcţiei f (r în definiţia funcţiei) este pasat prin valoare, deci în stivă se pune o copie a obiectului creată cu constructorul copiere.3) când se execută instrucţiunea

return s;obiectul s se copiază în stivă utilizand constructorul copiere.Menţionăm că definiţia funcţiei f putea fi

Ratio f(const Ratio r);deoarece funcţia nu modifică parametrul r.Putem defini tablouri de obiecte în mod obişnuit, de exemplu

Ratio x[20];

Page 95: Curs Program Are C++

Pentru aceasta, clasa trebuie să definească un constructor fără parametri. Tabloul definit poate fi prelucrat, de exemplu putem avea instrucţiunile

Ratio a;a = x[5];

8.2.2 Destructori

Destructorul unei clase este apelat automat când un obiect este distrus. Fiecare clasă are un singur destructor. Destructorul nu are tip şi nici parametri. Diagrama sintactică a destructorului este următoarea

~ nume_clasa(){/* corpul destructorului*/}Dacă el nu este definit explicit, compilatorul generează unul. Pentru a vedea cum sunt apelaţi constructorii şi destructorul fie următoarea clasă în care constructorul şi destructorul scriu un mesaj.

class Ratio{

public:Ratio() {cout << “obiectul este creat” << endl;} ~Ratio() {cout << “obiectul este distrus” << endl;}

private:int num;int den;

};

Fie două blocuri de instrucţiuni în funcţia main. In fiecare bloc creăm câte un obiect.

int main(){

{cout << “incepe blocul 1” << endl;Ratio x;cout << “iesire blocul 1” << endl;

}{

cout << “incepe blocul 2” << endl;Ratio y;cout << “sfarşit blocul 2” << endl;

}}

Mesaje le scrise vor fi:

incepe blocul 1obiectul este creatiesire blocul 1

Page 96: Curs Program Are C++

obiectul este distrus

incepe blocul 2obiectul este creatiesire blocul 2obiectul este distrus

Menţionăm că toate variabilele locale unei funcţii sau unui bloc sunt create intr-o stivă la intrarea în funcţie sau bloc şi sunt şterse din stivă la ieşirea din funcţie sau bloc. Stiva se numeşte heap.

8.3 Funcţii prietene

In general avem nevoie să utilizăm şi să modificăm datele unui obiect în timpul execuţiei programului. Dacă datele sunt declarate private, nu putem face acest lucru direct. De aceea se definesc funcţii care să modifice şi să furnizeze valorile datelor unui obiect. In cazul clasei Ratio putem defini funcţii membre ale clasei

int getnum();int getden();void setnum(int);void setden(int);

Aceste funcţii se numesc funcţii de acces şi de regulă sunt publice.Definiţiile acestor funcţii pot fi

int Ratio::getnum(){

return num;}

void Ratio::setnum(int x){

num = x;}

Exemplu. Vom defini o funcţie globală care să adune două numere raţionale. Prototipul funcţiei va fi

Ratio sum(const Ratio x, const Ratio y);Parametrii x şi y sunt cele două numere raţionale ce sunt adunate. Deoarece obiectele nu vor fi modificate, ele au fost declarate const.

Considerăm două numere rationale, şi . Suma lor va fi . In

implementarea funcţiei nu putem folosi direct variabilele num şi den, deoarece ele sunt declarate private în definiţia funcţiei. Vom utiliza funcţiile getnum() şi getden() pentru a obţine valoarea lor. Implementarea funcţiei este următoarea

Ratio sum(Ratio x, Ratio y)

Page 97: Curs Program Are C++

{int a = x.getnum();int b = x.getden();int c = y.getnum();int d = y.getden();Ratio temp(a * d + b * c, b * d);return temp;

}

O altă soluţie este următoarea. Pentru ca o funcţie externă să aibe acces la membri privaţi ai clasei, ea trebuie declarată în definiţia clasei ca funcţie prietenă, friend. Diagrama sintactică a definiţiei unei funcţii prietene este

friend tip nume_funcţie ( parametri);Noua definiţie a clasei Ratio este

class Ratio{

public:Ratio(int, int);Ratio(const Ratio& x);Ratio();void print();void invert();friend Ratio sum(Ratio x, Ratio y)

private:int num;int den;

};

In această definiţie funcţia sum a fost declarată prietenă, deci poate utiliza variabilele private ale clasei. Implementarea funcţiei sum este următoarea

Ratio sum(Ratio x, Ratio y){

Ratio temp(x.num * y.den + x.den * y.num, x.den * y.den);return temp;

}

8.4 Fişierele standard C++

Limbajul C++ defineşte clasele istream şi ostream pentru operaţii intrare şi ieşire cu fişierele standard, tastatura şi ecranul. Streamul cin este un obiect de tipul istream, iar

Page 98: Curs Program Are C++

streamurile cout, cerr şi clog sunt obiecte de tipul ostream. Aceste obiecte sunt definite în bibliotecile <iostream.h> şi <iostream>.Clasa istream defineşte operatorul de citire >> şi funcţii ce citesc caractere ASCII şi şiruri tip C. Funcţiile

int get();istream& get(char&);

citesc un character. Funcţiileistream& get(char* s, int size, char ch);istream& getline(char* s, int size, char ch);

citeşte cel mult size - 1 caractere în vectorul s. Citirea se opreşte după citirea a size - 1 caractere sau la întalnirea caracterului ch. Valoarea implicită a caracterului ch este ‘\n’.Clasa ostream defineşte operatorul de scriere << şi funcţia

ostream& put(char);ce scrie un character.Exemplu. Fie instrucţiunea

char c;Instrucţiunile

cin.get(c);şi

c = cin.get();citesc un caracter de la tastatură în variabila c.Menţionăm că operatorul >> este un operator binar. Operandul stâng este un obiect tip istream, oparandul drept este o variabilă, rezultatul este o referinţă la un obiect tip istream. Ţinând cont de prototipul funcţiei get

istream& get(char&);este posibil să scriem

char c;int x;cin.get(c) >> x;

deoarece operandul stâng este o referinţă tip istream, rezultatul evaluării funcţiei get(char&).Toate operaţiile de scriere de până acum în streamul cout nu au utilizat un format. Este posibil de a preciza formatul informaţiei scrise în felul următor

putem insera spaţii sau caracterul ‘\t’ cout << s << “ “ << ‘\t’ << x << endl;

putem insera manipulatori definiţi în biblioteca <iomanip> sau <iomanip.h>. Implicit, orice valoare este scrisă pe un număr de caractere specificat sau pe numărul de caractere necesare. De exemplu, numărul întreg 123 este totdeauna scris pe cel puţin 3 caractere. Funcţia

setw(int w);dă dimensiunea câmpului în care este scrisă valoarea. Dimensiunea implicită a câmpului este 0. Funcţia

setfill(char c);dă caracterul c cu care sunt umplute spaţiile libere. Valoarea implicită este spaţiu. Exemplu. Fie variabila

int k = 192;

Page 99: Curs Program Are C++

scrisă cu diverse dimensiuni ale campului şi caractere de umplere.

Instrucţiunea de scriere Rezultatcout << "[" << setw(6) << k << "]" << endl; [ 192]cout << "[" << setw(6) << setfill('*') << k << "]" << endl; [***192]

Funcţia setw() care dă dimensiunea câmpului este valabilă doar pentru câmpul următor.Funcţia

setprecision(int p);dă numărul p de caractere pe care este scris un număr real, tip float sau double.Exemple. Fie variabila

float x = 3.1459;scrisă cu diferite precizii. De remarcat că valoarea scrisă este rotunjită.

Instrucţiunea de scriere Rezultatcout << pi << endl; 3.1459cout << setprecision(3) << pi << endl; 3.15cout << setprecision(4) << pi << endl; 3.146cout << setprecision(6) << pi << endl; 3.1459

Clasa ios defineşte următoarii specificatori dec - pentru baza 10hex - pentru baza 16oct - pentru baza 8showbase pentru specificarea bazei

Specificatorul showbase scrie 0x pentru numere în baza 16 şi 0 pentru numere în baza 8. Exemple. Fie variabila

int k = 29;scrisă în baze diferite

Instrucţiunea de scriere Rezultatcout << hex << i; 13cout << hex << showbase << i; 0x13cout << oct << i; 35cout << oct << showbase << i; 035

Specificatorii următori specifică modul de cadrare a valorii scriseleft - cadrare la stânga right - cadrare la dreapta

Specificatorii următori specifică modul de scriere a numerelor realefixed - scriere fără exponentscientific - scriere cu exponent

Exemple. Fie instrucţiuneafloat f = 24.35;

Page 100: Curs Program Are C++

Instrucţiunea de scriere Rezultat cout << fixed << f << endl; 24.35cout << scientific << f << endl; 2.435e+001

Cap. 9 Supraîncărcarea operatorilor

Operatorii limbajului C++ sunt automat definiţi pentru tipurile fundamentale: int, double, char, etc. Când definim o clasă nouă, creăm un nou tip. Operatorii limbajului pot fi definiţi şi pentru tipurile nou create. Această operaţie se numeşte supraîncărcarea operatorilor (operator overloading). Pentru a supraîncarca un operator definim o functie de forma

tip operator semn (parametri) {/* corpul funcţiei */}unde semn este operatorul dorit +,-, *, / , [], (), <<, >>, =, etc. Operatorii supraîncărcaţi au aceeaşi prioritate ca cei originali şi acelaşi număr de parametri. Menţionăm că orice operator supraîncărcat poate fi definit ca funcţie membră a clasei sau ca funcţie globală.Exemplu. Să supraîncărcăm operatorul + pentru clasa Ratio, care să adune două numere raţionale. Operatorul va fi definit ca o funcţie globală.

Ratio operator+(Ratio x, Ratio y){

int a = x.getnum();int b = x.getden();

Page 101: Curs Program Are C++

int c = y.getnum();int d = y.getden();Ratio temp(a * d + b * c, b * d);return temp;

}

Se va compara această funcţie cu funcţia sum definită anterior. Funcţia are doi parametri, numerele de adunat deoarece este o funcţie globală. Putem utiliza funcţia definită astfel

int main(){

Ratio x(2, 3), y(3, -5), z;z = x + y;z.print();return 0;

}

Putem defini operatorul ca funcţie prietenă a clasei. In definiţia clasei vom scrie instrucţiunea

friend Ratio operator+ (Ratio, Ratio);In acest caz definiţia funcţiei este

Ratio operator+(Ratio x, Ratio y){

Ratio temp(x.num * y.den + x.den * y.num, x.den * y.den);return temp;

}

Al doilea mod de a defini operatorul + este ca funcţie membră a clasei. O funcţie membră a clasei este apelată de un obiect. Fie a, b, c obiecte de tip Ratio. Putem scrie

c = a.operator+(b);sau, echivalent

c = a + b;De accea, atunci când operatorul este o funcţie membră a clasei, funcţia corespunzătoare are un singur parametru, operandul drept, şi este apelată de operandul stâng. Definiţia clasei Ratio cu operatorul + este

class Ratio{

public:Ratio(int, int);Ratio(const Ratio& x);Ratio();void print();void invert();Ratio operator+(Ratio); // functia are un singur parametru

Page 102: Curs Program Are C++

private:int num;int den;

};

Implementarea funcţiei este

Ratio Ratio::operator+(Ratio x){

Ratio temp(num * x.den + den * x.num, num * x.den);return temp;

}

Exemplu. Fie instrucţiuneaRatio a(1, 3), b(4, -3), c;

Instrucţiunilec = a + b;

şic = a.operator+(b);

sunt echivalente.

9.1 Supraîncărcarea operatorului de atribuire

Considerăm clasa definită anterior în care definim constructorul implicit, constructorul copiere şi operatorul de atribuire. Menţionăm că operatorul de atribuire este automat definit pentru orice clasă. Supraîncărcarea lui aici este doar un exerciţiu. O definiţie incorectă a operatorului de atribuire este următoarea

# include <iostream>using namespace std;class Ratio{

private:int num, den;

public:Ratio();Ratio(const Ratio&);void operator = (const Ratio&);

};

O implementare a definiţei incorecte a operatorului de atribuire supraîncărcat este următoarea

void Ratio::operator = (const Ratio& r){

num = r.num;

Page 103: Curs Program Are C++

den = r.den;}

Ea copiază obiectul r în obiectul ce apelează operatorul. Fie de exemplu instrucţiuneaRatio x, y, z;

Cu această definiţie putem scriex = z;y = z;

dar nu putem scriex = y = z;

deoarece rezultatul funcţiei este void.Pentru a vedea care este definiţia corectă a operatorului de atribuire considerăm instrucţiunea int x, y, z = 2; Limbajul permite o instrucţiune de atribuire de forma

x = y = z = 2;Operatorul = este asociativ la dreapta şi are ca rezultat valoarea atribuită operandului stâng. Prima dată se execută instrucţiunea

z = 2;apoi

y = z; etc. Putem scrie instrucţiunea de mai sus

f(x, f(y, f(z, 2)));In consecinţă, operatorul trebuie să aibe ca rezultat o referinţă de acelaşi tip cu valoarea pe care o atribuie (tipul operandului stâng). In acest fel operatorul este asociativ la dreapta. Definiţia corectă a operatorului de atribuire al clasei Ratio este

Ratio& operator = (const Ratio & );Definiţia clasei va fi

class Ratio{

private:int num, den;

public:Ratio();Ratio(const Ratio&);Ratio& operator = (const Ratio&);

}

Prototipul operatorului de atribuire al unei clase T esteT& operator = (const T&);

Operatorul are ca rezultat o referinţă la obiectul ce apelează operatorul. Pentru aceasta se utilizează pointerul this. Implementarea corectă a operatorului de atribuire supraîncărcat este următoarea.

Ratio& Ratio::operator = (const Ratio& r)

Page 104: Curs Program Are C++

{ // obiectul ce a apelat operatorul primeste valoarea obiectului r

num = r.num;den = r.den;

// rezultatul funcţiei este referinta obiectului ce a apelat operatorulreturn *this;

}

Putem scrie acumRatio x, y, z(2, 3);x = y = z;

9.2 Supraîncărcarea operatorilor aritmetici

Presupunem că vrem să definim o clasă pentru lucrul cu numere complexe. Vrem să putem efectua operatii cu numere complexe la fel ca şi cu tipurile fundamentale, de exemplu să definim numere complexe

Complex a(1.0, -1.0), b(2.0, -3), c;şi să scriem pentru adunare

c = a + b;Limbajul ne dă posibilitatea de a defini funcţii care să efectueze operaţiile standard +,-, etc., pentru operanzi ce sunt obiecte ale unor clase. Acest lucru se numeşte supraîncărcarea operatorilor (operator overloading). Un operator poate fi definit ca o funcţie globală sau ca o funcţie membru a clasei. Vom exemplifica supraîncărcarea operatorului + pentru o clasă ce descrie numere complexe. O reprezentare a clasei este cea de mai jos.

Exemplu. Supraîncărcarea operatorului + pentru clasa Complex în cazul în cazul în care operatorul este o funcţie globală. Funcţia are numele operator+ şi doi parametri de tip Complex, numerele ce se adună. Tipul funcţiei este Complex. Definiţia funcţiei este

Complex operator+(Complex, Complex);Considerăm definiţia clasei Complex de mai jos.

class Complex{

private:float real;float imag;

Page 105: Curs Program Are C++

public:Complex();Complex (float, float);

};

Problema scrierii funcţiei este aceea că datele clasei Complex sunt declarate private şi nu pot fi utilizate direct de funcţie, care este globală. O soluţie este de a scrie două funcţii membre ale clasei, de exemplu

float getreal();float getimag();

care au ca rezultat variabilele real şi imag ale clasei. Această soluţie rămane ca exerciţiu. O altă soluţie este următoarea. Pentru ca o funcţie externă să aibe acces la membrii privaţi ai clasei, ea trebuie declarată în definiţia clasei ca funcţie prietenă, friend. Diagrama sintactică a definiţiei unei funcţii prietene este

friend tip nume_funcţie ( parametri); In consecinţă, definiţia clasei Complex va fi

class Complex{

private:float real;float imag;

public:Complex();Complex (float, float);friend Complex operator+(Complex, Complex);

};

Definiţia funcţiei va fi cea de mai jos.

Complex operator+(Complex& x, Complex& y){

Complex temp;temp.real = x.real + y.real;temp.imag = x.imag + y.imag;return temp;

}

Trebuie ca în definiţia clasei să includem un constructor implicit, fără parametriComplex();

Constructorul implicit nu mai este generat automat de compilator deoarece am definit un constructor. Constructorul implicit este apelat la declararea obiectului temp

Complex temp;în funcţia operator+.

Page 106: Curs Program Are C++

Exemplu. Supraîncărcarea operatorului + pentru clasa Complex în cazul în care operatorul este definit ca o funcţie membră a clasei, al cărei nume este operator+. O funcţie membru a clasei este apelată de un obiect. In consecinţă, vom scrie

c = a.operator+(b);sau, echivalent

c = a + b;De accea, atunci când operatorul este o funcţie membră a clasei, funcţia corespunzătoare are un singur parametru, operandul drept, şi este apelată de operandul stâng. Fie definiţia clasei Complex în care definim şi funcţia operator+

class Complex{

private:float real;float imag;

public:Complex();Complex (float, float);Complex operator+ (Complex);

};

Implementarea constructorilor este

Complex::Complex(float x, float y){

real = x;imag = y;

}

Complex::Complex(){

real = 0;imag = 0;

}

Implementarea operatorul + este următoarea.

Complex Complex::operator+(Complex p){

Complex temp;temp.real = real + p.real;temp.imag = imag + p.imag;return temp;

}

Trebuie ca în definiţia clasei să includem un constructor implicit, fără parametri

Page 107: Curs Program Are C++

Complex();Constructorul implicit nu mai este generat automat de compilator deoarece am definit un constructor. Constructorul implicit este apelat la declararea obiectului temp

Complex temp;în funcţia operator+.Exerciţiu. Să se scrie implementarea operatorilor de scădere, -, înmulţire, * şi împărţire, / a două numere complexe.

9.3 Supraîncărcarea operatorilor << şi >>

Operatorul << este numit operator de inserţie, el insereaza caractere într-un stream. Operatorul >> este numit operator de extracţie, el extrage caractere dintr-un stream. Toate functiile de inserţie au forma

ostream& operator << (ostream& stream, tip obiect){

// corpul functieireturn stream;

}

Primul parametru este o referinţă la streamul de ieşire. Al doilea este obiectul ce trebuie inserat. Ultima instrucţiune este

return stream;Exemplu. Operatorul de inserţie << pentru clasa Complex. In definiţia clasei definim funcţia

friend ostream& operator << (ostream& stream, Complex x);Implementarea funcţiei este

ostream& operator << (ostream& stream, Complex x){

stream << "(" << x.real << "," << x.imag << ")";return stream;

}

Exemplu. Operatorul de extracţie >> este supraîncărcat astfelfriend istream& operator >> (istream& stream, Complex& x);

Implementarea lui este următoarea

istream& operator >> (istream& stream, Complex& x){

stream >> x.real >> x.imag;return stream;

}

Când un operator este membru al unei clase, operandul stâng (transmis prin this) este cel care apeleaza operatorul. Operatorii << şi >> pe care i-am definit nu pot fi membri ai

Page 108: Curs Program Are C++

clasei deoarece operandul stâng ostream, respectiv istream nu este un membru al clasei. In consecinţă, aceşti operatori vor fi funcţii externe. Reamintim că pentru ca o funcţie externă să aibe acces la membrii privaţi ai clasei, ea trebuie declarată în definiţia clasei ca funcţie prietenă friend. Diagrama sintactică a definiţiei unei funcţii prietene este

friend tip nume_funcţie ( parametri);Definiţia clasei este

class Complex{

private:float real;float imag;

public:Complex (float, float);Complex operator+ (Complex);friend ostream& operator << (ostream&, Complex&);friend istream& operator >> (istream&, Complex&);

}

Putem utiliza operatorii definiţi astfel

int main(){

Complex a(1.0, 1.0);Complex b(0.0, 1.0);Complex c;c = a + b;// echivalent putem scriec = a.operator+(b);cout << c << endl;return 0;

}

Trebuie ca în definiţia clasei să includem un constructor implicit, fără parametri, de exemplu

Complex() {real = 0; imag = 0;}

Constructorul implicit nu mai este generat automat de compilator deoarece am definit un constructor. Constructorul implicit este apelat la declararea obiectului temp

Complex temp;în funcţia operator+ şi în funcţia main(). Este de dorit ca acest constructor să iniţializeze variabilele obiectului.Orice clasă include un operator implicit de atribuire = care are ca parametru un obiect de tipul clasei.

Page 109: Curs Program Are C++

Cap. 10 Moştenirea

10.1 Pointeri la obiecte. Operatorii new şi delete

O clasă, odată definită, este un tip valid. Putem defini variabile tip pointer la clase la fel cu pointeri la orice tip. O variabilă tip pointer la o clasă poate fi iniţializată cu adresa unui obiect iar datele şi funcţiile acelui obiect pot apelate utilizând pointerul.Instrucţiunea de definire a unui pointer la o clasă este

tip * identificator;unde tip este numele clasei iar identificator este numele variabilei tip pointer.Variabila tip pointer poate fi iniţializată ca orice variabilă tip pointer, folosind operatorul de calcul al adresei &.Fie p un pointer la un obiect. Un câmp al obiectului este adresat astfel

(*p).membruPrin definiţie, această scriere se prescurtează

p->membruO funcţie membră a clasei se apelează astfel

Page 110: Curs Program Are C++

(*p).funcţie(parametri);Prin definiţie, această scriere se prescurtează astfel

p->funcţie(parametri);Operatorii . şi -> au aceeaşi prioritate ca operatorii () şi [].Exemplu. Fie o clasă ce defineşte un dreptunghi. Clasa are ca variabile lungimea şi lăţimea dreptunghiului, funcţii de acces la variabilele clasei, şi o funcţie care calculează aria.

class Rectangle{

private:float h;float b;

public:float aria();Rectangle(float, float);float geth();float getb();void seth(float);void setb(float);

};

Rectangle::Rectangle(float x, float y){

h = x;b = y;

}

float Rectangle::aria(){

return h * b;}

float Rectangle::geth()

Page 111: Curs Program Are C++

{return h;

}

void Rectangle::seth(float x){

h = x;}

Funcţiile getb() şi setb() se definesc la fel ca geth() şi seth().Vom apela funcţiile clasei direct şi prin pointer.

int main(){

Rectangle d1(1.2, 4.5); // defineste un pointer la obiect

Rectangle * pd1;pd1 = &d1;

// apeleaza functiile directcout << “inaltimea = “ << d1.geth() << endl << “latimea = “ << d1.getb() << << “aria = “<< d1.aria() << endl;

// apeleaza functiile prin pointercout << “inaltimea = “ << pd1->geth() << endl << “latimea = “ << pd1->getb() << << “aria = “<< pd1->aria() << endl;

}

Există trei moduri de a crea obiecte. Obiecte globale declarate la nivelul programului, în afara oricărei funcţii. Ele sunt

create la inceputul execuţiei programului şi distruse la sfârşitul execuţiei. Obiecte locale, declarate în funcţii sau în blocuri din funcţii, între { şi } Ele sunt

create la intrarea în bloc şi distruse la ieşirea din bloc. Obiecte create în memorie cu operatorul new. Ele trebuie distruse cu operatorul

delete când nu mai sunt necesare. Ele sunt create într-o zonă specială de memorie denumită heap.

Operatorul new crează un obiect în memorie apeland constructorul şi furnizează adresa obiectului nou creat, adică un pointer. Diagrama sintactică este

ptr = new tip (listă de parametri);tip este numele unei clase iar ptr este variabilă tip pointer. Operatorul delete are forma

delete ptr;unde ptr este variabila tip pointer cu adresa obiectului.Exemplu. Vom considera aceeaşi clasă Rectangle, vom crea un obiect, (dreptunghi), cu operatorul new şi vom scrie dimensiunile şi aria lui.

int main()

Page 112: Curs Program Are C++

{Rectangle * ptr;

// creaza obiectul cu operatorul newptr = new Rectangle(2.5, 17.1);

// functii apelate prin pointercout << “inaltimea = “ << ptr->geth() << endl << “latimea = “ << ptr->getb() << << “aria = “<< ptr->aria() << endl;

// distruge obiectuldelete ptr;return 0;

}

Putem crea vectori de obiecte cu operatorul new. Pentru aceasta clasa trebuie să aibă un constructor fără parametri. De exemplu, putem crea un vector cu zece obiecte de tip Complex

Complex * vect;vect = new Complex[10];

Acest vector se şterge cu operatorul delete cu formadelete [] vect;

10.2 Moştenirea

Moştenirea este un concept al programării cu obiecte. Moştenirea permite să creăm clase care sunt derivate din alte clase existente, astfel încât ele includ unii dintre membri claselor părinte, variabile şi funcţii. Clasa derivată moşteneşte membrii clasei părinte, funcţii şi variabile. In acest fel putem construi programe complexe din obiecte simple. Clasa care este moştenită se numeşte clasă de bază, iar clasele care moştenesc se numesc clase derivate. Clasa de bază conţine caracteristicile comune ale mai multor elemente. Clasele care moştenesc sunt clase particulare ce adaugă doar elementele proprii.Diagrama sintactică a definirii unei clase derivate este

class nume_clasa_derivata : acces nume_clasa_baza{

// definitia clasei derivate};

In această definiţie cuvântul cheie acces poate fi public, protected sau private. Semnificaţia acestui termen va fi prezentată ulterior.Exemplu. Vrem să definim două clase Rectangle şi Triangle care să descrie dreptunghiuri şi respective triunghiuri. Clasele au în comun două variabile ce conţin baza şi înalţimea şi o funcţie ce dă valori acestor variabile. Vom defini deci o clasă de bază numită Polygon ce are două variabile h şi b, înălţimea şi lăţimea, declarate aici cu acces protejat, (protected), un constructor fără parametri, o funcţie setval() ce dă valori acestor variabile şi o funcţie print() ce afişază valorile variabilelor.

Page 113: Curs Program Are C++

class Polygon{protected:

float h, b;public:

void setval(float x, float y){h = x; b = y;}Polygon() {h = 0; b = 0;}void print() {cout << “b = “ << b << “,” << “ h = “, << h << endl;}

};

Vom defini cele două clase care descriu triunghiuri şi dreptunghiuri derivate din clasa Polygon. Clasele vor avea o funcţie ce calculează aria. O reprezentare grafică a acestei dependenţe este cea de mai jos.

Definiţia claselor derivate este următoarea.

class Rectangle : public Polygon{public:

float area{return h * b;}};

Page 114: Curs Program Are C++

class Triangle : public Polygon{public:

float area{return h * b / 2;}};

Un exemplu de utilizare a acestor clase în care calculăm aria unui triunghi şi a unui dreptunghi este următorul.

int main(){

Rectangle drpt;Triangle trgh;drpt.setval(1.3, 4.4);trgh.setval(2.3, 12);cout << “triunghi” << endl;cout << trgh.area() << endl;cout << “dreptunghi” << endl;cout << drpt.area() << endl;

}

Am definit variabilele clasei de bază h şi b de tipul protected. In acest caz ele pot fi utilizate în clasele derivate, dar nu pot fi utilizate de obiecte. De exemplu, nu putem scrie instrucţiunea

cout << trgh.h << endl;Pentru a afişa valorile variabilelor b şi h am definit funcţia print().Vom prezenta acum semnificaţia cuvântului cheie acces din definiţia clasei derivate. Fie definiţia unei clase

class Baza{public:

int x;protected:

int y;}

şi fie o clasă derivată cu specificatorul de acces public

class Deriv : public Baza{

/* declaratii din clasa Deriv */}

Page 115: Curs Program Are C++

In acest caz variabilele x şi y au în clasa Deriv specificatorul de acces definit în clasa Baza, respectiv public şi protected şi pot fi utilizate de funcţiile definite în clasa Deriv, dar numai variabila x poate fi utilizată de obiectele de tipul Deriv. Considerăm clasa derivată definită cu specificatorul de acces protected

class Deriv : protected Baza{

/* declaratii din clasa Deriv */}

In acest caz variabilele x şi y au în clasa Deriv specificatorul de acces protected. Ele pot fi utilizate de funcţiile definite în clasa Deriv, dar nu pot fi utilizată de obiecte de tipul Deriv.Considerăm clasa derivată definită cu specificatorul de acces private

class Deriv : private Baza{

/* declaratii din clasa Deriv */}

In acest caz toate variabilele x si y din clasa Baza au în clasa Deriv specificatorul de acces private. Menţionăm că datele şi funcţiile declarate private în clasa de bază nu pot fi utilizate în clasele derivate sau de obiecte de tipul claselor derivate.Specificatorul de acces din definiţia clasei derivate dă nivelul minim de acces pentru membrii moşteniţi din clasa de bază. La utilizarea specificatorului de acces public, clasa derivată moşteneşte toţi membrii clasei de bază cu nivelul de acces avut în clasa de bază.Putem sumariza acum accesul la variabilele unui obiect în funcţie de specificatorii lor

specificatorul de acces al variabileiPublic protected private

Membri ai aceleiaşi clase da da daMembri claselor derivate da da nuNemembri da nu nu

Exemplu. Fie clasele X şi Y definite după cum urmeazăclass X{public:

int x;protected:

int y;private:

int z;};

Page 116: Curs Program Are C++

class Y : public X{public:

int w;};

Fie două obiecte definite astfelX a;Y b;

Câmpurile celor două obiecte, a şi b sunt cele de mai jos.

Obiectul a are cele trei câmpuri din declaraţia clasei, x, y, z cu specificatorii respectiv public, protected, private. Obiectul b are câmpurile x şi w cu specificatorul de acces public şi y cu specificatorul protected. Câmpul z nu este moştenit de obiectul b.O clasă derivată moşteneşte toţi membri clasei de bază exceptând constructorii, destructorul şi operatorul =. Deşi constructorii nu sunt moşteniţi, constructorul implicit şi destructorul sunt totdeauna apelaţi când un obiect de tipul clasei derivate este creat sau distrus. In constructorul clasei derivate se apelează la început constructorul implicit al clasei de bază. Dacă este nevoie, putem apela explicit un constructor al clasei de bază pentru iniţializarea variabilelor. Apelarea sa se face astfel

constructor_clasa_derivata(lista de parametri) : constructor_clasa_de_baza (lista de parametri)

{/* definitia constructorului clasei derivate */

}

Exemplu. Să definim o clasă numită Point care descrie un punct pe ecran prin coordonatele sale şi o clasă Pixel ce descrie un pixel şi moşteneşte din clasa Point coordonatele pixelului. Clasa Point va avea două câmpuri de tip întreg, x şi y, ce sunt coordonatele punctului pe ecran, doi constructori şi o funcţie clear() ce pune la valoarea zero cele două coordinate.

Page 117: Curs Program Are C++

class Point{public:

int x, y;void clear();Point(int, int);Point(){x = 0; y = 0;}void print();

};

void Point::clear(){

x = 0;y = 0;

}Point::Point(int p1, int p2){

x = p1;y = p2;

}void Point::print(){

cout << “ x = “ << x << “,” << “ y = “ << y << endl;}

Când un obiect dintr-o clasă derivată este creat sau distrus, constructorul implicit (fără parametri) şi destructorul clasei de bază sunt totdeauna apelaţi. De aceea am definit şi constructorul implicit în definiţia clasei Point.Vrem să definim acum o clasă Pixel care să descrie un pixel pe ecran. Un pixel este caracterizat de coordonatele sale şi de culoare. Culoarea va fi un câmp de tip întreg. Clasa Pixel va moşteni coordonatele punctului din clasa Point. Funcţia clear() va pune la valoarea zero coordonatele şi culoarea obiectului tip Pixel.

Page 118: Curs Program Are C++

class Pixel : public Point{public:

int color;void clear();Pixel(int, int, int);void print();

};

In definiţia constructorului clasei Pixel vom apela constructorul cu parametri al clasei Point.

Pixel::Pixel(int a, int b, int c) : Point(a, b){

color = c;}

void Pixel::clear(){

Point::clear();color = 0;

}

void Pixel::print(){

Point::print();cout << “culoarea : “ << color << endl;

}

Page 119: Curs Program Are C++

Menţionăm că nu puteam defini funcţia clear() a clasei Pixel astfel

void Pixel::clear(){

clear();color = 0;

}

deoarece ar fi însemnat o apelare recursivă. Un exemplu de utilizare a clasei Pixel este prezentat mai jos.

int main(){

Pixel p(1, 2, 15);p.print();return 0;

}

10.3 Funcţii virtuale. Polimorfism

Considerăm exemplul anterior în care, pentru a apela funcţia area() prin pointer pentru cele două obiecte am definit două variabile pointer de tipuri diferite. Este posibil ca un pointer la clasa de bază să conţină adresa unui obiect al unei clase derivate. Exemplu. Fie clasele Point şi Pixel definite anterior. Vom crea obiecte de tip Point şi Pixel şi vom scrie valorile lor utilizând o variabilă pointer de tip Point.

int main(){ // defineste un pointer de tip Point

Point * prt; // creaza un obiect de tip Point

Point p(12, 240); // atribuie variabilei prt adresa obiectului p de tip Point

prt = &p; // afisaza coordonatele utilizand variabila pointer

prt->print(); /* creaza un obiect de tip Pixel */

Pixel px(15, 124, 5); /* atribuie variabilei prt adresa obiectului px de tip Pixel */

prt = &px; /* afisaza coordonatele utilizand variabila pointer */

prt->print();return 0;

}

Page 120: Curs Program Are C++

Menţionăm că ambele instrucţiuniprt->print();

apelează funcţia print() a clasei Point, chiar dacă ne aşteptăm ca în cazul al doilea să se apeleze funcţia print() a clasei Pixel.Funcţia apelată este determinată de tipul variabilei pointer, nu de tipul obiectului. Putem face ca funcţia apelată să fie determinată de tipul obiectului şi nu de tipul variabilei pointer definind funcţii virtuale.Exemplu. Vom redefini clasele Point şi Pixel astfel. Funcţie clear(), ce pune la valoarea zero variabilele, şi funcţia print() ce afişază valoarea variabilelor vor fi declarate virtuale. Definiţia clasei Point este următoarea

class Point{private:

int x, y;virtual void clear();virtual void print();Point(int, int);Point(){x = 0; y = 0;}

};

Definiţiile funcţiilor sunt cele anterioare. Ele vor fi repetate aici.

void Point::clear(){

x = 0;y = 0;

}Point::Point(int p1, int p2){

x = p1;y = p2;

}void Point::print(){

cout << “x = “ << x << “,” << “ y= “ << y << endl; }

Clasa Pixel moşteneşte clasa Point. Constructorul ei apelează constructorul cu parametri al clasei Point, iar funcţiile clear() şi print() apelează funcţiile cu acelaşi nume din clasa Point.

class Pixel : public Point{private:

int color;

Page 121: Curs Program Are C++

virtual void print();virtual void clear();Pixel(int, int, int);

};

Definiţiile funcţiilor sunt cele anterioare.

Pixel::Pixel(int a, int b, int c) : Point(a, b){

color = c;}

void clear(){

Point::clear();color = 0;

}

void Pixel::print(){

Point::print();cout “ culoare = “ << p.color << endl;

}

In funcţia main() vom crea obiecte de tip Point şi apoi Pixel. Vom scrie datele din aceste obiecte apelând funcţia print() printr-un pointer la clasa de bază.

int main(){

Point * ptr; // creaza un obiect de tipul Point

Point p(2, 7); // atribuie variabilei ptr adresa obiectului p de tip Point

ptr = &p; // afisaza coordonatele punctului

ptr->print(); // creaza un obiect de tipul Pixel

Pixel px(1, 2, 15); // atribuie variabilei ptr adresa obiectului px de tip Pixel

ptr = &px; // afisaza coordonatele si culoarea pixelului

prt->print();return 0;

}

Prima instrucţiune

Page 122: Curs Program Are C++

ptr->print();apelează funcţia print() a clasei Point. A doua instrucţiune

ptr->print();apelează funcţia print() a clasei Pixel.Vom spune că apelul de funcţie

ptr->print();este polimorfic, deoarece se modifică după natura obiectului indicat de pointer. Orice clasă ce defineşte sau moşteneşte funcţii virtuale se numeşte polimorfă.Exemplu. Vrem să definim clase care să conţină informaţii despre angajaţii unei intreprinderi, numele şi departamentul, în cazul tuturor angajaţilor, iar în cazul managerilor şi poziţia. Aceste informaţii vor fi şiruri de caractere. Va trebui să avem două tipuri de obiecte, unul ce va conţine numele şi departamentul, şi altul ce va conţine numele, departamentul şi poziţia. Pentru aceasta vom defini o clasă de bază numită Person ce conţine numele unei persoane (un şir de caractere) şi o funcţie ce afişază numele.

class Person{protected:

char * name;public:

Person(char * s){ name = new char[strlen(s) + 1]; strcpy(name, s);}

virtual void print(){cout << “name : “ << name << “\n”;}

};

Vom defini o clasă ce moşteneşte clasa Person, clasa Hired cu date despre angajaţi şi o clasă numită Manager cu date despre manageri ce moşteneşte din clasa Hired conform diagramei de mai jos.

class Hired : public Person{protected:

char * dept;public:

Hired(char * s, char * d) : Person(s){dept = new [strlen(d) + 1]; strcpy(dept, d);}

virtual void print(){Person::print(); cout << “ department : “ << dept << “\n”; }

};

Page 123: Curs Program Are C++

class Manager : public Hired{protected:

char * position;public:

Manager(char * s, char * d, char * p) : Hired(s, d){position = new [strlen(p) + 1]; strcpy(position, p);}

virtual void print(){Hired::print();cout << “ position : “ << position << “\n”; }

};

Ca exemplu de utilizare a claselor create vom defini obiecte de cele două tipuri.

int main(){

Hired x1(“Alex”, “proiectare”);Manager m1(“Bob”, “proiectare”, “sef”);Person * ps;

// scrie datele pentru x1ps = &x1;ps->print();

// scrie datele despre m1ps = & m1;ps->print();return 0;

}

Page 124: Curs Program Are C++

10.4 Date şi funcţii statice

10.4.1 Date statice

Obiectele de tipul unei clase au propriile variabile ce conţin valori ce dau starea fiecărui obiect. Uneori este necesar să existe o variabilă a clasei într-un singur exemplar pentru toate obiectele. Acest lucru se poate realiza simplu definind acea variabilă de tip static.Definirea se face scriind cuvântul cheie static la începutul declaraţiei variabilei. Datele de tip static iniţializate în afara clasei. Datele statice sunt la fel ca şi variabilele globale, dar în spaţiul de nume al clasei.

class A{

static int x;};int A::x = 0;

Datele statice există chiar dacă nu există nici un obiect de tipul clasei. Pentru exemplul de mai sus putem utiliza variabila statică x ca A::x sau A.x sau, dacă există obiecte de tipul clasei, ca nume_obiect.xO aplicaţie a datelor statice este aceea de a număra obiectele existente de tipul clasei. Exemplu. Vom defini o clasă cu o variabilă statică ce va număra obiectele existente de tipul clasei. Constructorul adună o unitate, iar destructorul scade o unitate din variabila statică.

# include <iostream>using namespace std;

class X{public:

static int nb;X(){nb ++;}~X(){nb--;}

};

int X::nb = 0;

In funcţia main() vom crea obiecte şi vom afişa numărul lor.

int main(){

X a, b;cout << “exista “ << X.nb << “ obiecte” << endl;{

X c, d;

Page 125: Curs Program Are C++

cout << “exista “ << X.nb << “ obiecte” << endl;}cout << “exista “ << X::nb << “ obiecte” << endl;return 0;

}

Rezultate afişate vor fiexista 2 obiecteexista 4 obiecteexista 2 obiecte

După instrucţiuneaX a, b;

există două obiecte, a şi b. După instrucţiuneaX c, d;

există patru obiecte. După ieşirea din blocul interior există două obiecte, obiectele c şi d fiind distruse. Menţionăm că putem adresa variabila nb ca X.nb sau X::nb.

10.4.2 Funcţii statice

Funcţiile statice sunt la fel ca şi funcţiile globale. Ele pot prelucra doar datele statice ale clasei. Ele pot fi apelate utilizând numele clasei sau al unui obiect de tipul clasei. Definirea unei funcţii statice se face scriind cuvantul cheie static înaintea definiţiei funcţiei. Un exemplu de utilizare a funcţiilor statice este tehnica de programare ClassFactory. Ea constă în următoarele. Fie o clasă de bază şi două clase derivate din ea, Deriv1 şi Deriv2. Vrem să definim o funcţie care să aibe ca rezultat un obiect de tipul Deriv1 sau Deriv2 în funcţie de valoarea unui parametru.

Exemplu. Vom defini o clasă cu o metodă ce calculează suma a două numere şi o clasă cu o metodă ce calculează produsul a două numere. In ambele cazuri metoda se va numi oper(). Ele vor moşteni o clasă ce memorează cele două numere. Clasa de bază are următoarea definiţie

# include <iostream>using namespace std;

class Base{protected:

float a, b;public:

Base() {a = 0; b = 0;}Base(float, float);virtual float oper(){return 0;}

};

Base::Base(float x, float y)

Page 126: Curs Program Are C++

{a = x;b = y;

}

Clasele derivate definesc funcţia oper() ce efectuează adunarea sau produsul a două numere. Ele au următoarele definiţii

class Add : public Base{public:

Add(float x, float y) : Base(x, y) {}float oper(){return a + b;}

};

class Mul : public Base{public:

Mul(float x, float y) : Base(x, y) {}float oper() {return a * b;}

};

Vom defini o clasă cu o metodă statică ce crează un obiect de tip Add sau Mul în funcţie de valoarea unui parametru şi are ca rezultat un pointer de tipul clasei de bază (Base).

class clf{public:

static Base * operfactory(float, float, char);};

Base * clf::operfactory(float x, float y, char c){

if(c == '+')return new Add(x, y);

elsereturn new Mul(x, y);

};

Funcţia main() crează un obiect de tipul Add şi memorează adresa lui într-un pointer de tipul clasei de bază. Apoi utilizează obiectul la calculul sumei a două numere.

int main(){

float z;Base * base;

Page 127: Curs Program Are C++

clf cf; // creaza un obiect care sa calculeze suma a doua numere

base = cf.operfactory(1.2, -2.35, '+'); // calculeaza suma numerelor

z = base->oper();cout << z << endl;return 0;

}

Cap. 11 Fişiere tip C++

Limbajul C++ defineşte clasele istream şi ostream pentru operaţii intrare şi ieşire cu fişierele standard, tastatura şi ecranul. Obiectul cin este o instanţă a clasei istream, iar cout o instanţă a clasei ostream. Pentru lucrul cu fişiere de date limbajul C++ defineşte următoarele clase

ofstream pentru operaţii de scriere de fişiere ifstream pentru operaţii de citire din fişiere fstream pentru operaţii de citire şi scriere a fişierelor

Aceste clase moştenesc din clasele istream şi ostream. Definiţiile acestor clase se găsesc în biblioteca <fstream>. O serie de constante utilizate de aceste clase sunt definite în clasa ios. Pentru prelucrarea unui fişier se crează un obiect instanţă a uneia din clasele de mai sus care este denumit stream. Diagrama moştenirii acestor clase este cea de mai jos.

Page 128: Curs Program Are C++

Funcţiile membre importante ale claselor sunt funcţia membră open() cu prototipul

open(char * filename, int mode);asociază obiectul cu fişierul. Parametrul filename este un şir de caractere cu numele fişierului. Al doilea parametru este opţional şi dă modul de deschidere. El poate avea valorile

ios::in - deschidere în citireios::out – deschidere în scriereios::binary – fişier binary

Aceşti parametri se pot combina folosind operatorul | . De exemplu, pentru un fişier binar deschis în scriere vom avea

ios::binary | ios:outiar pentru deschiderea unui fişier binar în citire

ios::binary | ios::in Acest al doilea parametru este opţional pentru obiecte de tipul ifstream şi ofstream care sunt automat deschise în citire şi respectiv scriere.

funcţiaclose();

închide un fişier. funcţia

eof()are valoarea adevărat dacă s-a detectat sfârşitul fişierului

funcţiais_open()

are valoarea adevărat dacă fişierul este deschis

11.1 Fişiere text

Există două tipuri de operaţii intrare/ ieşire pentru fişiere tip text funcţii pentru intrări / ieşiri cu format funcţii ce scriu / citesc caractere

11.1.1 Funcţii intrare / ieşire cu format

Fişierele tip text se prelucrează în acelaşi mod ca fişierele standard de intrare şi ieşire, cin şi cout. Operatorii de citire şi scriere sunt >> şi <<.Exemplu. Vom calcula valoarea expresiei

radu cautil, 04/29/06,
od
Page 129: Curs Program Are C++

pentru x cuprins în intervalul [0, 2] cu pasul 0.2. Vom scrie valorile calculate într-un fişier cu numele rez.txt.

# include <fstream># include <iostream># include <cmath>using namespace std;int main(){

char * filename = “rez.txt”;ofstream fil1;fil1.open(filename);if(!fil1.is_open){

cout << “ Nu se poate crea fisierul “ << filename << endl;return 0;

}// creaza fisierul

int i;double x, e;for(i = 0; i < 11; i++){ // calculeaza expresia si scrie in fisier

x = i * 0.2;e = (cos(x) * cos(x) + x) / (1.0 + fabs(x)) + sin(x);fil1 << x << ‘\t’ << e << endl;

}fil1.close();

// citeste fisierul creat si afisaza rezulateleifstream fil2;fil2.open(filename);if(!fil2.is_open()){

cout << “ nu se poate deschide fisierul “ << filename << endl;return 0;

}// scrie antetul

cout << "x" << '\t' << "e" << endl;fil2 >> x >> e;while(!fil2.eof()){

cout << x << ‘\t ‘ << e << endl;fil2 >> x >> e;

}

Page 130: Curs Program Are C++

fil2.close();return 0;

}

11.1.2 Funcţii intrare / ieşire tip caracter

Clasele istream şi ifstream au următoarele funcţii membre pentru citirea caracterelor. Funcţia

get(char&);citeşte un caracter, iar funcţia

getline(char * bloc, int size);citeşte cel mult size caractere în vectorul bloc.Clasele ostream şi ofstream au funcţia membră

put(char);ce scrie un caracter.Exemplu. Vom copia un fişier în altul şi vom calcula lungimea fişierului.

# include <iostream># include <fstream>using namespace std;int main(){

char filename [24];cout << “Introduceti numele fisierului de copiat” << endl;cin >> filename;ifstream fila(filename);if(!fila.is_open()){

cout << endl << “Fisierul “ << filename << “ nu exista “ << endl;return 0;

}cout << endl << “Introduceti numele noului fisier” << endl;cin >> filename;ofstream filb(filename);if(!filb.is_open()){

cout << “Fisierul “ << filename << “ nu se poate crea” << endl;return 0;

}char car;int nl = 0;fila.get(car);while(!fila.eof()){

filb.put(car);nl++;

Page 131: Curs Program Are C++

fila.get(car);}fila.close();filb.close();cout << "fisierul " << filename << “ are ” << nl << " caractere" << endl;return 0;

}

Menţionăm că expresia fila.get(car)

are valoarea fals la întâlnirea sfarşitului de fişier. In consecinţă, puteam copia fişierul astfel

while(fila.get(car)){

filb.put(car);nl++;

}

11.2 Fişiere binare

Scrierea şi citirea datelor din fişierele binare se face cu funcţiilewrite(char * block, int size);read(char * block, int size);

Primul parametru este adresa unui vector de caractere de unde sunt scrise datele sau unde sunt citite datele. Al doilea parametru dă numărul de caractere de citit sau de scris. Fişierele au indicatori interni care dau adresa următorului octet de citit sau de scris. Voloarea acestor indicatori este dată de funcţiile

tellg();pentru indicatorul următorului octet de citit.

tellp();pentru indicatorul următorului octet de scris.Indicatorii de poziţie sunt modificaţi de funcţiile

seekg(int offset, int direction);pentru indicatorul de citire şi respectiv

seekp(int offset, int direction);pentru indicatorul de scriere. Parametrul offset dă valoarea cu care se modifică indicatorul. Parametrul direction are valorile

ios::beg – relativă la începutul fişieruluiios::end – relativă la sfarşitul fişieruluiios::cur – relativă la poziţia curentă

Exemplu. Vom crea un fişier binar cu 10 blocuri de câte 10 caractere fiecare, după care citim fişierul şi îl afişăm pe ecran. Primul bloc va conţine cractere ‘0’, al doilea bloc caractere ‘1’, etc. Citirea fişierului se face după schema cunoscută, testând sfarşitul de fişier după fiecare citire.

# include <iostream>

Page 132: Curs Program Are C++

# include <fstream>using namespace std;int main(){

char filename[] = “fis.txt”;char x[11];ofstream fila;

// creaza fisierulfila.open(filename, ios::out / ios::binary);if(!fila.is_open()){

cout << “ Nu se poate crea fisierul “ << filename << endl;return 0;

}int i, j;

// creaza fisierulfor(i = 0; i < 10; i++){

// creaza un blocfor(j = 0; j < 10; j++)

x[j] = ‘0’ + i;x[10] = 0; // scrie blocul in fisierfila.write(x, 11);

}fila.close();ifstream filb;

// citeste si afisaza fisierulfilb.open(filename, ios::binary / ios::in);if(!filb.is_open()){

cout << “Nu se poate citi fisierul “ << filename << endl;return 0;

}filb.read(x, 11);while(!filb.eof()){

cout << x << endl;filb.read(x, 11);

}filb.close();return 0;

}

Page 133: Curs Program Are C++

Exemplu. Vom crea un fişier binar format din 26 de blocuri conţinând şiruri de 10 caractere fiecare. Vom citi apoi fiecare al patrulea bloc modificând indicatorul de citire. Primul bloc va conţine caractere ‘a’, al doilea bloc va conţine caractere ‘b’, etc.

# include <iostream># include <fstream>using namespace std;int main(){

char filename[] = “fil.txt”;char x[11];ofstream filx;

// creaza fisierulfilx.open(filename, ios::out | ios::binary);if(!filx.is_open()){

cout << “ Nu se poate crea fisierul “ << filename << endl;return 0;

}int i, j;for(i = 0; i < 26; i++){

// creaza un blocfor(j = 0; j < 10; j++)

x[j] = ‘a’ + i;x[10] = 0;// scrie blocul in fisierfilx.write(x, 11);

}filx.close();ifstream fily;

// citeste si afisaza fisierulfily.open(filename, ios::binary | ios::in);if(!fily.is_open()){

cout << “Nu se poate citi fisierul “ << filename << endl;return 0;

}// citeste si afisaza fisierul

for(i = 0; i < 26; i = i + 4){ // modifica indicatorul fisierului

fily.seekg(i * 11, ios::beg);fily.read(x, 11);cout << x << endl;

}

Page 134: Curs Program Are C++

fily.close();return 0;

}

Cap. 12 Siruri tip C++

Limbajul C++ defineşte clasa string pentru lucrul cu şiruri. Spre deosebire de şirurile tip C care sunt terminate printr-un octet cu valoarea zero, obiectele acestei clase au un câmp ce conţine lungimea şirului. Clasa string este definită în fişierul header <string>. Clasa are următorii constructori:

constructorul implicit care crează un şir vidstring();

constructor copiere cu argument şir tip C sau şir tip stringstring(const string&);string(const char *);

Operatorul = are ca argument drept un şir tip C sau şir tip string.

Page 135: Curs Program Are C++

Exemple de definiri de obiecte tip stringstring x = "abcd";string y(x);string z = x;string x1("abc");

Clasa implementează operatorii + şi +=(concatenarea şirurilor).Exemple.

string a = x + “xyz”;a += “abc”;

Clasa defineşte operatori de comparare, <, <=, >, >= , = = şi != ce au ca rezultat valorile true sau false.Exemple.

if(x < x1)cout << x1;

elsecout << x;

Funcţii membre importante ale clasei suntint length(); – dă lungimea şiruluiint size(); – dă lungimea şiruluiconst char * c_str(); - converteşte şirul într-un şir tip Cbool empty(); - are valoarea true dacă şirul este vid

Exemplu. Să copiem un şir C++ într-un şir tip C.char c[100];string str(“abcd”);strcpy(c, str.c_str());

Clasa are operatorul de selecţie [] ce poate selecta un caracter din şir s-au atribui o valoare unui caracter din şir.Exemplu. Să scriem un şir, caracter cu caracter.

string s = “abcd”;for(j = 0; j < s.length(); j++)

cout << s[j];Operatorii << şi >> scriu şi citesc şiruri tip string.Vom prezenta acum funcţii de căutare de subşiruri şi de modificare a şirurilor.Fie un obiect tip string. Funcţia find() cu prototipul

int find(char * substring);dă indicele primei apariţii a şirului substring în şirul respectiv. Dacă şirul substring nu există în şir funcţia are ca rezultat lungimea şirului. Exemple. Fie şirul

string str = “abcdefg”;Instrucţiunea

cout << str.find(“cd”) << endl; afişază valoarea 2. (indicele caracterului ‘c’ este 2). Instrucţiunea

cout << str.find(“xyz”) << endl;afişază valoarea 7 (lungimea şirului).Funcţia erase şterge un subşir din şir. Ea are prototipul

erase(int index, int size)

Page 136: Curs Program Are C++

Funcţia şterge size caractere începând cu caracterul cu indicele index.Exemplu. Fie şirul

string str = “ABCD*FGHIJK”;Instrucţiunea

str.erase(4, 2);şterge două caractere începând cu caracterul ‘*’ ce are indicele 4. Noul şir este

“ABCDGHIJK”Funcţia replace înlocuieşte un subşir cu altul. Ea are prototipurile

replace(int index, int size, char * sir);replace(int index, int size, string sir);

şi înlocuieşte subşirul de lungime size ce începe cu caracterul index cu subşirul sir. Exemple. Fie şirul

string str = “ABCDGHIJK”;Instrucţiunea

str.replace(5, 2, “xyz”);înlocuieşte subşirul “HI” cu subşirul “xyz”. Noul şir este

“ABCDGxyzJK”Fie şirul

string s2 = “abc”;Instrucţiunea

str.replace(5, 3, s2);înlocuieşte subşirul “xyz” cu şirul “abc”. Noul şir este

“ABCDGabcJK”Exemplu. Vom rescrie un exemplu anterior în care am utilizat şiruri tip C şi vom utiliza acum şiruri tip string. Vom defini o clasă de bază numită Person ce conţine numele unei persoane (un şir de caractere) şi o funcţie ce afişază acest şir (numele persoanei).

class Person{protected:

string name;public:

Person(string s){ name = s;}

virtual void print(){cout << “name : “ << name << “\n”;}

};

Vom defini o clasă ce moşteneşte clasa Person, clasa Hired cu date despre angajaţi şi o clasă numită Manager cu date despre manageri ce moşteneşte din clasa Hired.

class Hired : public Person{protected:

string dept;public:

Page 137: Curs Program Are C++

Hired(string s, string d) : Person(s){dept = d;}

virtual void print(){Person::print(); cout << “ department : “ << dept << “\n”; }

};

class Manager : public Hired{protected:

string position;public:

Manager(string s, string d, string p) : Hired(s, d){position = p;}

virtual void print(){Hired::print(); cout << “ position : “ << position << “\n”; }

};

Vom exemplifica acum claselor create.

int main(){

Hired x1(“Alex”, “proiectare”);Manager m1(“Bob”, “proiectare”, “sef”);Person * ps;

// scrie datele pentru x1ps = &x1;ps->print();

// scrie datele despre m1ps = & m1;ps->print();return 0;

}

Reamintim că funcţia print() este virtuală, astfel încât apelulps->print();

apelează funcţia print() din clasa corespunzătoare tipului obiectului, producând rezultatul corect. Menţionăm de asemenea că instrucţiunea

Hired x1(“Alex”, “proiectare”);este corectă deoarece constructorul copiere al clasei string are ca argument un şir de tip C sau de tip C++.

Page 138: Curs Program Are C++

Cap 13. Tratarea excepţiilor

13.1. Excepţii

In timpul execuţiei programului pot apare diverse erori. Un tip de erori sunt cele asociate operaţiilor intrare/ ieşire : încercăm să citim un fişier inexistent, vrem să creăm un fişier pe disc dar discul este plin, etc. Al tip de erori sunt cele legate de operaţiile aritmetice : împărţirea prin zero, indici eronaţi, depăşirea gamei de reprezentare a numerelor, etc. Limbajul C++ are un mecanism de tratare a excepţiilor ce permite ca atunci când apare o eroare (o excepţie) programul să apeleze automat o funcţie de tratare a erorii. Această funcţie are ca parametru un obiect cu informaţii despre eroare. Tratarea excepţiilor se face cu instrucţiunile try, catch şi throw. Instrucţiunea pe care o urmărim pentru apariţia

Page 139: Curs Program Are C++

unei excepţii trebuie inclusă într-un bloc try. Dacă apare o excepţie (o eroare), ea este lansată cu instrucţiunea throw care generează un obiect cu informaţii despre eroare şi este prinsă cu instrucţiunea catch care are ca parametru obiectul cu informaţii despre eroare generat de instrucţiunea throw. Forma generală a instrucţiunilor try şi catch este următoarea

try{

// bloc cu instrucţiuni}catch(tip1 arg){

// prelucreaza exceptia}catch(tip2 arg){

// prelucreaza exceptia}

……………catch(tipn arg){

// prelucreaza exceptia}

Tipurile argumentelor din instrucţiunile catch trebuie să fie diferite. Atunci când apare o excepţie trebuie să executăm instrucţiunea throw cu forma

throw obiect;Excepţia este un obiect cu informaţii despre eroare generat de instrucţiunea throw. Ea poate fi un obiect simplu de tip int, char, double, etc., sau un obiect instanţă a unei clase definită de programator. Intr-un bloc pot apare mai multe tipuri de excepţii. Fiecare tip de excepţie este tratată de o anumită instrucţiune catch. Ea se determină după argumentului, care este tipul obiectului generat de instrucţiunea throw. Exemplu. Tratarea unei excepţii împărţire prin zero.

# include <iostream>using namespace std;

int main(){

int x = 2, y = 0, z;try{

if(y == 0)throw y;

z = x / y;cout << "x = " << x << " y = " << y << " x / y = " << z << endl;

}

Page 140: Curs Program Are C++

catch(int a){

cout << "divide by zero" << endl;cout << "line : " << __LINE__ << endl;

}return 0;

}

Limbajul defineşte două constante utile în cazul excepţiilor. Constanta __LINE__ conţine numărul liniei curente compilate, iar constanta __FILE__ conţine numele fişierului curent compilat. Blocul catch încearcă repararea erorii apărute. Dacă nu este posibil acest lucru, se pot utiliza funcţiile exit() sau abort() pentru a termina programul. Pentru a prinde o excepţie trebuie ca tipul obiectului lansat de instrucţiunea throw să coincidă cu tipul obiectului specificat într-o instrucţiune catch, astfel excepţia nu este prinsă.

Preluarea tuturor excepţiilor

Dacă dorim ca un singur bloc catch să preia toate excepţiile unui bloc try vom scrie blocul catch astfel

catch(…){

// trateaza exceptiile}

13.2 Excepţii lansate de funcţii

Intr-un bloc try pot exista apeluri la multe funcţii şi orice funcţie poate lansa excepţii. Excepţiile lansate de o funcţie sunt precizate la definirea funcţiei în felul următor

tip nume_functie(lista de parametric) throw (lista de tipuri){

// corpul functiei}

Exemplu. Vom defini o funcţie ce calculează câtul a două numere întregi şi lansează o excepţie de tip string dacă numitorul este zero. Tipul excepţiei lansate de funcţie este precizat în linia de definiţie a funcţiei şi este un şir tip string ce conţine un mesaj.

# include <iostream># include <string>using namespace std;

int fun(int x, int y) throw(string){

if(y = = 0)

Page 141: Curs Program Are C++

throw string(“impartire prin zero”);else

return x / y;}

int main(){

int a = 10, b = 0, c;try{ // functie ce poate lansa o exceptie

c = fun(a, b);cout << a << “/” << b << “=” << c << endl;

}catch(string mesaj){

cout << mesaj << endl;}return 0;

}

Menţionăm că funcţia ce lansează o excepţie este apelată într-un bloc try, iar excepţia este prinsă în blocul catch corespunzător.

13.3 Excepţii standard

Limbajul C++ defineşte o clasă de bază special proiectată pentru a declara obiecte care să fie lansate de instrucţiunea throw. Clasa se numeşte exception iar prototipul ei se află în biblioteca <exeception>. Clasa defineşte un constructor implicit, un constructor cu parametru şir de caractere şi unul copiere, operatorul = şi funcţia virtuală what() cu prototipul

virtual char * what();

care are ca rezultat un mesaj despre excepţie.Exempul. Vom rescrie exemplul anterior utilizând clasa exception.

# include <iostream># include <exception>using namespace std;

int fun(int x, int y) throw(exception){

if(y = = 0)throw exception(“impartire prin zero”);

elsereturn x / y;

Page 142: Curs Program Are C++

}

int main(){

int a = 10, b = 0, c;try{ // functie ce poate lansa o exceptie

c = fun(a, b);cout << a << “/” << b << “=” << c << endl;

}catch(exception exm){

cout << exm.what() << endl;}return 0;

}

Cap. 14 Aplicaţii

14.1 Funcţii de timp

Limbajele C şi C++ au funcţii predefinite pentru a obţine timpul. Prototipurile acestor funcţii se găsesc în bibliotecile <time.h> sau <ctime>. Timpul este măsurat în milisecunde de la data de 1 Ianuarie 1970 GMT. Tipul variabilelor ce memorează timpul este long int şi este redefinit ca time_t.

typedef long time_t; /* time value */Funcţia standard ce completează această variabilă este

time_t time(time_t * timeptr);Funcţia time are ca rezultat timpul în parametrul timeptr şi ca valoare.

Page 143: Curs Program Are C++

Exemplu.time_t t1, t2;t1 = time(&t2);

Variabilele t1 şi t2 primesc ca valoare timpul curent.Funcţia ctime converteşte timpul unei variabile time_t într-un şir de caractere.

char* ctime(time_t * timeptr);Exemplu. Instrucţiunea

cout << ctime(&t1) << endl;afişază timpul din variabila t1.Structura predefinită tm conţine următoarele informaţii

struct tm { int tm_sec; /* seconds after the minute - [0,59] */

int tm_min; /* minutes after the hour - [0,59] */int tm_hour; /* hours since midnight - [0,23] */int tm_mday; /* day of the month - [1,31] */

int tm_mon; /* months since January - [0,11] */int tm_year; /* years since 1900 */int tm_wday; /* days since Sunday - [0,6] */int tm_yday; /* days since January 1 - [0,365] */int tm_isdst; /* daylight savings time flag */

};Funcţia

struct tm * localtime(time_t * timeptr);converteşte timpul unei variabile time_t în structura tm.Exemplu.

time_t t1;struct tm t2;struct tm * ptrt;t1 = time(NULL);ptrt = localtime(&t1);t2 = * ptrt;cout << t2.tm_hour << ":" << t2.tm_min << ":" << t2.tm_sec << endl;

Funcţia time_t mktime(struct tm * t);

converteşte timpul dintr-o variabilă tm într-o structură time_t.Funcţia

struct tm * gmtime(const time_t * timeptr);are ca rezultat un pointer la o structură tm ce conţine timpul GMT.

14.2 Fire de execuţie

Programele de până acum au fost procese cu un singur fir de execuţie (thread). Este posibil să cream programe cu mai multe fire de execuţie simultan. Fiecare fir execută o anumită funcţie cu prototipul

void f(void *);

Page 144: Curs Program Are C++

Prototipurile funcţiilor pentru lucrul cu fire de execuţie se află în biblioteca <process.h>. Limbajul C defineşte următoarele funcţii pentru lucrul cu fire de execuţie

funcţiaunsigned long _beginthread(void (*f) (void *), unsigned int stack, void * arglist);

lansează în execuţie un fir. Primul parametru este un pointer la funcţia ce va fi executată de fir. Al doilea parametru este dimensiunea unei stive ce poate fi utilizată de fir. Ultimul parametru este o listă de argumente pasată firului.

funcţia_sleep(int ms);

suspendă execuţia firului de aşteptare pentru un număr de milisecunde dat de parametrul ms.Exemplu. Vom face un program care să lanseze în execuţie două fire. Funcţiile executate de fire vor executa un ciclu în care se scrie un mesaj şi apoi se pune firul în aşteptare un număr de milisecunde. In exemplul nostru dimensiunea stivei firelor de execuţie va fi zero iar lista de parametri pasaţi firelor va fi NULL.

# include <stdio.h># include <stdlib.h># include <process.h>

// functie executata de un firvoid fthr1(void * arg){

int i;for(i = 0; i < 5; i++){

printf("thread1\n");_sleep(1000);

}return;

}

// functie executata de un firvoid fthr2(void * arg){

int i;for(i = 0; i < 5; i++){

printf("thread2\n");_sleep(500);

}return;

}

int main(){

Page 145: Curs Program Are C++

_beginthread(&fthr1, 0, NULL);_beginthread(&fthr2, 0, NULL);return 0;

}

Cap. 15 Biblioteca de şabloane standard

In activitatea de programare apar frecvent anumite structuri de date: liste simplu sau dublu înlănţuite, cozi, stive, mulţimi, vectori, etc. Limbajul C++ are o bibliotecă cu clase special proiectate pentru crearea şi manipularea acestor structuri. Aceste clase sunt clase generice care au ca parametru tipul obiectelor manipulate. Biblioteca de şabloane standard are trei componente

Containere. Un container este o structură de date ce conţine alte obiecte. Aceste obiecte se numesc elementele containerului. Exemple de containere sunt vectorii şi listele.

Iteratori. Iteratorii permit parcurgerea elementelor unui container

Page 146: Curs Program Are C++

Algoritme. Algoritmele sunt funcţii ce se aplică asupra elementelor unui container, de exemplu sortarea elementelor unei liste sau ale unui vector.

15.1 Clase generice

Clasele din biblioteca de şabloane standard sunt clase generice în care tipurile datelor şi funcţiilor sunt parametri. O clasă generică se defineşte cu instrucţiunea template cu forma

template <class T1, class T2, …, class Tn>class nume_clasa{

// definitia clasei};

In această definiţie T1, T2, …, Tn sunt tipuri ce se pot utiliza la declararea de obiecte de tipul clasei generice. Un obiect de tipul unei clase generice se declară cu următoarea diagramă sintactică

nume_clasa <tip1, tip2, …, tipn> nume_obiect;

Exemplu. Să definim o clasă generică X ce poate calcula pătratul unui număr întreg sau real.

# include <iostream>using namespace std;

template <class T>class X{private:

T a;public:

X(T b) {a = b;}T square() {return a * a;}T geta() {return a;}

};

Să definim obiecte de tipul X ce conţin elemente de tip int sau double utilizând clasa generică X.

int main(){

// crează un obiect cu şablonul <int>X<int> n(2);cout << "patratul valorii" << n.geta() << " este " << n.square() << endl;// crează un obiect cu şablonul <double>

Page 147: Curs Program Are C++

X<double> d(3.14);cout << "patratul valorii" << d.geta() << " este " << d.square() << endl;return 0;

}

Biblioteca de şabloane standard are trei componente containere. Ele sunt obiecte ce memorează alte obiecte. Exemple de containere

sunt vectori, liste, cozi, stive. iteratori sunt obiecte ce permit parcurgerea elementelor unui container algoritmi ce implementează operaţii asupra elementelor containerelor (de exemplu

sortarea elementelor unui vector)In cele ce urmează vom prezenta vectorii şi listele.

15.2 Vectori

Clasa generică vector implementează vectori cu elemente de un anumit tip. Un obiect de tip vector are un număr iniţial de componente şi dimensiunea lui creşte dacă este nevoie. Clasa vector are ca parametru tipul componentelor vectorului. Clasa defineşte următorii constructori

constructorul implicit (fără parametri) vector();

crează un vector vid constructorul copiere

vector(vector p);crează un vector în care copiază elementele unui vector p care este parametru

Fie T tipul componentelor vectorului (parametrul clasei generice). Clasa defineşte următoarele funcţii

void push_back(T&); adaugă un element la sfârşitul vectorului void pop_back(); ştrege ultimul element al vectorului int size(); dă numărul de componente ale vectorului bool empty(); are valoarea true dacă vectorul este vid operatorul [] şi funcţia T& at(int) selectează un element al vectorului T& front(); are ca rezultat primul component al vectorului T& back();are ca rezultat ultimul component al vectorului

Menţionăm că un vector poate avea două sau mai multe elemente egale.Exemplu. Să creăm şi să listăm un vector cu componente întregi.

# include <iostream># include <vector>using namespace std;int main(){

// definim un vector cu componente intregivector <int> v;// adauga 4 elemente v.push_back(12);

Page 148: Curs Program Are C++

v.push_back(-5);v.push_back(7);v.push_back(13);// afisaza componentele vectoruluiint k;for(k = 0; k < v.size(); k++)

cout << v[k] << endl;return 0;

}

Menţionăm că expresia v[k] putea fi înlocuită cu expresia v.at(k).La definirea obiectului v, vectorul are alocate un număr implicit de componente

După execuţia instrucţiunilor push_back() vectorul este următorul

şi are dimensiunea 4. Menţionăm că vectorul are un element fictiv înaintea primului element şi altul după ultimul element.

Iteratori

Pentru parcurgerea componentelor unui container (vector, listă, etc.) se pot utiliza iteratori. Un iterator este asemenea unui pointer la un element al containerului. Clasa iterator defineşte următorii operatori

operatorii ++ şi - - modifică iteratorul la următoarea / precedenta poziţie din container

operatorii relaţionali = =, !=, <, <=, >, >= permit compararea a doi iteratori operatorul de adresare indirectă * permite adresarea / modificarea valorii de la

poziţia curentă a containeruluiUn iterator poate fi iniţializat la orice poziţie iniţială din container. Vom menţiona că există două tipuri de iteratori ce permit parcurgerea vectorilor în sens direct şi respectiv în sens invers.

Parcugerea unui vector în sens direct

Clasa iterator este o clasă internă a containerului (în cazul nostru a clasei vector). Un obiect de tipul acestei clase se defineşte astfel

vector<tip>::iterator nume_obiect;Clasa vector defineşte funcţiile

begin();end();

Page 149: Curs Program Are C++

ce au ca rezultat un iterator ce indică primul element al vectorului şi respectiv elementul fictiv primul ce urmează după ultimul element al vectorului. Pentru vectorul anterior avem situaţia din figura de mai jos

Parcurgerea unui vector în sens invers

Clasa reverse_iterator este o clasă internă a containerului (în cazul nostru a clasei vector). Un obiect de tipul acestei clase se defineşte astfel

vector<tip>::reverse_iterator nume_obiect;Clasa vector defineşte funcţiile

rbegin();rend();

ce au ca rezultat un iterator ce indică ultimul element al vectorului şi respectiv elementul fictiv ce precede primul element al vectorului. Pentru vectorul anterior avem situaţia de mai jos

Exemplu. Să parcurgem vectorul anterior utilizând iteratori.

# include <iostream># include <vector>using namespace std;int main(){ // definim un vector cu componente intregi

vector <int> v;// se adauga 4 elemente v.push_back(12);v.push_back(-5);v.push_back(7);v.push_back(13);

// afisaza componentele vectorului utilizand iteratorivector <int>::iterator it;for(it = v.begin(); it != v.end(); it++)

cout << *it << endl; // afisaza conponentele vectorului in ordine inverse utilizand iteratori

Page 150: Curs Program Are C++

vector<int>::reverse_iterator iter1;for(iter1 = v.rbegin(); iter1 != v.rend(); iter1++)

cout << *iter1 << endl;return 0;

}

Putem reprezenta parcurgerea vectorului ca mai jos

Sortarea componentelor unui vector

Biblioteca de şabloane standard defineşte funcţii de sortare a componentelor unui vector. Aceste funcţii sunt definite în biblioteca <algorithm>. Prima dintre acestea, funcţia sort() are următorul prototip

sort(iter1, iter2);unde iter1 şi iter2 sunt iteratori ce dau primul element din vector şi ultimul element din vector ce vor fi sortaţi. Prototipul funcţiei sort() este definit în biblioteca <algorithm>.Exemplu. Vom crea un vector cu componente tip string şi îl vom sorta.

# include <iostream># include <vector># include <algorithm># include <string>using namespace std;

int main(){

int i;vector<string> vst;vst.push_back("abc");vst.push_back("rst");vst.push_back("ccd");vst.push_back("mnp");

// afisaza vectorulcout << "vectorul initial" << endl;for(i = 0; i < vst.size(); i++)

cout << vst.at(i) << endl; // sorteaza vectorul

sort(vst.begin(), vst.end()); // afisaza vectorul

cout << "vectorul sortat" << endl;

Page 151: Curs Program Are C++

for(i = 0; i < vst.size(); i++)cout << vst.at(i) << endl;

return 0;}

A doua funcţie sort() are următorul prototipsort(iter1, iter2, comp);

unde iter1 şi iter2 sunt iteratori ce dau primul element din vector şi ultimul element din vector ce vor fi sortaţi iar comp este o funcţie cu prototipul

bool comp(T x, T y);Parametri x şi y sunt două elemente de tipul T al componentelor vectorului. Funcţia sort permută elementele x şi y dacă funcţia comp are rezultatul false. Prototipul funcţiei sort() este definit în biblioteca <algorithm>.Exemplu. Vom crea un vector cu componente întregi şi îl vom sorta în ordine crescătoare şi apoi descrescătoare. Vom defini două funcţii de comparare pentru cele două cazuri

# include <iostream># include <vector># include <algorithm>using namespace std;

/*funcţie de comparare pentru sortarea elementelor vectorului in ordine crescatoare

*/bool comp(int k, int n){

return k < n;}

/*funcţie de comparare pentru sortarea elementelor vectorului in ordine descrescatoare

*/

bool comp2(int k, int n){

return k > n;}

int main(){

vector<int> v; // creaza vectorul

v.push_back(12);v.push_back(-3);

Page 152: Curs Program Are C++

v.push_back(4);v.push_back(12);

// afisaza vectorulcout << “Vectorul initial” << endl;int i;for(i = 0; i < v.size(); i++)

// cout << v[i] << endl;cout << v.at(i) << endl;

// afisaza vectorul cu iteratorvector<int>::iterator iter;cout << "Vectorul initial\n";for(iter = v.begin(); iter != v.end(); iter++)

cout << *iter << endl; // sorteaza vectorul in ordine crescatoare

sort(v.begin(), v.end(), comp); // afisaza vectorul sortat

cout << "Vectorul sortat in ordine crescatoare\n";for(i = 0; i < v.size(); i++)

cout << v[i] << endl; // sorteaza vectorul in ordine descrescatoare

sort(v.begin(), v.end(), comp2); // afisaza vectorul sortat

cout << "Vectorul sortat in ordine descrescatoare\n";for(i = 0; i < v.size(); i++)

cout << v[i] << endl;return 0;

}

15.3 Liste

Clasa generică list implementează o listă dublu înlănţuită, adică o listă ce poate fi parcursă în ambele sensuri. Lista poate avea oricâte elemente de acelaşi tip. Prototipul clasei este definit în biblioteca <list>. Clasa defineşte următorii constructori

constructorul implicit list();

defineşte o listă vidă constructorul

list(list p); crează o listă în care copiază elementele listei p

Fie T tipul elementelor listei. Funcţiile definite de clasa list sunt următoarele. void push_back(T&); adaugă un element la sfârşitul listei void push_front(T&); adaugă un element la începutul listei void pop_front(); şterge primul element din listă void pop_back();şterge ultimul element din listă T& front(); are ca rezultat primul element din listă T& back();are ca rezultat ultimul element din listă

Page 153: Curs Program Are C++

15.3.1 Parcurgerea listelor

Pentru parcurgerea componetelor unei liste se pot utiliza iteratori. Un iterator este un pointer la un element al listei.

Parcurgerea listelor în ordine directă Pentru parcurgerea unei liste în ordine directă se utilizează clasa iterator care este o clasă internă a clasei list. Un obiect de tipul acestei clase se defineşte astfel

list<tip>::iterator nume_obiect;Operaţiile implementate de clasa iterator sunt cele prezentate în paragraful anterior. Pentru parcurgerea directă a listelor clasa list defineşte funcţiile

begin();end();

ce au ca rezultat un iterator ce indică primul element al listei şi respectiv ultimul element al listei.

Parcurgerea listelor în ordine inversă

Pentru parcurgerea inversă a listelor se utilizează clasa reverse_iterator care este tot o clasă internă a clasei list. Un obiect de tipul acestei clase se defineşte ca

list<tip>::reverse_iterator nume_obiect;Operaţiile implementate de clasa iterator sunt cele prezentate în paragraful anterior.Clasa list defineşte funcţiile

rbegin();rend();

ce au ca rezultat un iterator pentru parcurgerea în sens invers a listei ce indică ultimul element al listei şi respectiv primul element al listei.

15.3.2 Sortarea listelor

Pentru sortarea în ordine directă şi inversă a listelor clasa list defineşte funcţiilevoid sort();void reverse();

ce sorteză elementele listei în ordine directă şi inversă.Exemplu. Vom crea o listă cu elemente întregi, o vom sorta ascendant şi descendent şi vom afişa elementele listei.

# include <iostream># include <list>using namespace std;

int main(){

list <int> ls;

Page 154: Curs Program Are C++

// adauga elemente la sfarsitul si inceputul listeils.push_back(11);ls.push_back(7);ls.push_front(4);ls.push_front(12);

// parcurgerea listeicout << "lista initiala\n";list<int>::iterator iter;for(iter = ls.begin(); iter != ls.end(); iter++)

cout << *iter << endl; // sortarea elementelor

ls.sort(); // parcurgerea listei sortate

cout << "lista sortata\n";for(iter = ls.begin(); iter != ls.end(); iter++)

cout << *iter << endl; // sortarea elementelor in ordine inversa

ls.reverse(); // parcurgerea listei sortate

cout << "lista sortata in ordine inversa\n";for(iter = ls.begin(); iter != ls.end(); iter++)

cout << *iter << endl; // parcurgerea listei

cout << "parcurgerea listei in ordine inversa\n";list<int>::reverse_iterator iter1;for(iter1 = ls.rbegin(); iter1 != ls.rend(); iter1++)

cout << *iter1 << endl;return 0;

}