Sunteți pe pagina 1din 158

Universitatea Transilvania Facultatea de Matematic i Informatic Catedra de Informatic

Note de curs despre Programarea obiect orientat n C++ (incluznd i bazele programrii n C)

Dorin BoKu 2001-2002

n loc de introducere
Dup iniierea n programare, cu sprijinul altor cursuri, paralele cu programarea sau complementare n cel mai bun neles al cuvntului, iat-ne btnd, cu nelepciune sporit, la poarta unei lumi n care vom vedea aceleai concepte ntr-o micare care respect rigorile unui joc nou, mult mai interesant din punct de vedere al mptimiilor n ale programrii. Avem de lmurit i adncit dou teme de meditaie mari i importante pentru formarea oricrui informatician : Paradigma programrii obiect orientate Universul C++ ale crui origini vin dinspre C i dinspre nceputurile dintotdeauna ale programrii. Dup experienele cursurilor introductive n programare, n prezentul curs voi deplasa accentul de la ct mai mult despre la esenialul despre, ceea ce va aduce beneficii tuturor, sper. Pentru titularul de curs este un prilej (nu tocmai comod) de a scoate n eviden cunotinele i abilitile indispensabile unei cltorii la captul creia, cei care nu au abandonat, primesc certificatul simbolic de Oameni care au ncercat limitele unui limbaj de cotitur pentru orice programator. Care este rezultatul unei astfel de ncercri? Depinde, ca ntotdeauna, de curiozitatea i complexul de motive care i nsoesc pe participanii la cltorie. Multe dintre tainele programrii n C/C++ vor trebui aflate n momentul n care, optnd pentru meseria de specialist n ingineria softului, vei avea motive, timp i condiii s o facei. Studentul care citete acest suport de curs trebuie s neleag un lucru elementar: fr determinare clar nu se poate face nimic cu temeinicie; dincolo de determinare se afl foarte mult munc, nelept drmuit ntre numeroasele borne care ateapt s fie trecute, cu o not care s certifice un anumit coeficient de inteligen. Autorul

I BAZELE C++. LIMBAJUL C

1 Privire de ansamblu asupra limbajului C


1.1 Despre originile limbajului C
Legenda spune c C a fost inventat i implementat de Dennis Ritchie pe un calculator DEC PDP-11, care utiliza sistemul de operare UNIX. Virtual, C este rezultatul unui proces de dezvoltare complex, la care putem spune c a participat ntreaga comunitate preocupat de ridicarea performanelor n munca de programare. De fapt, strmoi direci ai limbajului C pot fi considerate limbajele BCPL (creat de Martin Richards) i limbajul B (inventat de Ken Thompson). n anii 70, limbajul B a influenat n cel mai nalt grad specificarea limbajului C. Mult timp, standardul de facto pentru C a fost versiunea ce nsoea sistemul de operare UNIX. Acest standard a fost descris pentru prima dat n cartea The C Programming Language, scris de Brian Kernigan i Dennis Ritchie, aprut n 1978 la editura Prentice Hall. Odat cu creterea popularitii calculatoarelor personale, au fost create numeroase implementri de C, ceea ce a generat o problem nou: compatibilitatea implementrilor. Pentru rezolvarea acestei probleme, n vara anului 1983, a fost nfiinat un comitet pentru crearea unui standard ANSI (American National Standard Institute) care avea ca sarcin specificarea definitiv a limbajului C. Standardul ANSI C a fost adoptat n decembrie 1989, primele copii devenind disponibile la nceputul lui 1990. La ora actual, toate compilatoarele C/C++ se aliniaz la standardul ANSI C. Totodat standardul ANSI C este o baz pentru propunerea de standard ANSI C++(Bazat pe limbajul C, C++ adaug extinderi care permit programarea obiect orientat). Evident, vom reveni asupra topicii C++.

1.2 Locul limbajului C n lumea limbajelor de programare


Specialitii consider C un limbaj de nivel mediu. Aceast apreciere nu presupune neaprat c C este mai puin performant dect limbajele de nivel nalt, precum BASIC sau PASCAL. De asemenea, nu trebuie s concluzionm c C are dificultile unui limbaj de asamblare. C este considerat un limbaj de nivel mediu deoarece combin cele mai bune faciliti ale unui limbaj de nivel nalt cu posibilitile de control i flexibilitatea unui limbaj de asamblare. Astfel, ca limbaj de nivel mediu C permite lucrul cu bii, octei i adrese elemente de baz pentru funcionarea calculatorului. Cu toate acestea, codul C rmne n foarte mare msur portabil.  Despre un sistem soft spunem c este portabil dac n momentul n care este scris pe o anumit main, sub un anumit mediu de operare, poate fi uor adaptat pe alt main, eventual cu alt sistem de operare. Tot ca nite trsturi specifice limbajului C semnalm urmtoarele: Chiar dac C are cinci tipuri de date de baz, nu este un limbaj centrat pe tipuri de date aa cum este, de exemplu, Pascal. Prin aceast afirmaie sugerm faptul c n C lucrul cu diferite instane ale tipurilor de date de baz

beneficiaz de un cadru mult mai liberal (controalele compilatorului nu mai sunt att de severe ca n Pascal). Spre deosebire de un limbaj de nivel nalt, C nu face aproape nici un control n timpul executrii unui program. Responsabilitatea de a evita producerea erorilor i de a le dibui i corecta, dac le-a ocazionat, este a programatorului. C permite lucrul direct cu bii, octei cuvinte i pointeri ceea ce l face potrivit pentru programare la nivel de sistem.  Numim programare la nivel de sistem activitatea de scriere a codului pentru anumite componente ale sistemului de operare sau care sunt accesorii ale sistemelor de operare. C are doar 32 cuvinte-cheie (27 din standardul Kernigan & Ritchie i cinci adugate de comitetul de standardizare ANSI C). Limbajele de nivel nalt depesc cu mult oferta limbajului C. Faptul c are att de puine cuvinte cheie ar trebui s fie benefic pentru procesul de nvare a limbajului. Ceea ce se nva greu n C este democraia prea mare n comparaie, de exemplu, cu stilul autoritar n care compilatorul Pascal asist scrierea de programe corecte.

1.3 C este limbaj structurat


Deoarece nu ofer suport pentru implementarea autentic a conceptului de structur n blocuri, C este numit, simplu, limbaj structurat. C nu este limbaj <structurat n blocuri> deoarece nu permite definirea de funcii n interiorul altor funcii, situaie absolut normal i cu efecte interesante asupra modularizrii codului Pascal, de exemplu. Caracteristica esenial a limbajelor structurate este compartimentarea codului i a datelor, prin care se nelege capacitatea unui limbaj de a separa i ascunde de restul programului toate informaiile i instruciunile necesare efecturii unei sarcini. O modalitate de realizare a compartimentrii este utilizarea de subrutine care folosesc variabile locale. Utiliznd variabile locale putem scrie modulele astfel nct ceea ce se ntmpl n interiorul lor s nu aib efecte n alte seciuni ale programului. C permite compartimentarea n acest spirit, utiliznd potenialul noiunii de funcie.  Utilizarea excesiv a variabilelor globale, n orice limbaj de programare structurat, permite greelilor s se strecoare n program, sub forma efectelor secundare neprevzute. Evident, un limbaj de programare structurat descurajeaz sau interzice utilizarea instruciunii goto. Un alt mod de a structura i de a compartimenta codul, n C, este utilizarea blocurilor de cod. Un bloc de cod este un grup de instruciuni alturate logic, tratate ca un singur element. Blocurile de cod permit reprezentarea multor

algoritmi cu limpezime, elegan i eficien. n C un bloc de cod poate fi creat incluznd ntre acolade un grup de instruciuni. Dac n Pascal am avea: . if I<10 then begin gotoxy(12,I); write(Valoarea curent a lui I:,I); end; . atunci n mod echivalent, n C avem: . if (I<10) { gotoxy(12,I); cout<<Valoarea curent a lui I:<<I; } . Blocul de cod este delimitat de acolade. L-am evideniat folosind efectul de umbrire (shading) a textului.

1.4 Relaia limbajului C cu programatorul


Specialitii n limbaje de programare afirm ceea ce, de fapt, s-a avut n vedere la specificarea unor limbaje: acestea nu sunt pentru programatori. Limbaje precum COBOL sau BASIC au fost specificate nu pentru a ndulci soarta programatorilor sau pentru a mri sigurana n exploatare a codului creat, ci pentru a lrgi accesul celor muli la nelegerea programelor sau chiar la scrierea de programe pentru a rezolva probleme simple. Din contr C (ca i C++) a fost creat, influenat i testat de ctre adevraii programatori. Rezultatul final este c C ofer programatorului exact ceea ce i dorete: restricii puine, structurare, blocuri de cod, funcii de sine stttoare i un set compact de cuvinte cheie. De asemenea faptul c poate fi utilizat n locul limbajelor de asamblare i sporete limbajului C popularitatea printre programatori.

1.5 Componentele de baz ale unui program C


n Tabelul 1 prezentm cuvintele-cheie, care, utilizate conform regulilor sintactice specifice, permit scrierea de programe n C. Obinuina de a scrie programe n alte limbaje de programare poate fi de un real folos n C. Astfel, multe din abilitile cultivate ca autori de programe Pascal sunt transpuse, cu unele modificri de sintax sau semantic i n C. Diferite compilatoare de C pot beneficia de extinderi sintactice care permit programelor s beneficieze de avantajele unor medii de operare particulare.

Auto Break Case Char Const Continue Default Do

struct Int double switch Long else typedef Register enum union Return extern unsigned Short float for void Signed Sizeof goto volatile while Static if Tabelul 1 Cele 32 cuvinte-cheie definite de standardul ANSI C

Din cele 32 cuvinte-cheie prezentate n Tabelul 1, cele prezentate cu caractere bold au fost adugate de comitetul ANSI C, celelalte corespunznd standardului C original.  Toate cuvintele-cheie n C se scriu cu litere mici . De asemenea, este important s tim c n C literele mari difer de cele mici. Astfel c, n timp ce for este cuvnt-cheie, FOR nu este cuvnt-cheie. Toate programele C constau din una sau mai multe funcii a cror sintax i semantic o vom prezenta n seciunile urmtoare. n sfrit, componentele unui program C simplu se grupeaz n urmtoarele seciuni: Directive preprocesor n aceast seciune se bibliotecile de care are programul includ nevoie

Seciunea de definire a tipurilor i n aceast seciune sunt definite constante sau tipuri de date necesare constantelor globale n program Antet program principal Seciunea declarativ Seciunea executabil n aceast seciune se fac toate declaraiile de variabile ale programului n aceast seciune se amplaseaz versiunea cod C a algoritmului care rezolv problema

Un programator Pascal recunoate n acest ablon de organizare a codului unui program C o parte din filozofia scrierii codului Pascal dar i o serie de elemente inedite care l atenioneaz asupra faptului c limbajul C este o lume care propune o nou abordare a unei probleme mai vechi :nvarea calculatoarelor

s rezolve anumite probleme. Aceast nou abordare o vom dezbate treptat n seciunile care vor urma.

2 Expresii n C
n aceast seciune vom trece n revist propunerile limbajului C n ceea ce provete posibilitatea de a utiliza expresii. Toate limbajele de programare acord o atenie special noiunii de expresie deoarece, n ultim analiz, orice transformare a datelor de intrare ale unui program este realizat prin intermediul uneia sau a mai multor expresii. Aa cum vom descoperi pe parcurs, C este un limbaj mult mai flexibil dect alte limbaje n materie de definire i utilizare a expresiilor. Ceea ce este foarte limpede n acest moment este faptul c expresiile sunt realizate cu ajutorul elementelor atomice numite date i operatori. Datele pot fi reprezentate cu ajutorul constantelor sau prin intermediul variabilelor. Ajungem, evident, la problema tipurilor de date suportate de C.

2.1 Cinci tipuri de date de baz n C


n C exist cinci tipuri de date de baz: caracter (char), ntreg (int), virgul mobil (float), virgul mobil dubl precizie (double) i fr nici o valoare (void). Toate celelalte tipuri de date din C se bazeaz pe cele cinci tipuri de baz. Dimensiunea reprezentrii i domeniul valoric corespunztor pentru tipurile de date de baz pot s difere, n funcie de tipul procesorului i de modul de implementare a limbajului C. n toate cazurile, ns, un caracter se reprezint pe un octet. Chiar dac de multe ori un ntreg se reprezint pe doi octei, nu se poate conta pe aceast observaie dac dorim ca programele scrise s fie portabile. De fapt, nsui standardul ANSI C, stipuleaz doar domeniul de cuprindere minimal al fiecrui tip de date, nu i mrimea sa n octei. Formatul exact al valorilor n virgul mobil depinde de modul lor de introducere. ntregii corespund, n general, mrimii normale a unui cuvnt pe calculatorul respectiv. Valorile de tip char sunt, n general, utilizate pentru a memora valori definite de setul de caractere ASCII. Valorile care ies din acest domeniu sunt tratate n mod diferit de compilatoare. Domeniul valoric pentru float i double va depinde de metoda folosit pentru a reprezenta numere n virgul mobil. Standardul ANSI C indic domeniul valoric pentru virgul mobil de la 1E-37 pna la 1E+37. Evident, vor exista deosebiri att n ceea ce privete ordinul de mrime ct i n ceea ce privete precizia ntre cele dou specii de virgul mobil. Numrul minim de cifre din punct de vedere al preciziei este precizat n Tabelul 2. Tipul void declar explicit c o funcie nu returneaz nici o valoare sau creaz pointeri generici.

Tip de dat char unsigned char signed char int unsigned int signed int short int unsigned short int signed short int long int signed long int unsigned long int float double long double

Dimensiune reprezenatre Domeniul valoric minimal n bii 8 -127..127 8 0..255 8 -127..127 16 -32767..32767 16 0..65535 16 Similar cu int 16 Similar int 16 0..65535 16 Similar cu short int 32 -2.147.483.647..2.147.483.647 32 Similar cu long int 32 0..4.294.967.295 32 6 zecimale exacte 64 10 zecimale exacte 80 10 zecimale exacte Tabelul 2 Tipurile de date definite prin standardul ANSI C

2.2 Modificarea tipurilor de baz


Exceptnd tipul void, tipurile de baz pot fi precedate de diveri specificatori de conversie. Un specificator de conversie se utilizeaz pentru a modifica tipul de baz n ideea de adaptare mai precis la diferite situaii. Dup cum rezult i din Tabelul 2, lista specificatorilor de conversie este: signed unsigned long short n mod evident, diferena ntre ntregii cu semn i ntregii fr semn const n modul n care se interpreteaz bitul de pe poziia cea mai semnificativ. Dac, de exemplu, indicai un ntreg cu semn, compilatorul genereaz cod care presupune c bitul cel mai semnificativ va fi utilizat ca bit de semn (0 dac numrul este pozitiv, 1 dac numrul este negativ). S mai menionm faptul c, n general, pentru reprezentarea ntregilor negativi se folosete codul complementar (complementul fa de 2). Detalii referitoare la caracteristicile codului complementar se pot obine consultnd lucrri de specialitate n care se dezbate problema codificrii datelor numerice n sistemele de calcul. n acest sens se poate consulta lucrarea [4] care face o introducere accesibil n problematica mai sus menionat.

2.3 Nume de identificatori n C


n C/C++ numele variabilelor, funciilor, etichetelor i ale altor diverse obiecte definite de ctre utilizator sunt numite identificatori. Aceti identificatori pot s aib unul sau mai multe caractere. Primul caracter trebuie s fie obligatoriu o

10

liter sau o liniu de subliniere; urmtoarele pot fi litere, cifre sau liniua de subliniere. Exemple de identificatori: Coreci numr_de pagini _limita1 ok_ Incoreci 1_valoare_returnat ok! switch..conversie

Standardul ANSI C stipuleaz c identificatorii pot avea orice lungime. Totui, nu toate caracterele sunt obligatoriu semnificative. Dac identificatorul este implicat ntr-un proces de editare de legturi externe, vor conta cel mult ase caractere. Aceti identificatori, denumii nume externe, includ numele funciilor i ale variabilelor globale care aparin mai multor fiiere. Dac fiierul nu este utilizat ntr-un proces de editare de legaturi externe, atunci vor fi semnificative cel mult 31 de caractere. Acest tip de identificator este denumit nume intern i include, de exemplu, nume de variabile locale. De remarcat faptul ca n C++ nu exist limite ale lungimii unui identificator i toate caracterele sunt semnificative. Aceast diferen ncepe s conteze n momentul n care dorii s convertii un program din C n C++. Pare evident, dar facem precizarea c un identificator C nu poate fi identic cu un cuvnt-cheie i nu trebuie s aib acelai nume ca o funcie din biblioteca C sau C++.

2.4 Variabile C
Aa cum s-a aflat i n alte mprejurri, o variabil este numele unei locaii de memorie utilizat pentru a pstra o valoare care poate fi modificat de program. nainte de a fi utilizate n program, variabilele trebuie declarate. Declararea unei variabile n C are forma: <Tip> <List_de_variabile>; <Tip> trebuie s fie un tip de dat valid (predefinit sau definit de utilizator) precedat, eventual, de un specificator de conversie. <List_de_variabile> poate consta dintr-un nume de identificator sau mai multe nume de identificatori separate prin virgul. Exemple de declaraii de variabile: int nr_pag; char opt,ch; unsigned i, j; Variabilele se pot declara n trei moduri: n interiorul funciilor, n cadrul definiiei parametrilor funciei i n afara oricrei funcii. Prin urmare, este vorba despre variabile locale, parametri formali i variabile globale.

11

2.5 Variabile locale n C


Variabilele declarate n interiorul unei funcii sunt numite variabile locale. O parte din literatura C/C++ numete aceste variabile automatice. innd cont de asemnarea evident cu variabilele locale din Pascal, de exemplu, vom folosi termenul de variabil local. Afirmaia cea mai important referitor la variabilele locale este urmtoarea: Variabilele locale sunt accesibile doar instruciunilor care sunt n interiorul blocului n care sunt declarate variabilele. Alt amnunt important i specific limbajului C este faptul c: Variabilele locale exist ct timp se execut blocul de cod n care sunt declarate. Altfel spus, o variabil local este creat la nceperea execuiei blocului su i este distrus la ncheierea execuiei acestui bloc. Blocul de cod cel mai uzual n care se declar variabile este funcia. Deoarece noiunea de funcie este central pentru fizionomia unui program C, prezentm, n continuare, o viziune asupra unui program C care include i noiunea de funcie. Directive preprocesor Declaraii globale Declaraie funcie_1 : Declaraie funcie_n int main() { Declaraii de date ale programului principal Instruciuni program principal Orice program C conine obligatoriu o funcie al crei nume este main } Implementare funcie_1 : Implementare funcie_n Totodat s precizm faptul c sintaxa de declarare a unei funcii (prototip n C++), conform standardului ANSI C este : <Tip returnat> <Nume funcie> (<Lista de parametri formali>) Toat aceast construcie sintactic se mai numete i <Antet funcie>. n C clasic declararea unei funcii permitea doar specificarea tipului returnat, problema eventualilor parametri ateptai de funcie avnd o rezolvare aparte, pe

12

care nu mai insistm deoarece standardul ANSI C ca i standardul ANSI C++ descurajeaz, respectiv interzice utilizarea cii clasice. n C++ declararea unei funcii se face apelnd la prototipuri. n sfrit, implementarea unei funcii nseamn o construcie sintactic de tipul: <Antet funcie> { <Declaratii de date> <Instructiuni executabile> } Referindu-ne la standardul ANSI C/ANSI C++ putem spune c: <Nume funcie> este un identificator valid. <Tip returnat> specific tipul de dat asociat cu numele funciei. <Lista de parametri formali> const din o serie de perechi de denumiri, separate ntre ele prin virgul, unde prima denumire specific un tip de dat iar cea de-a doua este numele parametrului formal. Parametrii formali care apar n antetul unei funcii, n principiu, trebuie s corespund ca tip cu parametrii actuali care apar la apelul unei funcii.Cnd un parametru actual trimis unei funcii nu se potrivete exact, ca tip, cu parametrul formal corespunztor, compilatorul ncearc o conversie convenabil. De exemplu, o dat de tip int trimis unei funcii care ateapt o dat de tip float este convertit la tipul float. Prezentm, n continuare, cteva exemple. void f1(void) { int x; x=12; } void f2(void) { int x; x=-128; } Variabila x este declarat att n f1 ct i n f2. Variabila x din f1 nu are nici o influen asupra variabilei x din f2 deoarece fiecare variabil este cunoscut doar n blocul de cod gazd. Din obinuin i din respect fa de tradiie, majoritatea programatorilor declar variabilele folosite de o funcie imediat dup acolada deschis a funciei i nainte de orice instruciune executabil. De fapt, se pot declara variabile n orice bloc de cod, obinndu-se chiar efecte benefice pentru calitatea codului rezultat. Astfel putem avea: void fexemplu(void) { int i; printf(Introduceti un
13

numar:);

scanf(%d,&i); if (i>0) { char nume [40]; printf(Numele Dvs.:); gets(nume); } } Din exemplul de mai sus reinem doar faptul c, n situaia n care numrul preluat de la tastatur (cu ajutorul rutinei de uz general scanf ) este mai mare dect zero, atunci devine activ blocul de cod n care se declar variabila nume. Aceast variabil este distrus la ieirea din acest bloc de cod. Prin urmare, un avantaj potenial: variabilei nume i se aloc memorie numai dac se activeaz blocul de cod, ceea ce nseamn economie de memorie. Este absolut evident faptul c se declar ntr-un bloc de cod una sau mai multe variabile strict necesare n acest bloc de cod; n acest mod prevenim, ntr-o oarecare msur, apariia unor efecte secundare. Mai semnalm o diferen important ntre modul de declarare a variabilelor locale n C fa de C++. n C trebuie declarate toate variabilele locale la nceputul blocului n care intenionm s le definim, nainte de orice instruciune executabil. n C++ nu mai exist aceast restricie. Astfel c secvena de cod C: void f(void) { int i; i=10; int j; j=20; } este considerat greit de un compilator C i perfect acceptabil de ctre un compilator C++. Deoarece variabilele locale sunt create i distruse la fiecare intrare, respectiv ieire din blocul n care au fost declarate, coninutul lor se pierde odat cu prsirea blocului. Acest lucru este important s se tie cnd apelm o funcie. La apelarea ei sunt create variabilele locale iar la ncheierea ei acestea sunt distruse, deci variabilele locale nu pstreaz valorile lor ntre apelri. Putem determina compilatorul s pstreze aceste valori utiliznd specificatorul de stocare static asupra cruia vom reveni.

2.6 Parametrii formali


Dac o funcie urmeaz s foloseasc argumente, ea trebuie s declare variabilele pe care le accept ca valori ale argumentelor. Aceste variabile sunt denumite parametri formali ai funciei. Variabilele n cauz se comport ca oricare alt variabil local din acea funcie.

14

2.7 Variabile globale


Variabilele globale, spre deosebire de cele locale, sunt cunoscute n tot programul i pot fi utilizate de ctre orice zon a codului. Totodat, ele i pstraz valoarea tot timpul execuiei programului. Variabilele globale se creaz prin declarare n afara oricrei funcii. Orice expresie are acces la ele, indiferent de tipul blocului de cod n care se afl expresia. De semnalat faptul c o variabil global este vizibil n orice bloc de cod al programului ct timp n respectivul bloc de cod nu a fost declarat o variabil cu acelai nume, de acelai tip. n caz afirmativ, n blocul de cod este prioritar referirea la variabila local. Stocarea variabilelor globale este fcut de compilator ntr-o zon de memorie, special alocat. Variabilele globale sunt utile atunci cnd mai multe funcii ale aceluiai program folosesc aceleai date. Utilizarea excesiv a variabilelor globale ar trebui evitat deoarece: Ocup memoria pe tot timpul execuiei programului, nu doar cnd sunt necesare; Dac o funcie utilizeaz o variabil global n locul uneia locale, atunci funcia i pierde din generalitate, bazndu-se pe ceva exterior logicii ei; Variabile globale multe nseamn efecte secundare necunoscute i nedorite.

2.8 Modelatori de acces


n C exist doi modelatori de acces (const i volatile) care introduc restricii suplimentare asupra modului de apelare sau modificare a variabilelor. Aceti modelatori trebuie s precead specificatorul de tip i numele tipului de dat la care se refer. Variabilele de tip const nu pot fi modificate de programele n care sunt declarate. Acestor variabile li se permite, totui, s primeasc o valoare iniial. Compilatorul poate s plaseze variabilele de acest tip n ROM (Read Only Memory). Evident, aceste variabilele de tip const pot fi utilizate n construcia unor expresii. Tehnic vorbind, modelatorul const poate fi utilizat i pentru a proteja obiectele trimise ca argumente unei funcii, de modificri n interiorul acelei funcii. Multe funcii din biblioteca standard a limbajului utilizeaz const n declaraiile lor de parametri. Ca un exemplu, funcia strlen() are urmtorul prototip: size_t strlen( const char * s); Modelatorul volatile informeaz compilatorul c valoarea unei variabile poate fi modificat pe ci nedeclarate explicit de program. Aceasta nseamn c este posibil urmtorul scenariu: Adresa unei variabile este transmis unei rutine rezidente n memoria sistemului, aceast rutin fiind abilitat s modifice valoarea variabilei. Evident, pentru programatori acesta este un mecanism extrem de practic i interesant. Modelatorul volatile poate fi combinat cu modelatorul const fcndu-se plauzibil scenariul: Valoarea unei variabile poate

15

fi modificat de condiii externe, fiind protejat de modificri accidentale n programul n care este declarat.

2.9 Specificatori de clase de stocare


Limbajul C admite patru specificatori de clase de stocare: extern static register auto Aceti specificatori indic compilatorului modul n care trebuie s stocheze variabilele la care se refer. Cadrul sintactic general de utilizare a specificatorilor de clase de stocare este: <Specificator_de_stocare> <Tip> <Nume_variabil>; extern Deoarece C/C++ permit seciunilor separate ale unui program s fie compilate independent i s li se editeze legturile mpreun, trebuie s existe o modalitate de a comunica tuturor fiierelor variabilele globale necesare programului. Scenariul cruia trebuie s i facem fa este urmtorul: Cum putem informa toate fiierele care compun un program C despre variabilele globale utilizate?. Soluia este urmtoarea : Se declar variabilele globale ntr-un fiier, care este de preferat s conin i funcia principal a programului, aceleai variabile declarndu-se i n toate fiierele care folosesc variabilele globale, nsoite de specificatorul extern. static Variabilele de tip static sunt variabile permanente n interiorul funciei sau fiierului n care se gsesc. Spre deosebire de variabilele globale, ele nu sunt cunoscute n afara funciei sau fiierului, dar i pstreaz valoarea ntre dou apelri. Aceast caracteristic este folositoare la scrierea de funcii generice i de bibliotec , utilizabile de alte programe. Specificatorul static are efecte diferite asupra variabilelor locale i globale. Variabile locale statice Aplicat unei variabile locale, specificatorul static informeaz compilatorul de dorina de a i se asocia variabilei un loc de stocare permanent, similar celui asociat unei variabile globale. Diferena esenial ntre variabilele locale statice i o variabil global se refer la faptul c variabila local static rmne cunoscut doar blocului n care a fost declarat. Altfel spus, o variabil local static este o variabil care i pstreaz valoarea ntre apelurile funciei n care a fost declarat. Variabile globale statice

16

Aplicnd specificatorul static unei variabile globale, cerem compilatorului s creeze o variabil global care este cunoscut doar n fiierul n care a fost declarat. Aceasta nseamn c, dei variabila este global, rutine din alte fiiere nu au acces la ea i nu i pot modifica coninutul. register Specificatorul de stocare register se aplic, prin tradiie, doar variabilelor de tip int i char. Totui standardul ANSI C i d definiia astfel nct poate fi folosit pentru orice tip de variabil. Iniial, register cerea compilatorului s pstreze valoarea unei variabile ntr-un registru din CPU, nu n memoria RAM, unde variabilele sunt stocate, de regul. Scopul unei astfel de cereri: spor de vitez n timpul operaiilor asupra variabilei. Actualmente, definiia specificatorului register a fost mult extins, acesta putnd fi aplicat oricrui tip de variabil. Standardul ANSI C stipuleaz, simplu, c register este o indicaie dat compilatorului c obiectul vizat va fi utilizat preferenial din punct de vedere al vitezei. Specificatorul se poate aplica doar variabilelor locale i parametrilor formali. Prin urmare, nu sunt permise variabile globale de tip register. auto Specificatorul auto este, practic, nefolosit, fiind reclamat doar de motive de compatibilitate ntre codul C i codul C++.

2.10 Iniializarea variabilelor


Forma general a iniializrii unei variabile respect sintaxa: <Tip> <Nume_variabil> = <Constanta>; Exemple: char ch=A; int media=0; float bilant=0; Variabilele globale i cele statice locale sunt iniializate doar la nceputul execuiei programului. Variabilele locale (cu excepia celor de tip static, sunt iniializate de fiecare dat cnd este ntlnit blocul n care sunt declarate. Variabilele locale care nu sunt iniializate au valori necunoscute nainte de prima atribuire. Variabilele globale i locale de tip static neiniializate sunt iniializate din oficiu cu zero. Evident, trebuie s discutm n continuare despre problema reprezentrii constantelor n programele C. Constante C

17

Constantele se refer la valori fixe pe care programul nu poate s le modifice. Constantele pot fi de oricare din tipurile fundamentale de date. Modul n care se reprezint n program fiecare constant depinde de tipul su. Constantele de tip caracter sunt incluse ntre ghilimele simple. A i % sunt exemple de constante de tip caracter. C definete i caractere multioctet, n mediile care nu utilizeaz limba englez. O situaie asemntoare se ntlnete i n Delphi. Constantele de tip ntreg sunt specificate ca numere fr parte fracionar. De exemplu, 3 i -15 sunt exemple de constante ntregi. Constantele n virgul mobil cer punctul zecimal urmat de partea zecimal a numrului. 0.75 este un exemplu de constant care apeleaz la virgula mobil. Limbajul C permite i notaia tiinific, cunoscut i n alte limbaje. De asemenea, C stabilete pentru o constant numeric cel mai scurt tip de date compatibil care o poate pstra. Astfel c, -10 este implicit un int, 60.000 este unsigned int iar 120.000 este un long int. Tipul constantei numerice poate fi specificat i explicit ca n exemplele din Tabelul 3. Tip de data int long int short int unsigned int float double long double Exemplu de constant 1, 234, 2000, -243 100000, 23000L, -45L 9, -2000 10000U, 900U, 45000 120.15F, 3.75e-5F 120.15, 22122122, -0.9876432 1000.25L Tabelul 3 Exemple de constante numerice

Dup cum se vede, constantele n virgul mobil sunt asimilate implicit tipului double. Constante hexazecimale i octale Deoarece exist numeroase situaii n care avem nevoie de referiri la sistemele de numeraie octal i hexazecimal, C permite utilizarea constantelor octale i hexazecimale astfel: O constant octal ncepe cu 0 (zero). Exemplu: int octala=017; /*In zecimal 15 */ O constant hexazecimal ncepe cu 0x. Exemplu: int hexa =0x100; /*In zecimal 256*/

18

Constante de tip ir C admite, cum era i firesc i constanta de tip ir de caractere. O constant ir este o succesiune de caractere delimitat de ghilimele. A nu se confunda irurile de caractere cu caracterele. a i a sunt constante de tipuri diferite( disticie care, n Pascal, nu este operat sintactic, vorbind. Constante de tip backslash caracter ncadrarea constantelor de tip caracter ntre apostrofuri funcioneaz pentru majoritatea caracterelor afiabile. ns, altele, puine la numr, sunt imposibil de introdus de la tastatur (constanta BEL, de exemplu). n acest scop C introduce constante speciale, de tip backslash caracter. C admite mai multe coduri backslash, cum rezult i din Tabelul 4. Pentru a se asigura portabilitatea programelor sunt indicate codurile backslash n locul codurilor lor ASCII. Codul \b \f \n \r \t \ \ \0 \\ \v \a \N \xN Semnificaia Backspace form feed (=Salt la pagin nou) CR+LF CR tab orizontal Ghilimele Apostrof Null Backslash tabulare vertical Alert constant n octal; N este constanta constant n hexazecimal; N este constanta Tabelul 4 Coduri backslash n C

2.11 Operatori n C
C dispune de foarte muli operatori. De fapt, C acord acestora o importan mult mai mare n comparaie cu alte limbaje. C definete, n esen, patru clase de operatori: aritmetici, relaionali, logici i de aciune la nivel de bit. Pentru anumite sarcini, C are operatori suplimentari. Operatorul de atribuire Operatorul de atribuire poate fi folosit, n C, n cadrul oricrei expresii valide, lucru care nu este permis n majoritatea limbajelor de programare (inclusiv Pascal), care trateaz operatorul de atribuire ca pe un caz de instruciune special. Sintaxa de aplicare a operatorului de atribuire n C este: <Nume_variabil> = <Expresie>; unde <Expresie> poate fi o constant sau o construcie legal de complexitatea cerut n context. Atenie, se folosete = n loc de :=.

19

Membrul stng al atribuirii trebuie s fie o variabil sau un pointer, nu o funcie sau o constant. Conversii de tip la atribuire Cnd variabilele de un anumit tip sunt amestecate cu variabile de alt tip, au loc conversii de tip. ntr-o instruciune de atribuire, regula de conversie este simpl: valoarea din membrul drept (al expresiei) al instruciunii de atribuire este convertit la tipul din membrul stng. Pentru o mai bun nelegere a fenomenului, fie exemplul de mai jos. int nri; char car; float nrr; void functie(void) { car=nri; /* Linia 1 */ nri=nrr; /* Linia 2 */ nrr=car; /* Linia 3 */ nrr=nri; /* Linia 4 */ } n Linia 1, n variabila car ajung primii 8 bii cei mai puin semnificativi ai variabilei nri. Dac nri este cuprins ntre 0 i 255, car i nri vor avea valori identice .a.m.d.. Presupunnd c sistemul de calcul are cuvntul de 16 bii, Tabelul 5 prezint posibilele pierderi de informaie care pot s apar la conversia de tip n atribuiri. Tipul destinaiei signed char char char char int int float double Tipul expresiei char short int int long int long int float double long double Posibile pierderi de informaie Dac valoarea este > 127, destinaia este negativ. Cei mai semnificativi opt bii Cei mai semnificativi opt bii Cei mai semnificativi 24 bii Cei mai semnificativi 16 bii Partea zecimal i, posibil, mai mult Precizie, rezultat, rotunjit Precizie, rezultat, rotunjit Tabelul 5 Tipuri de conversie frecvent utilizate

Atribuiri multiple C permite atribuirea aceleeai valori mai multor variabile prin utilizarea atribuirii multiple ntr-o singur instruciune de atribuire. De exemplu instruciunea: x = y = z = 0;

20

permite setarea la 0 a variabilelor x,y,z. Programatorii profesioniti folosesc masiv atribuirea multipl pentru a obine cod performant. S mai adugm o observaie interesant pentru un programator profesionist. O atribuire de tipul: <Variabil>=<Variabil>+<Expresie> (1) este echivalent semantic cu sintaxa: <Variabil>+=<Expresie> (2) Dei s-ar putea s vin peste mn unor programatori, acetia trebuie s tie c pentru compilator sintaxa (2) este de preferat sintaxei (1) din punct de vedere al calitii codului generat. Aadar, x=x+1 este totuna cu: x+=1. Operatori aritmetici Tabelul 6 prezint operatorii aritmetici din C. Operator + * / % -++ Aciune Scdere, de asemenea i minus unar Adunare nmulire mprire Modul Decrementare Incrementare Tabelul 6 Operatori aritmetici n C

Dintre operatorii prezentai n Tabelul 6, operatorii ++ i -- sunt specifici limbajului C. Operatorul ++ adun 1 la variabila asociat. Operatorul -- scade 1 din variabila asociat. Astfel c: x=x+1; este sinonim cu: ++x; iar x=x-1; este sinonim cu: x--; Ambii operatori pot s fie plasai att nainte ct i dup variabila operat. Prin urmare: x=x+1; poate fi scris: ++x ; sau x++;
21

Exist, totui, o diferen ntre forma cu prefix i forma cu sufix, dac operatorii sunt utilizai ntr-o expresie. Atunci cnd operatorii preced variabila, C efectueaz operaiile corespunztoare lor, nainte de a evalua expresia. Atunci cnd operatorii succed variabila, C efectuiaz operaiile corespunztoare lor, dup evaluarea expresiei. Majoritatea compilatoarelor C/C++ produc rapid un cod obiect eficient pentru operaiile de incrementare i decrementare (cod mai bun dect cel obinut prin utilizarea atribuirii clasice echivalente). Pe acest considerent, recomandarea de a utiliza aceti operatori de cte ori este posibil este fireasc. n sfrit, ordinea de preceden a operatorilor aritmetici este: De ordinul cel mai nalt De ordinul cel mai cobort ++, -- (minus unar) *, / , % +, -

Operatori relaionali i logici Deoarece operatorii relaionali i logici lucreaz de multe ori mpreun, i prezentm mpreun. Semnificaia celor dou tipuri de operatori este cunoscut de la alte limbaje. Operatorii relaionali permit compararea (=punerea n relaie) a operanzilor. Rezultatele comparrii pot fi combinate dup rigorile algebrei Boole cu ajutorul operatorilor logici. Att operatorii relaionali ct i cei logici au o preceden mai sczut dect operatorii aritmetici. Deci, o expresie precum 8 > 1+5 este evaluat ca i cum ar fi fost scris 8 > (1+5). n Tabelul 7 prezentm opertorii relationali i logici folosii n C/C++. Operator > >= < <= == != Operator && || ! Operatori relaionali Aciune Mai mare dect Mai mare sau egal Mai mic dect Mai mic sau egal Egal Diferit Operatori logici Aciune AND (I) OR (SAU) NOT (NEGAT) Tabelul 7 Operatorii relaionali i logici n C.

Tabelul 8 prezint prioritile n relaia dintre operatorii relaionali i logici.

22

! >, >=, <, <= ==, != && || De ordinul cel mai cobort Tabelul 8 Relaia de preceden ntre operatorii relaionali i logici Evident, parantezele pot fi utilizate pentru a modifica ordinea fireasc de evaluare a unei expresii relaionale i/sau logice. De ordinul cel mai nalt Operatori de aciune pe bii Spre deosebire de multe alte limbaje, C admite un complement deplin cu operatorii de aciune pe bii. Deoarece C a fost proiectat s ia locul limbajului de asamblare pentru majoritatea sarcinilor, el trebuie s fie capabil s asigure multe operaii care pot fi executate n asamblor, inclusiv asupra biilor. Operaiile asupra biilor se refer la testare, iniializare sau deplasare a biilor existeni ntrun octet sau ntr-un cuvnt care corespund tipurilor de date char i int i variantelor acestora din standardul C. Operatorii de aciune asupra biilor nu pot fi utilizai asupra tipurilor float, double, long double,void. n Tabelul 9 prezentm operatorii C pentru bii. Operator & | ^ ~ >> << Aciune AND OR OR exclusiv (XOR) Complement fa de 1 (NOT) Deplasare la dreapta Deplasare la stnga Tabelul 9 Operatorii de aciune pentru bii

nelegerea transformrilor efective care au loc la utilizarea operatorilor AND, OR i XOR este legat de analiza n spiritul legilor logicii Boole a unei expresii de tipul: <Operand_1> <Operator><Operand_2> Aciunea operatorului este aplicat bit cu bit operanzilor, rezultnd o configuraie de bii asupra crora se pot continua alte prelucrri. Operatorii de deplasare a biilor >> i << deplaseaz toi biii dintr-o variabil la dreapta sau la stnga. Sintaxa asociat este: <Variabil> >> <Numr_de_poziii_de_shiftat> sau <Variabil> << <Numr_de_poziii_de_shiftat>

23

Sensul operaiei de shiftare este cel precizat la un curs de Bazele logice ale sistemelor de calcul. Precizri referitoare la operatorii suplimentari pot fi gsite n [1]. Operatorul ? C propune un operator unar foarte puternic i util care poate nlocui anumite instruciuni de forma dac atunci altfel. Operatorul ? apare n expresii avnd forma general: <Expresie_1> ? <Expreasie_2> : < Expresie_3> unde: -<Expresie_1>, <Expresie_2>, <Expresie_3> sunt expresii, cu precizarea c <Expresie_1> este o expresie condiional; -semantica construciei sintactice de mai sus este urmtoarea: z Se evaluiaz <Expresie_1>; z Dac <Expresie_1> este adevrat se evaluiaz <Expresie_2> i rezultatul evalurii ei i se atribuie expresiei globale; z Dac <Expresie_1> este fals. atunci se evaluiaz <Expresie_3> i rezultatul evaluirii acesteia este atribuit expresiei globale. Aceast semantic este evideniat i de exemplul de mai jos de utilizare a operatorului ?. X=24; X=X<24 ? 1:X+1; Acest cod este echivalent cu codul: X=24; if (X<24) X=1 else X=X+1; Operatorii & i * Un pointer este adresa din memorie a unei variabile. O variabil de tip pointer este declarat explicit pentru a reine un pointer ctre un obiect de un tip specificat. Cunoaterea adresei unei variabile poate fi de mare utilitate n anumite situaii. n C pointerii au trei funcii principale. z asigur o cale rapid de acces la elementele unei matrici; z permit funciilor C s modifice parametrii de apelare; z permit realizarea structurilor dinamice de date. Pentru simplificarea lucrului cu pointeri au fost introdui operatorii & i *. Operatorul & este un operator unar care permite recuperarea adresei din memorie a unui element C precum i declararea de variabile sinonime.

24

O atribuire de tipul : adr=&Numar_Elemente; este util pentru a obine n variabila adr adresa din memorie a variabilei Numar_Elemente. Aceast adres este adresa locaiei din memorie ncepnd de la care se pstreaz coninutul variabilei Numar_Elemente. Al doilea operator pentru pointeri este * , oarecum complementar operatorului &. i operatorul * este unar, returnnd valoarea din variabila localizat la adresa specificat. Astfel , dup execuia secvenei: adr=&Numar_Elemente; nr=*adr ; variabila nr va conine aceeai valoare cu variabila Numar_Elemente, presupunnd c cele dou variabile sunt compatibile ca tip. Evident, exist, din punct de vedere al utilizatorului, primejdia de a utiliza confuz aceti doi operatori, din moment ce * mai nseamn i nmulire iar & mai nseamn i i logic la nivel de bii. Variabilele care pstreaz pointeri trebuie declarate ca atare. Sintaxa de declarare a unei variabile pointer este: <Tip de baz> * <Nume_Variabila>; Exemplu #include <stdio.h> void main(void) { int destinatar,sursa; int *m; sursa=10; m=&sursa; destinatar=*m; printf (%d, destinatar); } Operatorii & i destinatar . * sunt utilizai pentru a introduce valoarea 10 n variabila

Operatorul sizeof (cu aciune n timpul compilrii)

25

sizeof este un operator unar utilizat n timpul compilrii care returneaz lungimea n octei a variabilei sau a specificatorului de tip dintre parantezele asociate de sintax operatorului. Sintaxa de utilizare a operatorului: sizeof ( <Nume_Variabila>) sau sizeof (<Specificator_ de_ tip>)

De remarcat faptul c n cazul unei variabile este permis i sintaxa: sizeof <Nume_Variabila> Acest operator este util n procesul de realizare a aplicaiilor portabile. Operatorii . (punct) i -> (sgeat) Sunt utilizai pentru a realiza referirea la elementele individuale ale structurilor i uniunilor din C. Aa cum se va vedea, structurile i uniunile sunt tipuri de date agregate la care se poate avea acces sub un singur nume. Operatorul punct este utilizat atunci cnd se lucreaz cu structuri sau uniuni efective. Operatorul sgeat este folosit mpreun cu un pointer la o structur sau la o uniune. Astfel, anticipnd puin, dac avem codul: struct tangajat { char nume[80]; int varsta; float salariu; } angajat; struct tangajat *pangajat=&angajat; are sens urmtorul cod: angajat . salariu:=1100000; Aceast atribuire este echivalent cu atribuirea: pangajat->salariu=1100000; De remarcat faptul c n C exist multe alte faciliti puse la dispoziia programatorilor, precum: conversia automat n expresii, forarea tipurilor expresiilor utiliznd modelatorii, utilizarea expresiilor prescurtate.

2.11 Comentarea programelor C/C++


26

Un comentariu este o not explicativ pentru programatori, ignorat complet de compilator n C comentariile sunt delimitate de perechile de caractere /* */. Compilatorul C ignor orice text aflat ntre aceti delimitatori. Exemple de comentarii: /* sau clrscr() /* Stergere ecran n mod text */ Acest tip de comentariu nu poate fi imbricat. Compilatoarele C++ accept i comentarii care ncep cu secvena //, dar se pot ntinde pe o singur linie, astfel: clrscr() // Stergere ecran n mod text. Un comentariu se poate ntinde pe mai multe linii dac aceasta este dorina programatorului*/

27

3 Reprezentarea structurilor de prelucrare n C/C++


Limbajul C nu propune o revoluie de fond n ceea ce privete problema reprezentrii structurilor de prelucrare. Vine, ns, cu o serie de inovaii interesante, n ceea ce privete flexibilitatea i varietatea propunerilor de reprezentare. Nu spunem nici o noutate amintind c reprezentarea structurilor de prelucrare se bazeaz pe combinarea, dac se poate, profesional, a instruciunilor executabile ale limbajului. Potrivit standardelor ANSI C i ANSI C++ instruciunile sunt mprite n urmtoarele grupe: Instruciuni de selecie Instruciuni iterative Instruciuni de salt Instruciuni de etichetare Instruciuni bloc Instruciunile de selecie cuprind enunurile if i switch. n categoria instruciunilor iterative intr enunurile while, for i do-while. Instruciunile de salt desemneaz n C instruciunile break, continue, goto i return. Instruciunile de etichetare includ enunurile case i default (implicate, esenial, n sintaxa i semantica instruciunii switch i etichetele (discutate relativ la instruciunea goto. Problematica instruciunilor expresie am discutat-o, pe larg, n seciunea precedent. De asemenea, blocul de cod este, deja, o noiune cu care ne-am familiarizat. De altfel, aa cum n Pascal, secvena de cod cuprins ntre begin i end era numit i instruciune compus, standardul ANSI C++ propune, aceeai denumire, ca denumire alternativ pentru blocul de cod. S atragem atenia asupra faptului c C++ adaug elemente sintactice specifice pentru tratarea excepiilor de ctre programator. Nu toate compilatoarele implementeaz, ns, acest suport important pentru a realiza aplicaii robuste i fiabile n C++.

3.1 Ce se nelege prin adevrat i fals n C?


Mai multe instruciuni din C se bazeaz pe expresii condiionale pentru a reprezenta un anumit tip de prelucrare. La fel ca n Pascal, sintaxa acestor instruciuni C face supoziia c alegerea unei traiectorii de prelucrare depinde de rezultatul evalurii acestor expresii condiionale. Teoretic, valorile admisibile pentru o expresie condiional sunt adevrat i fals. Punctul de vedere al limbajului C cu privire la adevr i fals este urmtorul: Un rezultat al evalurii egal cu zero este interpretat ca fals. Un rezultat nenul al evalurii este interpretat ca adevrat. Aceast schimbare de optic n ceea ce privete adevrul i falsul este de bun augur pentru scrierea de cod flexibil i eficient.  Propunerea de standard ANSI C++ definete un tip de dat boolean numit bool (care poate s aib doar valorile adevrat i fals. Ceea ce nu nseamn, ns, c nu se pstreaz punctul de vedere al limbajui C relativ la adevr i fals.

28

3.2

Instruciuni de selecie n C

C admite dou tipuri de instruciuni de selecie: if i switch. if Sintaxa general a instruciunii if este urmtoarea: if (<Expresie>) <Instruciune 1>; else <Instruciune 2>; <Instruciune 1> i <Instruciune 2> desemneaz o singur instruciune, un bloc de instruciuni sau nici o instruciune. Clauza else este opional. Dac <Expresie> este evaluat ca adevrat (adic rezultatul evalurii este orice valoare diferit de zero), atunci este executat <Instruciune 1>; altfel, se execut <Instruciune 2>. Evident, nu este obligatoriu s fie prezent clauza else. <Expresie> trebuie s returneze o valoare scalar (un ntreg, un caracter, un pointer sau un numr real n virgul mobil). Un numr real n virgul mobil se utilizeaz cu reinere pentru a controla o instruciune de selecie deoarece ncetinete semnificativ execuia programului. Aceasta deoarece sunt necesare mai multe instruciuni pentru a efectua o operaie n virgul mobil dect pentru a executa operaii la nivel de caracter sau cu ntregi. De semnalat faptul c, n situaia n care una din instruciunile asociate condiiilor true sau false ale unui if este bloc de instruciuni atunci sintaxa corect este: if (<Expresie>) {} else {}; Modul de utilizare a instruciunii if poate fi urmrit i n exemplul de mai jos. #include <stdio.h> #include <stdlib.h> #include <conio.h> void main() { int magic; /* Numar generat aleator */ int ghicit; /* Numar banuit */ magic=rand(); clrscr(); printf("Ghiceste numarul generat aleator:\n"); printf("Numarul generat aleator este cuprins intre 0 si %d",RAND_MAX); scanf("%d",&ghicit); if (ghicit==magic) printf("Ati nimerit numarul generat!!!"); else printf("Nu ati nimerit numarul generat!!!"); }
29

Instruciunea if poate fi imbricat. n caz de imbricare, clauza else se refer ntotdeauna la cea mai apropiat instruciune if situat n amonte, n acelai bloc cu else i neasociat nc cu un else, ca n exemplul: #include <stdio.h> #include <conio.h> #include <string.h>; void main() { float A,B,X; clrscr(); gotoxy(20,12); printf("____________________________________"); gotoxy(20,13); printf(" Rezolvarea unei ecuatii de gradul I"); gotoxy(20,14); printf(" A="); gotoxy(20,15); printf(" B="); gotoxy(20,17); printf("____________________________________"); gotoxy(20+strlen(" A="),14); scanf("%f",&A); gotoxy(20+strlen(" B="),15); scanf("%f",&B); gotoxy(20,17); printf("_____________________________________"); if (A==0) if (B==0) { gotoxy(20,16); printf("Ecuatie nedetrminata!!!"); getch(); } else { gotoxy(20,16); printf("Ecuatie imposibila!!!"); getch(); } else { X=B/A; gotoxy(20,16); printf("Solutia ecuatiei este:%f",X); getch(); } }
30

Destul de frecvent instruciunea if este utilizat i n construcia numit scara if-else-if avnd sintaxa general: if (<Expresie_1>) <Instruciune_1>; else if (<Expresie_2>) <Instruciune_2>; else if (<Expresie_3>) <Instruciune_3>; : : else if (<Expresie_k>) <Instruciune_k>; : Reamintim c, n anumite situaii, putem utiliza operatorul ? pentru a nlocui instruciunea if-else de forma: if (<Expresie>) <Expresie_1>; else <Expresie_2>; cu o construcie de forma: <Expresie>?<Expresi_1>:<Expresie_2>; switch C permite reprezentarea structurilor alternative cu mai multe ramuri utiliznd n acest scop instruciune switch avnd sintaxa: switch (<Expresie>) { case <Constanta_1>: <Secvena_de_instruciuni_1> break; case <Constanta_2>: <Secvena_de_instruciuni_2> break; : case <Constanta_k>: <Secvena_de_instruciuni_k> break; default <secvena_de_instruciuni> }

31

Semantica instruciunii este urmtoarea: se compar valoarea expresiei <Expresie> cu valorile constantelor specificate n instruciunile case. Cnd se ntlnete o coinciden, se execut secvena de instruciuni asociat acelui case pn la instruciunea break sau pn cnd se ajunge la finalul instruciunii switch. Instruciunea default se execut dac nu este ntlnit nici o coinciden. Clauza default este opional i dac nu este prezent, atunci cnd nu avem nici o coinciden nu se execut nici o aciune. Standardul ANSI C stipuleaz c switch poate s aib cel mult 257 de clauze case. Standardul propus de ANSI C++ recomand s se poat introduce cel mult 16.384 clauze case. n practic, din motive de eficien, se urmrete limitarea numrului de clauze case ntr-o instruciune switch. Instruciunea case nu poate fi utilizat dect n contextul instruciunii switch. Instruciunea break este o instruciune de salt n C, fiind utilizat pentru a determina ieirea forat din anumite tipuri de structuri (switch, for, do-while). Ilustrm modul de utilizare a instruciunii switch prin exemplul de mai jos. #include <stdio.h> #include <stdlib.h> #include <conio.h> void main() { int x; char Ras; clrscr(); x=random(2); switch(x+2){ case 3: { gotoxy(20,10); printf("Switch atins..."); break; } default: { gotoxy(20,10); printf("Switch ocolit..."); } } Ras=getch(); } Evident i instruciunea switch poate fi imbricat. De asemenea, facem urmtoarele precizri relativ la instruciunea switch: -n acelai switch nu pot exista dou constante case cu valori identice. Dou instruciuni switch, imbricate, pot s aib aceeai constant case.

32

-Dac n instruciunea switch sunt utilizate constante de tip caracter, ele sunt automat convertite n ntregi.

3.3 Instruciuni iterative


n C, ca i n alte limbaje de programare evoluate, exist enunuri de limbaj pentru a reprezenta: structuri repetitive cu numr cunoscut de pai (for); structuri repetitive cu numr necunoscut de pai, anterior condiionate (while); structuri repetitive cu numr necunoscut de pai, posterior condiionate (do-while); Structurile repetitive se mai numesc i bucle. Bucla for (pentru structuri repetitive cu numr cunoscut de pai) Conceptul general de <bucl for> se reflect ntr-o form sau alta n orice limbaj de programare de nivel nalt. n C, acest concept are o implementare de o flexibilitate i o putere neateptate. Sintaxa general a instruciunii for este: for (<Iniializare>;<Condiie>;<Increment>) <Instruciune>; O astfel de sintax permite multe variante constructive ale buclei for. n general vorbind, ns: -<Iniializare> este o instruciune de atribuire utilizat pentru a iniializa variabila de control a buclei; -<Condiie> este o expresie relaional care determin condiia de ieire din bucl; -<Increment> definete modul n care se modific variabila de control a buclei de fiecare dat cnd aceasta se repet. De remarcat faptul c cele trei seciuni trebuie separate prin punct i virgul. Aadar, bucla for se execut ct timp <Condiie> este adevrat. Dac <Condiie> devine fals, atunci execuia programului continu cu instruciunea care urmeaz construciei for, dac aceasta exist. Maniera clasic de utilizare a buclei for n C, o prezentm n exemplul de mai jos. #include <stdio.h> #include <iostream.h> #include <conio.h> long int fact( int n); void main() { clrscr(); gotoxy(20,12); int nr; cout<<"Introduceti numarul:"; cin>>nr;

33

gotoxy(20,14); cout<<"Factorial("<<nr<<")="<<fact(nr); getch(); } long int fact(int n) { long int f; int i; f=1; /*------------------------------------------------------------------/* Bucla for clasic cu o singur variabil de control /*------------------------------------------------------------------for (i=2;i<=n;i++) f=f*i; return f; } Exemplul de cod C prezentat mai sus permite calculul factorialului pentru un numr natural dat. Exemplul arat, anticipnd, modul de utilizare a conceptului de funcie n C++. Cteva variaiuni pe tema buclei for Sintaxa C permite variante dintre cele mai neatepate de definire a unor bucle for. Rigiditatea buclei for din Pascal este nlocuit n C cu o sintax care eman mai mult putere, flexibilitate i aplicabilitate n situaii specifice de programare. Una dintre cele mai folosite variaiuni folosete operatorul virgul, pentru a permite ca bucla s fie controlat de dou sau mai multe variabile. De exemplu, variabilele x i y controleaz urmtoarea bucl i amndou sunt iniializate n interiorul instruciunii for. : for (x=0,y=0;x+y<10;++x) { y+=1; //Aceast scriere reste echivalent cu y=y+1; printf(Y= %d,y); } : Prezentm, mai jos, un exemplu practic de utilizare a unei bucle for controlat de dou variabile: afiarea unui ir de caractere ncepnd de la ambele capete, mergnd ctre mijlocul irului. Se observ prezena fiierului header <dos.h> n care se afl prototipul funciei delay(). #include <conio.h> #include <stdio.h> #include <string.h> #include <dos.h>; //Semnatura functie converg

34

void converg(int linie, char *mesaj); //Functia principala void main() { clrscr(); gotoxy(20,12); converg(12,"Acesta este un test pentru functia converg()."); } //Implementare functie converg void converg(int linie, char *mesaj) { int i,j,ls,cs; ls=(80-strlen(mesaj))/2; cs=ls+strlen(mesaj)-1; for (i=ls,j=cs;i<=j;i++,j--) { gotoxy(i,linie);printf("%c",mesaj[i-ls]); delay(50); //prototipul in fisierul antet <dos.h> gotoxy(j,linie);printf("%c",mesaj[strlen(mesaj)-1-cs+j]); } getch(); } Ca un caz particular prezentm i sintaxa pentru o bucl infinit cu for for ( ; ; ) Bucla while (pentru structuri repetitive cu numr necunoscut de pai condiionate anterior) A doua bucl disponibil n C este bucla while. Forma sa general este: while (<Condiie>) <Instruciune>; unde <Instruciune> este o instruciune vid, o instruciune sau un bloc de instruciuni.. Condiia poate s fie orice expresie, fiind adevrat pentru orice valoare nenul. Bucla se reia ct timp condiia este adevrat. Cnd condiia devine fals, controlul programului trece la linia de cod urmtoare buclei, dac aceasta exist. Prezentm un exemplu de cod n care bucla while este utilizat pentru a simula, eventual, execuia unei secvene de instruciuni pn cnd dorete liberul arbitru al utilizatorului programului.

35

#include <stdio.h> #include <conio.h> #include <ctype.h> //Contine prototipul functiei toupper void main() { int sw; char ras; sw=1; /* Bucla while n aciune while(sw) { gotoxy(20,12); printf("Continuam(D,N):"); ras=getch(); if (toupper(ras)=='N') sw=0; } } Bucla do-while (repetitive cu numr necunoscut de pai condiionate posterior) Spre deosebire de buclele for i while, care testeaz condiia din bucl la nceputul execuiei lor, bucla do-while o verific la sfrit. Aceasta nseamn c bucla do-while se execut cel puin odat. Forma sa general este: do { <Instruciune>; } while <Condiie>; Bucla do-while se repet pn cnd <Condiie> devine fals. Dm, mai jos, un exemplu practic de utilizare a buclei do-while (afiarea/ selectarea opiunilor unui program C).
:

char prelopt() { int c; clrscr(); gotoxy(20,9); printf( "Optiunile programului..."); gotoxy(20,11); printf("1-Preluare elemente vector"); gotoxy(20,12); printf("2-Determinare suma"); gotoxy(20,13); printf("3-Terminare program");

36

gotoxy(20,15); printf("Optiunea Dvs.:"); /* Utilizare bucl do-while do c=getch(); while ((c!='1')&&(c!='2')&&(c!='3')); return c; }; : Alte instruciuni C Aa cum rezult i din exemplele prezentate, n C mai sunt intens folosite urmtoarele instruciuni: return, exit, break , continue i goto. Instruciunea return este utilizat pentru ntoarcerea dintr-o funcie. Este considerat ca instruciune de salt deoarece determin execuia programului s revin la prima instruciune dup funcia apelat n care apare return. Forma general a instruciunii return este: return [<Expresie>] Aceast sintax arat c dac <Expresie> este prezent atunci rezultatul evalurii expresiei este valoarea returnat de funcie. O funcie void nu trebuie s returneze nimic, deci return poate s apar fr <Expresie>. ntr-o funcie return poate s apar de cte ori este necesar. n C++ o funcie care nu este void trebuie s returneze o valoare. Funcia exit() Este utilizat pentru ieirea imediat dintr-un program, returnnd, eventual un cod de retur. Sintaxa de apel este: exit (<Cod_de_retur>); <Cod de retur> poate fi interpretat de procesul care a apelat programul, de regul, sistemul de operare. Exit acioneaz ca un break generalizat. Instruciunea break Are dou utilizri. Poate fi folosit, dup cum am vzut deja pentru a ncheia un case dintr-o instruciune switch, sau pentru a determina ncheierea imediat a unei bucle. Instruciunea continue Foreaz trecerea la urmtoarea iteraie a unei bucle, determinnd ignorarea restului codului iteraiei n care se afl. Nu facem meniuni speciale referitor la instruciunea goto.

37

38

4 Operaii I/O relativ la perifericele standard


Ne propunem n acest modul s prezentm funciile C care fac parte din sistemul I/O referitor la efectuarea operaiilor de introducere a datelor de la tastatur i afiare a acestora pe ecranul monitorului.

4.1 Operaii la nivel de caracter


Cele mai simple funcii C pentru lucrul la nivel de caracter relativ la perifericele standard au urmtoarele prototipuri: int getch (void); int getche (void); int putchar (int car); { Fiierul antet depozitar :conio.h} { Fiierul antet depozitar :conio.h} {Fiierul antet depozitar :stdio.h}

Funcia getch() este utilizat pentru a citi un caracter de la tastatur fr ecou pe ecranul monitorului , analog mecanismului readkey din Pascal. Funcia getche() este utilizat pentru a citi un caracter de la tastatur cu ecou pe ecranul monitorului. Funcia putchar() se folosete pentru a scrie un caracter pe ecranul monitorului n poziia curent a cursorului. Dup cum sugereaz prototipul, getch() returneaz un ntreg. Totui, funcia poate returna aceast valoare unei variabile de tip caracter deoarece caracterul este coninut n octetul de ordin inferior. Aceeai observaie este valabil i pentru funcia getche(). Dei din alt punct de vedere, i funcia putchar() poate fi apelat cu argument de tip caracter deoarece ieirea pe ecran se refer, oricum, doar la octetul de ordin inferior. n cazul apariiei unei erori, putchar() returneaz EOF. Funcia macro EOF, este definit n stdio.h i, n general este egal cu 1. Exemplul pe care l prezentm permite citirea de caractere de la tastatur i, dac sunt litere mici, convertirea lor la litere mari. Citirea se termin n momentul n care se tasteaz caracterul ESC al crui cod backslash este \033. #include <stdio.h> #include <conio.h> #include <ctype.h> void main() { char car; do { car=getche(); car=toupper(car); putchar(car);

39

} while (car!='\033'); } Prototipul funciei toupper() se afl (aa cum am mai spus-o) n fiierul antet ctype.h. Se observ c citirea se face cu ecou pe ecranul monitorului. Este cazul s spunem c programatorul n C/C++ are de fcut o serie de descoperiri folositoare n ceea ce privete oferta numeroaselor fiiere antet livrate odat cu compilatorul. Prezentarea tuturor acestor funcii, fie i numai prina antet, nu este de loc o treab uoar, din punct de vedere al volumului. De aceea. n acest suport de curs adresm cititorului invitaia de a descoperi, cu ajutorul help-ului on line i a unor cri care nu fac economie de spaiu, potenialul structurat n fiierele antet.

4.2 Operaii la nivel de ir de caractere


Pentru operaii la nivel de ir de caractere C pune la dispoziie funciile avnd urmtoarele prototipuri: char *gets(char *sir); int puts(const char *sir); Funcia gets() citete un ir de caractere de la tastatur i l plaseaz la adresa indicat de argumentul su. Funcia puts() scrie pe ecran argumentul su, urmat de o linie nou. Prototipurile acestor dou funcii se gsesc n stdio.h. Exemplul care urmeaz arat modul de utilizare al funciilor gets() i puts() cnd argumentul lui gets() este o variabil obinuit. #include <stdio.h> #include <conio.h> void main() { char s[30]; clrscr(); gotoxy(20,10); printf("Introduceti un sir de caractere:"); gets(s); gotoxy(20,11); printf("Sirul introdus :"); puts(s); getch(); clrscr(); } n exemplul de mai jos se arat modul de utilizare al funciilor gets() i puts() cnd argumentul lui gets() este o variabil pointer la un ir de caractere.

40

#include <stdio.h> #include <conio.h> void main() { char s; char *p; clrscr(); gotoxy(20,10); p=&s printf("Introduceti un sir de caractere:"); gets(p); gotoxy(20,11); printf("Sirul introdus :"); puts(p); getch(); clrscr(); } Operaii I/O cu iruri de caractere se pot efectua i cu funciile scanf() i printf(), mult mai versatile dect gets() i puts() , dup cum vom vedea n continuare.

4.3 Funcii I/O relativ la consol formatate


Funciile printf() i scanf() efectueaz operaii I/O formatate, adic pot scrie i, respectiv, citi date n diverse formate specificate de programator. Funcia printf() afieaz date formatate la consol. Funcia scanf(), complementul funciei printf() citete date formatate de la tastatur. Ambele funcii pot lucra cu toate tipurile de date existente n C. Prototipurile celor dou funcii sunt: int scanf( const char *format [, address, ...]); {Fiierul antet gazd :stdio.h} int printf(const char *format [, argument, ..]); {Fiierul antet gazd :stdio.h}

printf()
Returneaz, cu destinaia ecran, numrul de caractere scrise sau, dac apare o eroare, o valoare negativ. Parametrul format se compune din dou tipuri de simboluri. Primul tip l formeaz caracterele care vor fi afiate pe ecran. Al doilea tip se refer la specificatorii de format cu ajutorul crora se stabilete modul n care sunt afiate argumentele care urmeaz. Un specificator de format ncepe cu un semn % i este urmat de un cod de format. Trebuie s existe acelai numr de argumente ca i acela al specificatorilor de format i, totodat, specificatorii de format i argumentele se asociaz n ordinea de la stnga la dreapta.

41

Codurile de format disponibile pentru printf() sunt: Cod %c %d %I %e %E %f %g %G %o %s %u %x %X %p %n %% Format Caracter Numere ntregi n baza 10, cu semn Numere ntregi n baza 10, cu semn Notaie tiinific (cu litera e) Notaie tiinific (cu litera E) Numr zecimal n virgul mobil Folosete %e sau %f , anume, care din ele este mai mic Folosete %E sau %f , anume, care din ele este mai mic Numr n octal, fr semn ir de caractere Numere ntregi zecimale fr semn Numere hexazecimale fr semn (cu litere mici) Numere hexazecimale, fr semn, cu litere mari Afieaz un pointer Argumentul asociat este un pointer de tip ntreg n care a fost plasat numrul de caractere scrise pn atunci. Afieaz un semn %

Pentru a nelege modul deosebit n care acioneaz codul %n urmrii exemplul de mai jos. #include <stdio.h> #include <conio.h> void main() { int numara; printf("Acesta%n este un test...\n",&numara); printf("%d",numara); getch(); } De remarcat faptul c specificatorii de format accept modelatori de format care modific uor semnificia lor. De exemplu, se poate specifica un minim de caractere permise la afiare, numrul de cifre zecimale i alinierea la stnga. Modelatorul de format se afl ntre semnul % i codul pentru format. Codul de mai jos poate forma o idee asupra utilitii modelatorilor de format. #include <stdio.h> #include <conio.h> void main() { int numara;
42

clrscr(); printf("Acesta%n este un test...\n",&numara); printf("Aliniere la dreapta.......\n"); printf("%10d\n",numara); printf("Aliniere la stanga .......\n"); printf("%-d\n",numara); printf("Completare cu zerouri.....\n"); printf("%010d\n",numara); getch(); }

scanf()
Este o rutin de uz general pentru intrri de la consol. Ea poate s citeasc toate tipurile de date ncorporate i s fac automat conversia numerelor n format intern corect. Se aseamn mult cu complementara ei printf(). Ca funcie, scanf() returneaz numrul de elemente crora li s-a atribuit cu succes o valoare. Dac apare o eroare scanf() returneaz EOF. Argumentul format determin modul n care vor fi citite valorile n variabilele din lista de argumente. Codurile de format disponibile pentru scanf() sunt prezentate n tabelul de mai jos. Cod %c %d %I %e %E %f %g %o %s %u %x %p %n %u %[] Format Citete un singur caracter Citete un numr ntreg n baza 10 Citete un numr ntreg n baza 10 Citete un numr n virgul mobil Citete un numr n virgul mobil Citete un numr n virgul mobil Citete un numr n virgul mobil Citete un numr n octal Citete un ir de caractere Numere ntregi zecimale fr semn Citete un numr n hexazecimal Citete un pointer Argumentul asociat este un pointer de tip ntreg n care a fost plasat numrul de caractere citite pn atunci. Citete un ntreg fr semn Caut un set de caractere Tabelul 10. O parte din codurile pentru formatarea operaiilor I/I relativ la periferice standard

Primul exemplu de cod prezentat mai jos ilustreaz ideea de scanset posibil la utilizarea funciei scanf(). Definirea unui scanset nseamn, de fapt c citirea corespunztoare unei variabile este validat ct timp caracterele citite se potrivesc celor definite n scanset.

43

Al doilea exemplu este conceput n ideea c valoarea variabilei n2 va fi citit numai dup introducerea de la tastatur a dou caractere virgul. Acest mecanism permite, printre altele, definirea ca separator la introducerea datelor a altui caracter dect <CR>. #include <stdio.h> #include <conio.h> void main() { char sir[3]; clrscr(); scanf("%[TOC]",&sir); //Citire cu scanset printf("\n%s",sir); getch(); } #include <stdio.h> #include <conio.h> void main() { int n1,n2; clrscr(); scanf("%d,,%d",&n1,&n2); printf("%d\n",n1); printf("%d\n",n2); getch(); } Atenie la necesitatea ca variabila n care se citete s fie un pointer! Alte faciliti pentru lucrul cu consola n categoria alte faciliti pentru lucrul cu consola includem, cu prioritate, funciile clrscr(), clreol(), gotoxy(col,lin) ale cror valori de ntrebuinare sunt identice celor din Borland Pascal. Chiar i sintaxa funciei gotoxy(col,lin) este concordant n ceea ce privete ordinea parametrilor dup care se face poziionarea cursorului. Aadar: clrscr() se utilizeaz pentru teregerea ferestrei text curente; fereastra text curent implicit depinde de modul text n care se lucreaz. Caracteristicile modurilor text recunoscute de C sunt prezentate n tabelul de mai jos.

44

Constanta LASTMODE BW40 C40 BW80 C80 MONO C4350

Valoare -1 0 1 2 3 7 64

Mod Text Caracterisrici Precedentul mod text Black and white 40 coloane Color 40 coloane Black and white 80 coloane Color 80 coloane Monochrome 80 coloane EGA and VGA 50 linii Tabelul 11. Moduri video text standard

Aceste moduri text pot fi selectate cu ajutorul funciei textmode(), al crei antet se afl tot n conio.h i face parte tot din arsenalul C de lucru cu ecranul n mod text. Tot n mod text, putem controla culorile textului i ale fondului (backgroundul). Sintaxa funciilor cu care realizm controlul culorilor este: textcolor(<Culoare>); textbackground(<Culoare>); Parametrul <Culoare> poate lua una din valorile prezentate n tabelul de mai jos: Constanta Valoare Fond Text BLACK 0 Da Da BLUE 1 Da Da GREEN 2 Da Da CYAN 3 Da Da RED 4 Da Da MAGENTA 5 Da Da BROWN 6 Da Da LIGHTGRAY 7 Da Da DARKGRAY 8 Da Da LIGHTBLUE 9 Nu Da LIGHTGREEN 10 Nu Da LIGHTCYAN 11 Nu Da LIGHTRED 12 Nu Da LIGHTMAGENTA 13 Nu Da YYELLOW 14 Nu Da WHITE 15 Nu Da BLINK 128 Nu *** Tabelul 12. Valori admise pentru culoare text si fond. Moduri video text standard *** Codul BLINK se adaug la culoarea textului pentru a obine efectul de blinking.

45

clreol() se utilizeaz pentru tergerea liniei curente ncepnd cu coloana pe care se afl cursorul i pn la sfritul liniei. gotoxy(col,lin) permite poziionarea cursorului pe linia i coloana specificate. De asemenea, pot fi de interes, n anumite situaii, urmtoarele funcii: window(css,lss,cdj,ldj); Definete coordonatele ferestrei text active. wherex(); Returneaz coloana curent a cursorului. wherey(); Returneaz linia curent a cursorului. gettext() i puttext() Permit citirea/scrierea memoriei video n mod text, ca n exemplul de mai jos. #include <conio.h> //Buffer pentru salvat coninut memorie-video char buffer[4096]; int main(void) { int i; // Comutare n modul text C4350 textmode( C4350); clrscr(); for (i = 0; i <= 20; i++) cprintf("Linia %d\r\n", i); //Salvare coninut memorie video n variabila buffer gettext(1, 1, 80, 24, buffer); gotoxy(1, 25); cprintf("Apasati o tasta pentru a sterge ecranul..."); getch(); //tergere ecran clrscr(); gotoxy(1, 25); cprintf(" Apasati o tasta pentru a restaura ecranul..."); getch();

46

//Refacere coninut memorie video, utiliznd datele salvate n variabila buffer puttext(1, 1, 80, 24, buffer); gotoxy(1, 25); cprintf("Apasati o tasta pentru a termina..."); getch(); return 0; } Ca o aplicaie la cele prezentate pn n acest moment n legtur cu programarea n C, prezentm codul care ncapsuleaz cteva noi faciliti de lucru n mod text, sub forma unui fiier antet, care poate fi utilizat n orice program C, dac este prezent directiva de compilare: #Include facilcrt.h Numele fiierului antet este, evident, facilcrt.h. #include <conio.h> #include <stdio.h> #include <string.h> //Activare video-invers void avideo() { textcolor(BLACK); textbackground(WHITE); } //dezactivare video-invers void dvideo() { textcolor(WHITE); textbackground(BLACK); } //Afisare centrata text in interiorul unei linii void acentext(int ls,int ld,int linia,char *sir) { int sw; int col; sw=(ls>=1) && (ls<=79) && (ld<=80) && (ld>=2) && (ls<ld); sw=sw && ((ld-ls+1)>=strlen(sir)); if (sw) { col=ls+(ld-ls+1-strlen(sir))/2;

47

gotoxy(col,linia); cprintf(sir); } } //Construire fereastra cu rama dreptunghiulara de dimensiuni specificate void makewin2(int ass,int oss,int adj,int odj) { short int i; int sw; sw=(ass>0) && (ass<81) && (adj>0) && (adj<81) && (ass<=adj); sw=sw && (oss>0) && (oss<25) && (odj>0) && (odj<25) && (oss<=odj); if(sw) { for(i=ass;i<=adj;i++) { gotoxy(i,oss-1);cprintf("\315"); gotoxy(i,odj+1);cprintf("\315"); } for(i=oss;i<=odj;i++) { gotoxy(ass-1,i);cprintf("\272"); gotoxy(adj+1,i);cprintf("\272"); } gotoxy(ass-1,oss-1);cprintf("\311"); gotoxy(adj+1,oss-1);cprintf("\273"); gotoxy(ass-1,odj+1);cprintf("\310"); gotoxy(adj+1,odj+1);cprintf("\274"); } else { gotoxy(1,24); printf("Coordonate ecran eronate!!!"); getch(); } } Ca un comentariu la exemplul de cod C prezentat mai sus i la alte exemple prezentate deja, facem precizarea c directiva de includere #include este soluia C pentru o modularizare a codului, orientat pe clasificarea tipurilor de capabiliti de prelucrare n fiiere speciale numite fiiere antet. Fiierele antet sunt incluse n codurile surs ale programelor noastre de cte ori avem nevoie de o capabilitate al crei prototip se afl n acele fiiere antet.

48

5 Matrice i iruri
O matrice este o colecie de variabile de acelai tip, apelate cu acelai nume. Accesul la un anumit element al matricei se face cu ajutorul unui indice. n C toate matricile constau n locaii de memorie contigue. Cel mai mic indice corespunde primului element iar cel mai mare ultimului element. Matricele pot avea una sau mai multe dimensiuni. Ca i n alte limbaje, cea mai simpl matrice este irul. n C irul este o matrice de caractere terminate cu un caracter NULL. Aceast caracteristic ofer limbajului C mai mult putere i eficien dect posed alte limbaje. Semnalm, de asemenea, faptul c n C, exist o strns legtur ntre matrice i pointeri.

5.1 Matrice cu o singur dimensiune


Forma general n declararea unei matrice cu o singur dimensiune este: <Tip> <Nume_variabil>[<Dimensiune>]; <Tip> declar tipul de baz al matricei, care este tipul fiecrui element al su. <Dimensiune> indic numrul maximal de elemente pe care le poate conine matricea.  De reinut faptul c, n C, toate matricele au ca indice pentru primul element pe 0. Cantitatea de memorie necesar pentru nregistrarea unei matrice este direct proporional cu tipul i mrimea sa. Pentru o matrice unidimensional, mrimea total n octei este calculat astfel: Total_octei = sizeof(<Tip>)*<Dimensiune>  C nu controleaz limitele unei matrice. Putei depi ambele margini ale unei matrice i scrie n alte variabile sau peste codul programului. Riscurile i responsabilitile sunt de partea programatorilor.

5.2 Crearea unui pointer la o matrice


Un pointer la primul element al unei matrice se creeaz simplu, specificnd numele matricei, fr nici un indice. De exemplu, avnd : float sir[10]; putem crea un pointer la primul element al matricei astfel:

49

float *p; float sir[10]; p=sir;

5.3 iruri de caractere


De departe, cea mai utilizat matrice unidimensional este irul de caractere. Reamintim faptul c n C un ir de caractere este definit ca o matrice de caractere care se termin cu un caracter NULL. n convenie backslash un NULL se reprezint prin \0 i are valoarea 0. Din acest motiv, matricele de tip caracter se declar cu un caracter mai mult dect lungimea celui mai mare ir pe care l vor conine. Dei C nu are date de tip ir permite constante ir. O constant ir este o succesiune de caractere nchise ntre ghilimele. Nu este necesar s introducei manual caracterul NULL la sfritul constantelor ir, compilatorul face acest lucru automat. C admite, totodat, o gam larg de funcii de manipulare a irurilor. Cele mai des utilizate sunt prezentate n tabelul de mai jos. Nume funcie strcpy(s1,s2) strcat(s1,s2) strlen(s1) strcmp(s1,s2) Utilitate Copiaz s2 n s1 Concateneaz s2 la sfritul s1 Returneaz lungimea lui s1 Returneaz 0 dac s1 i s2 sunt identice; un numr mai mic dect 0 dac s1<s2 n sens lexicografic; un numr mai mare dect 0 dac s1>s2 n sens lexicografic. strchr(s1,ch) Returneaz un pointer la prima apariie a caracterului ch n s1. strstr(s1,s2) Returneaz un pointer la prima apariie a lui s2 n s1. Tabelul 13. Funcii C pentru lucrul cu iruri de caractere pstrate n fiierul antet string.h  Atenie! strcmp() returneaz fals dac irurile sunt egale.

5.4 Matrici bi i multidimensionale


C admite matrice multidimensionale. Cea mai simpl form de matrice multidimensional este cea cu dou dimensiuni. De fapt, o matrice bidimensional este o matrice de matrice unidimensionale. Pentru a declara o matrice bidimensional utilizm sintaxa: <Tip> <Nume_variabil>[<Dimensiune_1>][<Dimensiune_2>]; De exemplu, o matrice bidimensional de ntregi, de mrime10/20 se declar astfel: int matr[10][20];

50

Adresarea elementelor matricei se bazeaz pe premiza c, dup fiecare dimensiune, indexarea ncepe de la 0. De asemenea, facem precizarea c, atunci cnd o matrice bidimensional este utilizat ca un argument pentru o funcie, se transmite doar un pointer ctre primul element al matricei. ns, parametrul care primete o matrice bidimensional trebuie s defineasc cel puin numrul de coloane, necesare compilatorului pentru a indexa corect matricea. De urmrit exemplul de mai jos de cod C care permite calculul sumei elementelor strict pozitive de pe diagonala principala a unei matrice patratice . #include<stdio.h> #include<conio.h> // Declarare matrice bidimensionala int matr[10][10]; int dimm; //Functia care primeste ca argument o matrice . Declara numarul de coloane. void sespdp( int m[ ][10]) { int k,suma; suma=0; for (k=0;k<dimm;k++) { if (m[k][k]>0) suma=suma+m[k][k]; } clrscr(); gotoxy(20,12); cprintf("Suma elementelor strict pozitive ddp: %d",suma); getch(); } //Functia principala void main() { int i,j,el; clrscr(); gotoxy(20,10);cprintf("Nr. de linii :"); scanf("%d",&dimm); clrscr(); for(i=0;i<dimm;i++) for(j=0;j<dimm;j++) { gotoxy(20,12); cprintf("Elementul[ %d , %d ]=",i,j); scanf("%d",& matr[i][j]); }

51

//Apelare functie cu parametru matrice transmisa ca pointer static sespdp(matr); } Evident, n cazul unei matrice multidimensionale, forma general de declarare precum i modul de utilizare sunt deductibile din cazul bidimensional. Sintaxa pentru declararea unei matrice multidimensionale este: <Tip> <Nume_variabil>[<Dim_1>][<Dim_2>][Dim_n]; n ncheierea paragrafului referitor la matrice prezentm o aplicaie n care, artm, totodat, primii pai spre modularizarea i interfaarea unui program C. Este vorba de un program C care cerceteaz ortogonalitatea a doi vectori reali. Se utilizeaz nc odat fiierul antet facilcrt.h . #include<stdio.h> #include<conio.h> #include "facilcrt.h" #include<stdlib.h> //Directiva #define permite specificarea unor macrouri #define lmaxs 100 #define optiuni "1234" //Solutie modularizata C pentru problema determinrii //ortogonalitatii a doi vectori reali char prelopt(); void prelvec(); int ortogon(); float sir1[lmaxs],sir2[lmaxs]; int dims; void main() { for(; ;) { switch (prelopt()) { case '1': { prelvec(); break; } case '2': { ortogon(); break; }; case '3': exit(0); }

52

} } char prelopt() { char opt; clrscr(); gotoxy(20,9); cprintf("Optiunile programului...."); gotoxy(20,10); cprintf("1- Preluare vectori"); gotoxy(20,11); cprintf("2- Determinare ortogonalitate...."); gotoxy(20,12); cprintf("3- Terminare program"); makewin2(20,9,55,12); gotoxy(20,15); cprintf("Optiunea Dvs.:"); makewin2(20,15,55,15); do { gotoxy(20+strlen("Optiunea Dvs.:"),15); opt=getch(); } while (strchr(optiuni,opt)==NULL); return opt; } void prelvec() { int i,col; clrscr(); gotoxy(20,9); cprintf("Dimensiune vectori:"); makewin2(20,9,20+strlen("Dimensiune vectori:"),9); gotoxy(22+strlen("Dimensiune vectori:"),9); col=strlen("Dimensiune vectori:"); makewin2(22+col,9,30+col,9); gotoxy(23+col,9); cscanf("%d",&dims); clrscr(); gotoxy(20,9); cprintf("Preluarea componentelor primului vector..."); makewin2(20,9,20+strlen("Preluarea componentelor primului vector..."),12); for (i=0;i<dims;i++) {

53

gotoxy(20,10); cprintf("Elementul [ %d ]=",i); cscanf("%f",&sir1[i]); } clrscr(); gotoxy(20,9); cprintf("Preluarea componentelor celui de-al doilea vector..."); makewin2(20,9,20+strlen("Preluarea componentelor celui de-al doilea vector..."),12); for (i=0;i<dims;i++) { gotoxy(20,10); cprintf("Elementul [ %d ]=",i); cscanf("%f",&sir2[i]); } } int ortogon() { float suma; int i; suma=0; for (i=0;i<dims;i++) suma=suma+sir1[i]*sir2[i]; clrscr(); if (suma) { gotoxy(20,10); textcolor(RED+BLINK); cprintf("Vectorii nu sunt ortogonali!!"); makewin2(20,10,21+strlen("Vectorii nu sunt ortogonali!!"),10); getch(); textcolor(WHITE); } else { gotoxy(20,10); textcolor(GREEN+BLINK); cprintf("Vectorii sunt ortogonali!!"); makewin2(20,10,21+strlen("Vectorii sunt ortogonali!!"),10); getch(); textcolor(WHITE); }; }

54

Se cuvine s mai fac o parantez referitoare la insistena cu care apar n programele C/C++ construcii sintactice care ncep cu #, precum #include, #define. Aceste construcii fac parte din categoria instruciunilor care se adreseaz compilatorului i se mai numesc i directive pentru preprocesor. dei nu fac parte din limbajul C/C++ aceste directive lrgesc sfera de aciune a programelor C/C++. Am vzut deja care este utilitatea directivei preprocesor #include. S spunem, totodat, faptul c, directiva pentru preprocesor #define permite definirea unor macrouri astfel: #define <Nume_macrou> <Secven_de_caracter> Oriunde n program apare <Nume_macrou> compilatorul l nlocuiete cu <Secven_de_caractere>. Adic avem la dispoziie un instrument foarte util la parametrizarea codului. S adugm c #define poate permite i definirea de funcii macro, situaie n care numele macroului poate avea i argumente. Exemplu: #define ABS(a) (a)<0 ? (a) : (a) : printf(abs de 1 si 1%d %d,ABS(-1), ABS(1)); La compilarea acestei secvene, a care apare n definirea funciei macro va fi nlocuit cu valorile 1 i 1. Utilizarea unei funcii macro mrete viteza de execuie a codului. Merit cercetate cu atenie i trucurile care pot fi realizate cu ajutorul directivei #define. Semnalm c, aa cum se ntmpla i n Pascal, n C exist o serie de directive pentru compilare condiional a codului surs. Din aceast categorie fac parte #if, #endif, #else #ifdef, #ifndef, etc.

55

6 Pointeri
Un pointer este o variabil care poate conine o adres de memorie. Aceast adres este localizarea n memorie a unui alt obiect (de regul o alt variabil). De exemplu, dac o variabil conine adresa alteia, despre prima se spune c este un pointer la cea de-a doua. O variabil de tip pointer se declar n C astfel: <Tip> * <Nume>; unde <Tip> este tipul de baz al pointerului iar <Nume> este numele variabilei pointer. Tipul de baz al pointerului definete tipul de variabil ctre care indic pointerul. Practic, orice tip de pointer poate s indice orice n memorie. Problema este, ns, c aritmetica pointerilor este integral raportat la tipul de baz al pointerului. Operatori pentru pointeri Exist doi operatori speciali pentru pointeri: * i &. & este un operator unar care returneaz adresa din memorie a operandului su. Operatorul * returneaz valoarea nregistrat la adresa care l urmeaz. Evident c operatorul & se aplic oricrui tip de variabil dar operatorul * cere dup el, neaprat, o variabil pointer. Expresii cu pointeri n general, expresiile care implic pointeri se conformeaz acelorai reguli ca i celelalte expresii. Exist, totui, o serie de particulariti pe care le vom evidenia n acest paragraf n ceea ce privete aritmetica pointerilor. Astfel: Atribuirile sunt permise ntre pointeri concordani ca tip. Pointerilor li se poate aplica operatorul ++ (incrementare) ca mai jos : int *p; int mat[10]; : p=&mat[0]; : p++; // Operatorul de incrementare aplicat pointerului p p- -; : n urma aplicrii operatorului de incrementare pointerului p, care coninea adresa primului element al tabloului unidimensional mat, offset-ul acestuia este incrementat cu 2, adic att ct este lungimea n octei a tipului de baz al pointerului p. Pointerilor li se poate aplica operatorul - - (decrementare) . n urma aplicrii operatorului de decrementare pointerului p, care coninea adresa celui de-al doilea element al tabloului unidimensional mat, offset-ul acestuia este decrementat cu 2, adic att ct este lungimea n octei a tipului de baz al pointerului p.

56

De asemenea, putem aduna sau scdea ntregi la, sau din pointeri. De exemplu: : p=p+10; : face ca p s indice al 10-lea element de acelai tip cu tipul de baz al lui p, relativ la elementul curent. n exemplul de mai jos ilustrm utilitatea aritmeticii pointerilor n contextul lucrului cu matrice. #include<stdio.h> #include<conio.h> int matr[10][10]; //matrice de intregi bidimensionala int *p; // pointer la intregi int dimm; void main() { int i,j,el; clrscr(); gotoxy(20,10);cprintf("Dimensiune matrice :"); scanf("%d",&dimm); clrscr(); for(i=0;i<dimm;i++) for(j=0;j<dimm;j++) { gotoxy(20,12); cprintf("Elementul[ %d , %d ]=",i,j); scanf("%d",&el); matr[i][j]=el; }; p=&matr[0][0]; //p refera primul element al matricei for (i=0;i<dimm;i++) cprintf("\r\n Ref_poin=%d",*(p+i)); //modificare offset p in expresie getch(); p=&matr[0][0]; for (i=0;i<dimm;i++) { cprintf("\r\n Ref_poin=%d",*(p)); p=p+1; //modificare offset p prin incrementare } getch(); p=&matr[0][0]; for (i=0;i<dimm;i++) { cprintf("\r\n Ref_poin=%d",*(p)); p++;

//aplicare operator de incrementare

57

} getch(); } Pointerii pot fi comparati n cadrul expresiilor relaionale. Dai pointerii p i q, este perfect valabil instruciunea: if (p<q) cprintf (p indica o memorie de adresa mai mica decat q\n); Evident, n C se poate vorbi de matrice de pointeri, de indirectare multipl i, evident, de alocarea dinamic a memoriei aferente unor pointeri. Funcii de alocare dinamic n C Pointerii ofer suportul necesar pentru sistemul puternic de alocare dinamic a memoriei n C. Alocarea dinamic este caracteristica prin care un program poate obine memorie n timpul execuiei. Dup cum se tie, variabilelor globale li se aloc memorie n timpul compilrii. Variabilele locale folosesc memoria de tip stiv. n mod cert, nici variabilele globale nici cele locale nu pot fi adugate n timpul execuiei programului. Exist situaii n care necesarul real de memorie este cunoscut de-abia n timpul execuiei programului.  Chiar dac C++ accept pe deplin sistemul de alocare dinamic al lui C, el i definete propriul sistem, care conine mai multe mbuntiri fa de cele din C. Memoria alocat de funciile de alocare dinamic din C este obinut din HEAPzona de memorie liber situat ntre zona permanent a memoriei programului i stiv. Nucleul sistemului de alocare din C const din funciile malloc() i free(). Aceste instrumente de alocare lucreaz n pereche, folosind zona de memorie liber pentru a stabili i a pstra o list cu memoria disponibil. Funcia malloc() aloc memorie, avnd urmtorul prototip: void *malloc(size_t numar_de_octei); n acest prototip, numar_de_octei este numrul de octei din memorie pe care dorim s-l alocm programului. Funcia malloc() returneaz un pointer de tipul void, ceea ce nseamn c l putei atribui oricrui tip de pointer. Prototipul funciei free este: void free(void *p); Funcia free returneaz n sistem memoria alocat anterior pointerului p. Este esenial s nu se apeleze free cu un argument impropriu; deoarece acest fapt poate aduce prejudicii gestiunii de ctre sistem a memoriei libere. Exemplul de mai jos arat modul efectiv de utilizare a funciilor malloc i free, precum i
58

modul de utilizare a funciei memcpy, n situaia n care se dorete copierea coninutului memoriei de la o anumit adres la alt adres. De remarcat, totodat, faptul c bibliotecile C au numeroase alte funcii pentru a satisface cerinele de alocare/ manipulare dinamic a memoriei n programele utilizator. #include<stdio.h> #include<conio.h> #include<stdlib.h> #include<string.h> void main() { char c[20]; int *p; //pointer generic void *x; int nr=10000; p=&nr; clrscr(); gotoxy(20,10); //Alocare dinamica memorie incepand //de la o adresa returnata in x x=(int*) malloc(sizeof(int)); //Copiere 2 octeti din zona referita de p in zona referita de x memcpy(x,p,sizeof(int)); printf("%d",*(int*)x); getch(); //Utilizarea pointerului x pentru a referi un sir de caractere gotoxy(20,11); strcpy(c,"Sir de caractere"); x=(char*) malloc(21); //alocare cu conversie de tip memcpy(x,c,21); puts((char*)x); getch(); free(x); } Programatorul Pascal recunoate n suportul oferit de malloc() i free() soluia pentru problema alocrii de memorie la nivel de octet, ceea ce nseamn responsabilitatea programatorului de a realiza conversiile necesare n procesul de utilizare a memoriei astfel alocate, ca n programul de mai sus. C++ are propria ofert pentru alocarea dinamic de memorie din perspectiv structurat.

59

7 Structuri
O structur este, pragmatic vorbind, un grup de variabile reunite sub acelai nume, ceea ce permite un mod convenabil de manipulare a unor date care au afiniti semantice ntre ele. O declarare de structur formeaz un ablon care poate fi folosit pentru a crea structuri efective. n C, variabilele care fac parte din structur se numesc membri ai structurii. Uzual, membrii structurii se mai numesc i elemente sau cmpuri. Aa cum vom vedea mai jos, o structur poate ncapsula i metode de prelucrare a datelor, dac programatorul dorete s abstractizeze tipuri de date cu suport struct. Sintaxa general pentru declararea unei structuri este: struct <Nume_generic> { <Tip> <Nume_membru_1>; <Tip> <Nume_membru_2>; : <Tip> <Nume_membru_n>; : <Tip returnat> <Nume functie>(<Lista de parametri>); } <Variabila_1>,[<Variabila_k>]; n cazul n care n definiia structurii este ncapsulat i o metod atunci la implementare se folosete sintaxa: <Tip returnat> <Structur>::<Nume functie>(<Lista de parametri>); { //Corp funcie }; Pentru mai mult claritate se poate urmri exemplul de mai jos. #include<stdio.h> #include<conio.h> typedef struct { char matricol[6]; char nume[30]; float media; void setstud(char matr[],char num[],float med); } TStud; void TStud::setstud(char matr[],char num[],float med) { strcpy(matricol,matr); strcpy(nume,num); media=med;

60

}; void main() { clrscr(); TStud stud; stud.setstud("12","Mihai Guramare",10); printf("Matricol :%s\n",stud.matricol); printf("Nume :%s\n",stud.nume); printf("Media :%f",stud.media); getch(); } Dup cum se vede deja, numele variabilei structurate, urmat de un punct i numele membrului permite adresarea acelui membru. Adic, n general vorbind: <Variabila_structurat> . <Nume_membru> n codul de mai jos se arat modul concret de declarare a unei structuri i de asociere a acestei declaraii cu un nume de tip prin intermediul cuvntului cheie typedef. #include<conio.h> #include<iostream.h> #include<string.h> void main() { typedef struct TPers{ char nume[30]; float salariu; } TPers; TPers pers; strcpy(pers.nume,"Test typedef + struct."); pers.salariu=1200000; clrscr(); cout<<pers.nume<<"\r\n"<<pers.salariu; getch(); } n mod evident, structurile pot fi asociate i cu pointerii prin declaraii asemntoare celor din codul alternativ de mai jos. #include<conio.h> #include<iostream.h> #include<string.h> void main() {

61

typedef struct TPers{ char nume[30]; float salariu; } TPers; TPers *pers; alocare memorie pentru pers strcpy(pers->nume,"Test relatie structura - pointer"); pers->salariu=1200000; clrscr(); cout<<pers->nume<<"\r\n"<<pers->salariu; getch();

O alt ntrebuinare a structurilor o reprezint posibilitatea de a defini cmpuri de bii. Cmpurile de bii sunt mecanisme cu ajutorul crora programatorul poate avea acces la coninutul variabilelor pn la nivel de bit. Sintaxa pentru definirea unui camp de biti este: typedef struct { tip1 nume1:lungime1; tip2 nume2:lungime2; ... tipn numen:lungimen; } TOctet; Tipurile pot lua una din valorile int, unsigned, signed, pentru majoritatea compilatoarelor. Cmpul de bii cu lungimea 1 este obligatoriu unsigned. A se vedea i exemplul de mai jos. #include<stdio.h> #include<conio.h> #include<string.h> typedef struct { unsigned bit :1; unsigned :7; unsigned :7; unsigned bits:1; } TOctet; TOctet Octet;
62

int nr=1; int *pointer; void main() { int i; pointer=&nr; memcpy(&Octet,pointer,sizeof(int)); clrscr(); printf("Bitul 0 : %i\n",Octet.bit); printf("Bitul de semn: %i\n",Octet.bits); getch(); }

63

8 Uniuni
O uniune este o locaie de memorie care este partajat n momente diferite ntre dou sau mai multe variabile diferite. Sintaxa generala i modul de utilizare pot fi deduse din exemplul de mai jos. #include<stdio.h> #include<conio.h> #include<string.h> typedef union { int codsal; float salar; } TUSal; void main() { clrscr(); TUSal vunion; vunion.codsal=10; printf("Cod confidential salariu:%i\n",vunion.codsal); vunion.salar=1000.50; printf("Salariu :%f",vunion.salar); getch(); } n acest exemplu variabilele codsal i salar, de tipuri diferire, partajeaz aceeai locaie de memorie, dar, important de tiut, n momente diferire ale execuiei unui programului, dup cum se observ, de altfel i din exemplu. ncheiem aici partea intitulat BAZELE C++. LIMBAJUL C, cu meniunea c exist nenumrate alte aspecte ale programrii n C care necesit timp i rbdare pentru a nelege toate consecinele stilului C de programare, nelegere folositoare i n abordarea C++. Dintre aceste aspecte semnalez: fluxurile C, abloanele C, suprascrierea funciilor, etc. Pentru toate acestea exist, ns suport C++ mult mai adecvat i mai comod de multe ori pentru programarea cu adevrat n spirit obiect orientat.

64

II Programarea C++

65

1 Introducere
Programarea orientat pe obiecte (POO) este expresia, n materie de codificare a proiectelor, a paradigmei care presupune modelarea orientat pe obiecte a sistemelor soft. POO este o paradigm care ctig tot mai muli adereni datorit calitilor pe care le au produsele i sistemele soft realizate n spiritul conceptelor i principiilor promovate de aceasta. Rezultant a unor direcii diverse de cercetare i experimentare (programare structurat, programare modular, programare orientat pe structuri abstracte, reprezentarea cunotinelor n sisteme expert, etc.) POO, aplicat corect poate rezolva, parial sau integral, multe din problemele obsedante ale ingineriei softului, n genere: reutilizarea codului, extinderea/modificarea cu minim de efort a sistemelor soft, ascunderea detaliilor de implementare fa de anumite categorii de utilizatori ai sistemelor soft. n acest mod platforma POO poate ajuta programatorii i companiile de soft s realizeze produse i sisteme soft performante, n timp util i la un pre sczut.

1. 1 Concepte POO
Orice demers de modelare orientat pe obiecte apeleaz la o serie de concepte specifice paradigmei POO. Astfel, pentru modelarea unui sistem soft se opereaz frecvent cu: 1. Concepte ale teoriei generale a sistemelor (sistem, subsistem, descompunere, agregare, structur, etc.) 2. Concepte care provin din arsenalul conceptual al modelrii n genere a sistemelor i produselor soft (modul, modularizare, interfa, tip de dat, structur de date, ascunderea informaiei, etc.). Tuturor acestora li se adaug principiile cu ajutorul crora aceste concepte devin operaionale. Paradigma POO a intrat n competiia pentru modernizarea i mbuntirea real a procesului de realizare a unui sistem soft cu un set propriu de concepte i principii. Prezentm mai nti conceptele cheie ale paradigmei POO. Conceptul de clas , prin care se desemneaz o colecie de obiecte (de natur material sau spiritual) care au n comun faptul c pot fi caracterizate similar din punct de vedere informaional i comportamental. Este evident faptul c identificarea unei clase este n mod normal, rezultatul unui demers cognitiv care presupune caracterizarea unui obiect prin nsuirile lui (informaionale i comportamentale) care i definesc apartenena la o anumit clas de obiecte. Aadar, conceptul de clas adun laolalt datele i metodele de prelucrare a acestora. n esen definirea unei clase se bazeaz pe analiza, clasificarea i abstractizarea nsuirilor obiectelor de un anumit tip.
66

Acesta este un exerciiu de ndemnare a crui rezolvare o poate nva oricine care ncearc, are rbdare cu el nsui i citete cum procedeaz iniiaii cnd ntlnesc astfel de exerciii. Aa cum va reiei din implementarea conceptului de clas n C++, de exemplu, conceptul de clas este o abstracie care pregtete un anumit tip de background pentru descrierea soluiei unei probleme date. Fr a intra prea mult n problem s amintim, totui, c modelnd obiect orientat soluiile problemelor noastre ne asigurm o serie de avantaje imediate i de perspectiv n ceea ce privete gestiunea relaiei dintre domeniul problemei i domeniul soluiei n ingineria sistemelor soft. Conceptul de obiect (instan a unei clase) este deja un concept cu valoare operaional, prin care se desemneaz un obiect concret al unei clase definitoare, caracterizat prin valori specifice ale atributelor informaionale. Dac definirea unei clase pune, n primul rnd, probleme de natur conceptual, manipularea unui obiect se bazeaz pe abilitile specifice limbajului referitoare la alocarea de memorie pentru obiect (static sau dinamic), controlul strii obiectului (= valorile atributelor informaionale) cu ajutorul metodelor proprii, etc. Proprietile unui obiect sunt, n mare parte, proprietile unei clase. Calitatea unui obiect depinde de calitatea clasei definitoare. Exist dou perspective din care putem aprecia calitile unui obiect: perspectiva utilizator de obiect i perspectiva specificator de clas definitoare. Utilizatorul de obiect este interesat, practic, de calitatea interfeei obiectului, care permite o apreciere i asupra potenialului informaional i comportamental al obiectului respectiv. Munca unui specificator de clas definitoare poate fi apreciat dup potenialul informaional i comportamental al clasei, accesibil prin intermediul unei interfee care implementeaz principiul cine dorete date despre starea unui obiect al unei clase trebuie s se mulumeasc cu deschiderea oferit n acest sens de interfaa obiectului n cauz. Trdarea acestui principiu este o dovad de neprofesionalism n abordarea obiect orientat a unei probleme. Aa se ajunge c, pentru un programator, un obiect conteaz prin: identitate, interfa(partajat n comun cu alte obiecte de acelai tip prin raportare la o clas definitoare) i stare. Identitatea unui obiect desemneaz procedeul prin care se asigur, n sistem, unicitatea unui obiect. Cele mai multe limbaje rezolv aceast problem prin asocierea instanei unei clase (=obiect) cu o anumit adres de memorie. Interfaa unui obiect desemneaz mulimea metodelor unei clase cu ajutorul crora obiectele clase comunic cu ambiana n care acestea exist. Comunicarea ntre obiecte se realizeaz, dogmatic vorbind, prin mesaje. Conceptul de stare a unui obiect prin care se desemneaz valorile atributelor informaionale ale obiectului. Conceptul de mesaj prin care se nelege un semnal emis de ctre un obiect emitor ctre un obiect receptor, din iniiativa obiectului emitor.

67

Este evident faptul c obiectul care emite mesajul trebuie s cunoasc protocolul de comunicaie al obiectului receptor. De asemenea, se subnelege faptul c mesajul trimis de obiectul emitor va provoca o reacie(= un rspuns) din partea obiectului receptor. Conceptul de metod prin intermediul cruia se d o expresie procedeelor care definesc comportamentul i, direct sau indirect, protocolul de comunicaie al unei clase. Pentru mai mult exactitate, este bine s se neleag faptul c "metoda" este o denumire generic pentru un procedeu de prelucrare care face parte din protocolul de comunicare al unei clase. Metoda poate fi implementat n C, de exemplu, ca funcie, constructor sau destructor. Asupra semnificaiei noiunilor de constructor i destructor vom mai reveni .

1. 2 Principiile POO
Noutatea POO, ca paradigm este ilustrat i de principiile pe care le promoveaz pentru a completa potenialul oferit de concepte. Promovarea sistematic a principiilor pe care le prezentm n continuare promite, dar nu garanteaz, realizarea unor produse, sisteme sau platforme soft remarcabile din punct de vedere al performanelor i al efortului de ntreinere. 1.2.1 Principiul ncapsulrii nelegerea acestui principiu presupune dou nivele de abordare. @ Ca metod de concepie, ncapsularea se refer la capacitatea de a separa aspectele externe ale unui obiect (interfaa), accesibile altor obiecte, de aspectele implementaionale, interne obiectului, care sunt ascunse fa de celelalte obiecte. Utilizatorul unui obiect poate accesa doar anumite metode ale acestuia, numite publice, n timp ce atributele i celelalte metode i rmn inaccesibile (acestea se numesc private). ncapsularea este foarte important atunci cnd dorim s schimbm implementarea anumitor metode (cu scopul de a optimiza un algoritm sau de a elimina posibile erori). ncapsularea ne va mpiedica s modificm toate caracteristicile obiectului iar aplicaiile care utilizeaz obiectul nu vor avea de suferit deoarece protocolul de comunicaie al obiectului motenit de la interfaa clasei (rezultatul ncapsulrii ca metod de concepie) nu s-a schimbat. @Ca implementare, la nivelul unui limbaj de programare, ncapsularea este asigurat de exigenele sintactice specifice.

1.2.2 Principiul motenirii

68

Acest principiu este de mare utilitate n transmiterea sistematic a similaritilor de la o clas la alta. Principiul poate fi aplicat cu succes doar n conjuncie cu operatorii conceptuali complementari:generalizarea i specializarea. Generalizarea apare n relaia dintre o clas i una sau mai multe versiuni mai rafinate ale acesteia. Este, n limbajul de specialitate, relaia dintre clasa de baz (sau superclas) i clasele derivate (sau subclase). Atributele i operaiile comune sunt grupate n superclas i se spune c sunt motenite de subclase. Se poate spune c motenirea se refer la mecanismul de a transmite atribute i operaii de-a lungul unei relaii de generalizare. Evident, simpla motenire a proprietilor unei clase nu rezolv nici o problem din punct de vedere conceptual. Utilitatea principiului motenirii este reliefat n condiiile aplicrii operatorului de specializare, prin care subclasa rafineaz (specializeaz) superclasa. Implementarea motenirii nu creeaz probleme deosebite de nelegere i utilizare. 1.2.3 Principiul polimorfismului Prin aplicarea principiului motenirii se creeaz condiii pentru specializarea unei superclase. Specializarea poate fi realizat prin adugarea de noi operaii celor motenite sau prin redefinirea unora dintre operaiile motenite. Specializarea orientat pe redefinirea operaiilor se afl la baza implementrii principiului polimorfismului. Ca rezultat final aplicarea principiului polimorfismului creeaz posibilitatea ca un mesaj (n structura cruia intervine un nume de operaie) s genereze rspunsuri diferite, n funcie de contextul n care este formulat mesajul. O astfel de posibilitate este cu att mai valoroas cu ct gestiunea contextului n care se formuleaz mesajul este asumat de ctre sistem. Ca mecanism, aplicarea principiului presupune instituirea unui protocol pentru evidena legturilor dintre clase i operaiile aferente, susceptibile de redefinire.

69

2 Implementarea C++ a POO


2.1 Definirea claselor
Pentru a putea lucra cu obiecte n C++ trebuie, n prealabil, s definim forma lor general folosind n acest scop cuvntul cheie class. O clas este similar, sintactic vorbind, cu o structur. Forma general a unei declaraii de clas care nu motenete nici o alt clas este urmtoarea: class <Nume_clasa> [:<Lista_claselor_de_baz>] { <Date i funcii particulare> <Specificator de acces>: <Date si functii> <Specificator de acces>: <Date si functii> : : <Specificator de acces>: <Date si functii> } [<Lista de obiecte>]; <Lista de obiecte> este opional. Dac exist, ea declar obiecte din acea clas. <Specificator de acces> este unul din cuvintele cheie: -public -private -protected. Implicit, funciile i datele declarate ntr-o clas sunt proprii acelei clase, doar membrii si avnd acces la ele. <Lista_claselor_de_baz> indic, opional, clasele de la care se pot moteni atribute informaionale i comportamentale.  Pentru datele i funciile care fac parte din definiia unei clase se obinuiesc i denumirile de variabile membre, respectiv, funcii membre sau, pur i simplu, membri. Folosind specificatorul de acces public, permitem funciilor sau datelor membre s fie accesibile altor seciuni ale programului nostru. Odat utilizat un specificator, efectul su dureaz pn cnd se ntlnete alt specificator de acces sau se ajunge la sfritul declaraiei clasei. Pentru a reveni la modul privat de declarare a membrilor, se folosete specificatorul de acces private. Specificatorul protected are implicaii asupra vizibilitii membrilor unei clase n cazul n care se face i motenire. Prezentm, n continuare, un exemplu de definiie de clas, care ncapsuleaz date i operaii minimale referitoare la un salariat al unei firme( nume, matricol, salariu).

70

#include <conio.h> #include <string.h> #include <iostream.h> // Definitie clasa fara declarare de variabile obiect. class Persoana { public: char nume[30]; int matricol; float salariu; void setfields(char n[30], int m, float s); void afis(); }; // Implementare functie setfields void Persoana:: setfields(char *n, int m, float s) { strcpy(nume,n); matricol=m; salariu=s; } // Implementare functie afis void Persoana:: afis() { clrscr(); cout<<nume<<"\n"<<salariu; getch(); } //Implementare functie principala void main() { // Declarare variabila obiect Persoana pers; pers.setfields("Radu Vasile",1,10000); pers.afis(); } n exemplul prezentat am folosit specificatorul de acces public pentru a permite funciei principale, care a declarat variabila obiect pers, accesul la membrii clasei definitoare a variabilei pers. n programarea obiect orientat adevrat n C++, efectul de ascundere a membrilor unei clase este intens utilizat, interzicnd

71

anumitor categorii de utilizatori accesul la membrii clasei, definind pentru comunicaia cu alte clase i categorii de utilizatori ceea ce se numete interfaa clasei. Evident, se poate face o oarecare analogie ntre clas i structur; analogia cade, ns, n momentul n care se pune problema manevrabilitii instanelor celor dou tipuri de concepte sau atunci cnd este vorba de motenire i polimorfism. Un exemplu ceva mai apropiat de realitatea preocuprilor unui programator C++ poate fi programul care implementeaz o stiv de ntregi, prezentat n continuare. #include <iostream.h> #include <conio.h> #define SIZE 100 // Definirea clasei stack class stack { int st[SIZE]; int top; public: void init(); void push(int i); int pop(); }; //Implementare functie init void stack::init() { top=0; } //Implementare functie push void stack::push(int i) { if (top==SIZE) { gotoxy(20,24); cout<<"Stiva este plina!!!"; getch(); return; }; st[top]=i; top++; } //Implementare functie pop

72

int stack::pop() { if(top==0) { gotoxy(20,24); cout<<"Depasire inferioara stiva!!!"; getch(); return 0; }; top--; return st[top]; } //Functia principala void main() { int i; stack st1,st2; st1.init(); st2.init(); for (i=0;i<=9;i++) { st1.push(i); st2.push(9-i); } clrscr(); for (i=0;i<=9;i++) { gotoxy(35,wherey()+1); cout<<st1.pop()<<"*****"; cout<<st2.pop()<<endl; } getch(); }

2.2 Clase derivate. Constructori. Destructori. Obiecte C++


Modelarea obiect orientat a soluiei unei probleme de oarecare complexitate pune, inevitabil i problema transmiterii similaritilor ntre clase care formeaz o ierarhie sau o reea. Suportul C++ pentru rezolvarea acestei probleme l reprezint posibilitatea ca o clas s fie derivat din una sau mai multe clase, ceea ce nseamn posibilitatea de a abstractiza soluia cu scopul de a reutiliza cod i de a adapta codul uor la cerine noi dac este cazul. Am vzut mai sus sintaxa pentru motenire. S adugm, n plus la cele spuse mai sus, c n <Lista_claselor_de_baz> clasele definitoare apar precedate, eventual, de un modificator de protecie i sunt separate ntre ele prin virgul.

73

Modificatorii de protecie utilizai n <Lista_claselor_de_baz> definesc protecia n clasa derivat a elementelor motenite. Prezentm sub form tabelar accesul la elementele motenite de clasa derivat n funcie de protecia fiecrui element i de modificatorul de protecie asociat n <Lista_claselor_de_baz>. n clasa Tipul de acces al Modificatorul de protecie Accesul elementului n clasa asociat clasei de baz la derivat la element definirea clasei de baz private private interzis protected private private public private private private public interzis protected public protected public public public Tabelul 14. Problematica accesului la membrii unei clase derivate Concluzionnd, dac n clasa de baz accesul la element este private atunci n clasa derivat accesul este interzis. n schimb, clasa derivat are acces la elementele clasei de baz aflate sub incidena accesului protected/public. Se mai poate observa, totodat, c dac la definirea clasei derivate se utilizeaz modificatorul de protecie private, atunci elementele protejate n clasa de baz prin protected sau public devin protejate private n clasa derivat; deci inaccesibile unor clase care s-ar deriva eventual din clasa derivat. Exemplu #include <iostream.h> class CB { protected: int i,j; //Pentru un program care utilizeaz aceast clas //sau pentru alt clas care nu se afl n relaie //de derivare cu CB I,j sunt private din punct de //vedere al accesului. i,j sunt accesibile unei clase derivate. public: void setij(int a, int b) {i=a;j=b} void dispij() { cout<<i<< <<j<<endl;} } class CD:public CB { int k; public: void setk(){k=I*j;} void dispk(){cout<<k<<endl;}

74

} void main() { CD ob; ob.setij(2,3); ob.dispij(); ob.setk(); ob.dispk (); }

//OK! //OK

Motenirea protected a clasei de baz Este posibil s se moteneasc o clas de baz ca protected. Cnd se procedeaz astfel, toi membrii public i protected ai clasei de baz devin membri protected ai clasei derivate. Exemplu #include <iostream.h> class CB { protected: int i,j; //Pentru un program care utilizeaz aceast clas //sau pentru alt clas care nu se afl n relaie //de derivare cu CB i, j sunt private din punct de //vedere al accesului. i,j sunt accesibile unei clase derivate. public: void setij(int a, int b) {i=a;j=b} void dispij() { cout<<i<< <<j<<endl;} } class CD:protected CB { int k; public: void setk(){setij(10,20);k=I*j;} void disptot(){cout<<k<< ;dispij();} } void main() { CD ob; ob.setij(2,3); ob.setk();

//ilegal! //OK!, membru public n CD

75

ob.setk(); ob.disptot (); ob.dispij(); }

//OK! //ilegal! Membru protected n CD

Constructori i destructori Este un fapt obinuit necesitatea ca unele elemente ale unui obiect s fie iniializate. Pentru a degreva programatorul de o asemenea sarcin de rutin, compilatorul C++ genereaz cod care permite obiectelor s se iniializeze singure. Aceast iniializare automat este efectuat prin intermediul unei funcii membru speciale a clasei definitoare numit constructor. Constructorul este o funcie care are acelasi nume cu clasa. Un constructor al unui obiect este apelat automat la crearea obiectului. Un constructor al unui obiect este apelat o singur dat pentru obiecte globale sau pentru cele locale de tip static. Constructorul este o funcie fr tip ceea ce nu reclam, totui cuvntul cheie void n locul tipului. Pentru obiecte locale constructorul este apelat de fiecare dat cnd este ntlnit declararea acestuia. Complementul constructorului este destructorul. De multe ori un obiect trebuie s efectueze anumite aciuni cnd este distrus. Este evident faptul c obiectele locale sunt distruse la prsirea blocului n care apar iar obiectele globale la terminarea programului. Cnd este distrus un obiect, este apelat destructorul clasei definitoare. Destructorul are acelai nume cu constructorul, dar precedat de un caracter ~. O clas are un singur destructor i acest destructor nu poate avea parametri formali. Att constructorul ct i destructorul, n C++ nu pot s returneze valori. De semnalat faptul c n situaia n care programatorul nu specific un constructor explicit la definirea unei clase, la crearea unei instane a clasei se folosete constructorul implicit ataat de compilator fiecrei clase. Constructorul implicit nu are parametri formali, ceea ce are drept consecin faptul c nu este permis iniializarea la declarare a datelor membre ale obiectelor. De asemenea, constructorul implicit nu este generat n cazul n care clasa are ataat un alt constructor fr parametri. Programatorul poate nzestra clasa cu o proprie funcie constructor. n acest scop trebuie inut cont de faptul c o metod constructor are ntotdeauna numele clasei din care face parte. Programatorul poate nzestra clasa i cu parametri formali ceea ce permite i o formul elegant de iniializare a datelor membre ale obiectelor clasei respective.

76

 Foarte important mi se pare s semnalez i existena unui constructor special, numit constructor de copiere. Acesta are rolul de a atribui datele unui obiect altuia. Mai mult, poate fi apelat chiar la definirea obiectelor. Dac programatorul nu definete propriul constructor de copiere, compilatorul adaug automat un astfel de constructor. n exemplul de mai jos se pot urmri elemente de sintax i semantic care trebuie cunoscute cnd se lucreaz cu constructori n diferite ipostaze. #include<stdio.h> #include<conio.h> //Definire clas class intreg { public: int a; //Constructor parametrizat intreg(int v) { printf("Constructor parametrizat\n"); getch(); a=v; } //Constructor de copiere intreg (intreg& v) { a=v.a; printf("Constructor de copiere\n"); getch(); } void dispa() { printf("a=%i\n",a); getch(); }; }; void main() { clrscr(); //Declarare obiect static cu iniializare; intreg x=100; x.dispa(); //Declarare obiect static cu iniializare prin copiere intreg y=x;

77

y.dispa(); } Evident, o clas poate avea mai muli constructori, ceea ce este tot n beneficiul programatorilor. Crearea obiectelor n C++ Obiectele pot fi create static sau dinamic. Aa cum s-a vzut i n exemplul de mai sus sintaxa pentru varianta static este: <Clasa> <Obiect>[(<Lista de valori>)]; sau <Clasa> <Obiect>[=<Valoare>]; Sintaxa ne arat c odat cu crearea instanei se poate face i iniializarea datelor membre ale obiectului cu ajutorul constructorilor parametrizai. Pentru a nelege alocarea dinamic a memoriei pentru obiecte trebuie s facem o scurt prezentare a problematicii alocrii dinamice structurate a memoriei n C++. Operatori de alocare dinamic a memoriei n C++ n C, alocarea dinamic a memoriei este realizat cu ajutorul funciior malloc() i free(). Din motive de compatibilitate i nu numai, aceste funcii sunt valabile i n C++. Totodat, C++ are un sistem alternativ de alocare dinamic bazat pe operatorii new i delete. Sintaxa general pentru new i delete este: <Pointer>=new <Tip>; delete <Pointer> ; <Pointer> este o variabil pointer, compatibil ca tip cu <Tip>. Aadar, <Pointer> poate pstra adresa ctre zona de memorie n care ncap date avnd tipul <Tip>. De subliniat c operatorul delete trebuie folosit doar cu un pointer valid, alocat deja prin utilizarea operatorului new. n caz contrar, rezultatele sunt imprevizibile. Fa de malloc()i free(), operatorii new i delete prezint cteva avantaje: @ new aloc automat memorie suficient pentru a pstra obiectele de tipul specificat. Nu mai este necesar folosirea operatorului sizeof. @ new returneaz automat un pointer de tipul specificat. Nu mai este necesar s folosim modelatorii de tip ca n cazul funciei malloc(). @ Att new ct i delete pot fi suprancrcai, ceea ce v permite crearea unui sistem propriu de alocare dinamic a memoriei. Despre suprancrcare vom discuta n paragrafele 2.4 i 2.7 . n plus, operatorul new mai are i alte capabiliti: @Permite iniializarea memoriei alocate unui tip de baz cu o valoare dat, utiliznd sintaxa:

78

<Pointer>=new <Tip> (<Valoare_iniial>); ca n exemplul: int *p; p=new int(10); @Operatorul new permite alocarea de memorie pentru matrici, utiliznd sintaxa: <Pointer>=new <Tip> [Marime]; Memoria alocat unei matrici de operatorul new se elibereaz de ctre delete apelat cu sintaxa: delete [ ] <Pointer>  Matricile nu pot fi iniializate n timpul alocrii. Suntem n msur s prezentm soluia C++ pentru problema alocrii dinamice a memoriei pentru obiecte. Obiectelor li se poate aloca memorie dinamic folosind operatorul new. Cnd procedai astfel se creeaz un obiect i se returneaz un pointer ctre el. Obiectul creat dinamic se comport ca oricare altul. Cnd este creat este apelat i funcia constructor. Cnd este eliberat se execut i funcia destructor. Sintaxa general: <Pointer_la_obiect>=new <Clasa>; Dac <Clasa> are constructor parametrizat atunci se poate folosi sintaxa: <Pointer_la_obiect>=new <Clasa>(<Lista_valori>); pentru a realiza i iniializarea obiectului. n sfrit, n cazul matricilor de obiecte, crora li se poate aloca memorie dinamic, trebuie s ne asigurm c n cazul n care exist funcii constructor, una va fi fr parametri. n caz contrar, compilatorul d eroare de sintax. Modalitatea concret de utilizare a conceptului de constructor, precum i a conceptului complementar, destructorul, poate fi desprins i din exemplul de mai jos care reia modelarea obiect orientat a unei stive de ntregi. #include <iostream.h> #include <conio.h> #define SIZE 100

79

// Definirea clasei stack class stack { int st[SIZE]; int top; public: stack(); //constructor ~stack(); //destructor void push(int i); int pop(); }; //Implementare constructor stack::stack() { top=0; gotoxy(20,24); cout<<"Stiva este initializata"; getch(); clrscr(); } stack::~stack() { gotoxy(20,24); cout<<"Stiva este distrusa!!"; getch(); clrscr(); } //Implementare functie push void stack::push(int i) { if (top==SIZE) { gotoxy(20,24); cout<<"Stiva este plina!!!"; getch(); return; }; st[top]=i; top++; } //Implementare functie pop int stack::pop()

80

{ if(top==0) { gotoxy(20,24); cout<<"Depasire inferioara stiva!!!"; getch(); return 0; }; top--; return st[top]; } //Functia principala void main() { int i; stack st1,st2; for (i=0;i<=9;i++) { st1.push(i); st2.push(9-i); } clrscr(); for (i=0;i<=9;i++) { gotoxy(35,wherey()+1); cout<<st1.pop()<<"*****"; cout<<st2.pop()<<endl; } getch(); }

2.3 Funcii virtuale i polimorfism


C++ asigur suport pentru polimorfism att n timpul compilrii ct i pe timpul execuiei unui program. Polimorfismul n timpul compilrii este legat de posibilitatea de a suprancrca funciile i operatorii, problem pe care o vom discuta n paragrafele 2.4 i 2.7. Polimorfismul n timpul execuiei este obinut combinnd principiile motenirii cu mecanismul funciilor virtuale. Funcii virtuale O funcie virtual este o funcie declarat virtual n clasa de baz i redefinit ntr-un lan de derivare asociat respectivei clase de baz. O funcie virtual definete o clas general de aciuni. O redefinire a ei introduce o metod specific. n esen, o funcie virtual declarat n clasa de baz acioneaz ca un substitut pentru pstrarea elementelor care specific o clas general de aciuni, stabilind elementele de interfa. Redefinirea unei funcii virtuale ntr-o

81

clas derivat ofer operaiile efective pe care le execut funcia. Utilizate static, funciile virtuale se comport ca oricare alt funcie membru a clasei . Capabilitile funciilor virtuale ies n eviden atunci cnd sunt apelate n context dinamic. Exemplu #include <iostream.h> #include <conio.h> //Clasa de baza care are doua functii membri publici //o functie ordinara //cealalta functie virtuala class CB { public: void f() { cout<<"CB::f()"<<endl; getch(); } virtual void g() //funcie virtual { cout<<"CB::g()"<<endl; getch(); } }; //Clasa derivata din clasa de baza class CD:public CB { public: void f() { cout<<"CD::f()"<<endl; getch(); } virtual void g() { cout<<"CD::g()"<<endl; getch(); } }; //Functie care permite utilizarea polimorfismului //Parametrul functiei este un pointer la un obiect de tip CB //De retinut ca un parametru de tip CD este compatibil //cu tipul lui p

82

void ExecPolim(CB *p); void main() { CB *pb=new CB; CD *pd=new CD; clrscr(); pb->f(); pb->g(); pd->f(); pd->g(); clrscr(); cout<<"Apel polimorfic in context CB"<<endl; ExecPolim(pb); cout<<"Apel polimorfic in context CD"<<endl; ExecPolim(pd); delete pb; delete pd; }; void ExecPolim(CB *p) { p->f(); p->g(); } La prima vedere redefinirea unei funcii virtuale ntr-o clas derivat pare similar cu suprancrcarea unei funcii. Nu este aa, deoarece exist mai multe diferene: @Prototipul pentru o funcie virtual redefinit trebuie s coincid cu prototipul specificat n clasa de baz. @Funciile virtuale nu pot s fie membri de tip static ai clasei din care fac parte i nu pot fi nici friend Funciile obinuite suport suprancrcarea; funciile virtuale suport suprascrierea. n cele ce urmeaz facem o serie de precizri relativ la clauza virtual. Clauza virtual este motenit Cnd o funcie virtual este motenit se motenete i natura sa virtual. Astfel c, n situaia n care o clas derivat a motenit o funcie virtual i este folosit drept clas de baz pentru o alt clas derivat, funcia virtual poate fi n continuare suprascris. Altfel spus, o funcie rmne virtual indiferent de cte ori este motenit. Funciile virtuale sunt ierarhizate

83

Deoarece n C++ motenirea este ierarhizat este normal ca funciile virtuale s fie, de asemenea, ierarhizate. Acestea nseamn c, atunci cnd o clas derivat nu suprascrie o funcie virtual, este utilizat prima redefinire gsit n ordinea invers derivrii. S mai spunem c exist situaii n care programatorul vrea s se asigure c toate clasele derivate suprascriu o funcie virtual sau programatorul definete o clas de baz astfel nct s nu permit definirea unei funcii virtuale n aceast clas. Pentru aceste dou situaii exist funciile virtuale pure. Sintaxa unei funcii virtuale pure este: virtual <Tip> <Nume_funcie> [(<Lista)_de_parametri>)]=0; O clas care conine cel puin o funcie virtual pur se numete clas abstract. O clas abstract nu poate fi utilizat pentru crearea de obiecte dar poate fi utilizat pentru a crea pointeri i referine spre astfel de clase permind astfel manifestrile polimorfice n timpul execuiei.

2.4 Funcii suprancrcate


O modalitate C++ de realizare a polimorfismului o reprezint suprancrcarea funciilor (overloading). n C++, dou sau mai multe funcii pot s aib acelai nume ct timp declaraiile lor de parametri sunt diferite. n aceast situaie se spune c funciile cu acelai nume sunt suprancrcate iar procesul este numit suprancrcarea funciilor. Cnd suprancrcai o funcie trebuie s respectai urmtoarele cerine: @Listele de parametri ale diferitelor versiuni ale unei funcii trebuie s difere; @Versiunile pot diferi i din punct de vedere al tipului returnat; acest lucru nu este, ns, obligatoriu. Odat specificate versiunile unei funcii respectnd restriciile de mai sus, compilatorul este n msur s aleag metoda adecvat unui apel specific. Avantajele suprancrcrii, nu neaprat n context obiect orientat, pot fi desprinse i din analiza exemplului de cod C++ de mai jos. #include <iostream.h> #include <conio.h> //functia abs este definita in trei moduri diferite int abs(int i); double abs(double d); long abs(long l); void main() { clrscr();

84

cout<<abs(-10); cout<<abs(-10.0); cout<<abs(-10L); getch(); } int abs(int i) { gotoxy(20,10);cout<<"Varianta int !!! :"; return i<0 ?-i:i; } double abs(double d) { gotoxy(20,12);cout<<"Varianta double!!! :"; return d<0 ?-d:d; } long abs(long l) { gotoxy(20,14);cout<<"Varianta long !!! :"; return l<0 ?-l:l; } Acest program implementeaz i utilizeaz trei funcii cu acelai nume (abs()) dar diferite prin proprietile listelor de parametri formali. Fiecare returneaz un mesaj specific i valoarea absolut a argumentului. Numele abs() reprezint aciunea generic care urmeaz s fie efectuat, compilatorul fiind acela care alege metoda potrivit tipului de parametru actual asociat cu numele funciei generice. Este uor de bnuit puternicul impact al suprancrcrii n cazul programrii generice.

2.5 Funcii inline


Aceste funcii sunt similare macrocomenzilor adresate preprocesoarelor, compilatorul nlocuind fiecare apel de funcie inline cu corpul acesteia. Funciile inline sunt destinate s sprijine implementarea eficient a tehnicilor OOP n C++. Deoarece abordarea OOP necesit utilizarea extensiv a funciilor membre, desele apeluri de funcii ar putea duce la scderea performanelor programelor. Pentru funcii al cror cod este redus se poate utiliza specificatorul inline pentru a evita acest inconvenient. Similaritatea macrocomenzi-funcii inline este doar aparent. Spre deosebire de macrocomenzi, compilatorul consider funciile inline ca funcii adevrate. O funcie membru inline se declar astfel: inline <Tip><Nume_Funcie>([<Lista de parametri>]);

2.6 Funcii prietene


85

Este posibil s se permit unei funcii care nu este membru al unei clase s aib acces la membrii privai ai clasei declarnd-o ca funcie prieten a clasei (friend). Aadar, o funcie prieten are acces la membrii private i protected ai clasei creia i este prieten. Sintaxa pentru declararea unei funcii prietene este: friend <Tip> >Nume_funcie>[(<Lista_parametri>)] Declararea funciei friend se face n interiorul clasei dar implementarea nu este legat de definirea clasei. A se vedea i exemplul de mai jos pentru mai multe detalii. #include<conio.h> #include <iostream.h> #define liber 0 #define ocupat 1 //Declarare anticipata clasa class Clasa2; class Clasa1 { //<ocupat> daca o metoda a clasei a scris pe ecran //<liber> in caz contrar int stare; public: void setare_stare(int val); friend int ecran_liber(Clasa1 a,Clasa2 b); }; class Clasa2 { //<ocupat> daca o metoda a clasei a scris pe ecran //<liber> in caz contrar int stare; public: void setare_stare(int val); friend int ecran_liber(Clasa1 a,Clasa2 b); }; void Clasa1::setare_stare(int val) { stare=val; } void Clasa2::setare_stare(int val) { stare=val; }
86

int ecran_liber(Clasa1 a, Clasa2 b) { if (a.stare ||b.stare) return 0; else return 1; } void main() { Clasa1 x; Clasa2 y; x.setare_stare(ocupat); y.setare_stare(liber); if (ecran_liber(x,y)) { clrscr(); gotoxy(20,12); cout<<"Ecranul este liber..."; getch(); } else { clrscr(); gotoxy(20,12); cout<<"Ecranul este ocupat..."; getch(); }; x.setare_stare(liber); y.setare_stare(liber); if (ecran_liber(x,y)) { clrscr(); gotoxy(20,12); cout<<"Ecranul este liber..."; getch(); } else { clrscr(); gotoxy(20,12); cout<<"Ecranul este ocupat..."; getch(); }; }

87

2.7 Suprancrcarea operatorilor


Polimorfismul este realizat n C++ i prin suprancrcarea operatorilor. Dup cum s-a vzut n numeroase exemple, n C++ se pot folosi operatorii >> i << pentru a efectua operaii I/O relativ la consol. Aceti operatori pot efectua aceste operaii suplimentare (tiut fiind faptul c pot funciona i ca operatorii de shiftare la nivel de bii) deoarece operaiile sunt suprancrcate n fiierul antet IOSTREAM.H. Cnd un operator este suprancrcat, el capt o semnificaie suplimentar relativ la o anumit clas fr s-i piard vreunul din nelesurile iniiale. Majoritatea operatorilor din C++ pot fi suprancrcai, stabilind semnificaia lor relativ la o anumit clas. Limbajul C++ permite suprancrcarea numai a operatorilor existeni n limbaj. Dintre acetia nu pot fi suprancrcai operatorii: . :: ? : . S mai precizm faptul c, prin suprancrcarea operatorilor nu se poate schimba n-aritatea, prioritatea sau asociativitatea operatorilor, acestea fiind elemente predefinite pentru tipuri predefinite i deci ele se vor menine i pentru tipuri abstracte. Prin n-aritate nelegem c operatorul este unar sau binar. Suprancrcarea operatorilor se realizeaz cu ajutorul unor funcii membre sau prietene speciale. Specificul acestora se afl n numele lor. El se compune din cuvntul cheie operator i unul sau mai multe caractere care definesc operatorul care se suprancarc. ntre cuvntul cheie operator i caracterele care definesc operatorul care se suprancarc se afl cel puin un spaiu alb. Felul n care sunt scrise funciile operator difer pentru cele de tip membru de cele de tip friend.

2.7.1 Crearea unei funcii operator membru


Funciile operator membru au sintaxa de implementare: <Tip_returnat> <Nume_clas>::operator # (<Lista_argumente>) { //Operaii specifice } Deseori funciile operator returneaz un obiect din clasa asupra creia opereaz, dar <Tip_returnat> poate fi orice tip valid. # este o notaie pentru numele operatorului aa cum va fi folosit in program dup redefinire. Deci, dac suprancrcm operatorul = atunci sintaxa prototipului funciei membru va fi: <Tip_returnat> operator =(<Lista_argumente>); Funciile operator membre au un singur parametru sau nici unul. n cazul n care au un parametru, acesta se refer la operandul din dreapta al operatorului. Cellalt operand ajunge la operator prin intermediul pointerului special this.

88

Astfel stnd lucrurile, trebuie s avem grij de eventualitatea ca operandul din stnga s fie o constant, ceea ce nseamn ca nu mai avem context de apel pentru operatorul redefinit. Inivitm cititorul s urmreasc exemplul de mai jos. #include<conio.h> #include<iostream.h> //Clasa complex contine functia operator + //ca membru //operatorul + este extins la multimea numerelor complexe class complex { float x,y; public: complex(){}; complex(float a,float b) { static i; i++; clrscr(); cout<<"Lucreaza constructorul...Obiectul->:"<<i; getch(); x=a; y=b; }; void disp_nc(); //prototipul operatorului + complex operator+(complex &op2); }; void complex::disp_nc() { cout<<x<<"+i*"<<y; }; //Implementare operator + //Aceasta sintaxa transforma apelul <ob1+ob2> //in +(ob2) ob1 fiind accesibil prin pointerul //special <this> complex complex::operator+(complex &op2) { complex temp; temp.x=op2.x+x; temp.y=op2.y+y; return temp;

89

}; void main() { complex tamp1,tamp2; complex *pod1,*pod2; complex ob1(10,10),ob2(11,11); clrscr(); gotoxy(20,10);cout<<"Primul numar complex ->:"; ob1.disp_nc(); getch(); gotoxy(20,11);cout<<"Al doilea numar complex->:"; ob2.disp_nc(); getch(); ob1=ob1+ob2; gotoxy(20,13);cout<<"Suma numerelor complexe->:"; ob1.disp_nc(); getch(); pod1=new complex(200,200); pod2=new complex(300,300); tamp1=*pod1; clrscr(); gotoxy(20,10);cout<<"Al treilea numar complex tamp1.disp_nc(); tamp2=*pod2; gotoxy(20,11);cout<<"Al patrulea numar complex tamp2.disp_nc(); ->:";

->:";

gotoxy(20,14);cout<<"Suma numerelor complexe->:"; tamp1=tamp1+tamp2; tamp1.disp_nc(); getch();

2.7.2 Suprancrcarea operatorilor folosind o funcie friend


Se poate suprancrca un operator relativ la o clas folosind o funcie friend. Deoarece un prieten nu este membru al clasei, nu are disponibil un pointer de tip this. De aceea, unei funcii suprancrcate de tip friend operator I se vor transmite explicit operanzii. Deci, dac suprancrcm un operator unar vom avea un parametru, dac suprancrcm unul binar vom avea doi parametri. Dac suprancrcm un operator binar, operandul din stnga este pasat n primul parametruiar cel din stnga n al doilea parametru.

90

Invitm cititorul s urmreasc utilizarea funciilor friend la suprancrcare n Exerciiul 6 de la Capitolul aplicativ al prezentului suport de curs.

2.8 Forma general a unui program n C++


Cu toate c stilurile individuale difer, majoritatea programelor n C++ au urmtoarea form general: #include declaraii clase de baz declaraii clase derivate prototipuri de funcii nemembre main() { : } definiii de funcii ne-membre De remarcat, totodat, faptul c, n majoritatea proiectelor mari, toate declaraiile de clase se pun ntr-un fiier antet i sunt incluse n fiecare modul.

91

3 Bazele sistemului I/O n C++


Pe lng faptul c permite sistemul de I/O din C, C++ definete propriul su sistem I/O orientat pe obiecte. Ca i sistemul de I/O din C, cel din C++ este complet integrat. Aceasta nseamn c diferitele tipuri de operaii I/O sunt doar perspective diferite ale aceluiai mecanism. Aceast perspectiv integratoare asupra operaiilor I/O are la baz, att n C ct i n C++, conceptul de flux (stream).

3.1 Fluxuri n C i C++


Sistemul de fiiere din C i C++ este proiectat s lucreze cu o mare varietate de echipamente, care include terminale, drivere de disc, drivere de unitate de band, etc. Chiar dac echipamentele difer, sistemul de fiiere din C i C++ le transform ntr-un instrument logic numit flux (stream). Toate fluxurile se comport la fel. Deoarece fluxurile sunt independente de echipamente, o funcie care poate s scrie ntr-un fiier de pe hard poate fi folosit cu aceeai sintax pentru a scrie la alt dispozitiv. Sistemul de fiiere C/C++ recunoate dou tipuri de fluxuri:text i binar. Fluxuri de tip text Un flux de tip text este o secven de caractere. Standardul ANSI C permite (dar nu impune) ca un flux de tip text s fie organizat n linii terminate cu un caracter de linie nou. Totui, caracterul de linie nou din ultima linie este opional, utilizarea sa fiind determinat de modul de implementare a compilatorului (Majoritatea compilatoarelor de C/C++ nu ncheie fluxul de tip text cu un caracter de linie nou. S mai semnalm faptul c, ntr-un flux de tip text pot s apar anumite transformri cerute de mediul de operare gazd (De exemplu, un caracter de linie nou poate fi nlocuit cu perechea nceput de rnd-linie nou. Acesta este motivul pentru care nu exist o relaie biunivoc ntre caracterele care sunt scrise sau citite i cele de la echipamentul extern. Fluxuri binare Un flux binar este o secven de octei ntr-o coresponden biunivoc cu cei de la echipamentul extern. Fiiere n C/C++ un fiier poate s fie: un fiier de pe disc, tastatura, ecranul monitorului, imprimanta,etc. Un flux se asociaz cu un anumit fiier efectund o operaie de deschidere. Odat deschis fiierul, este posibil schimbul de date ntre el i programul utilizator care l-a deschis. De observat faptul, trivial pentru cunosctori, c nu toate fiierele au aceleai posibiliti. De exemplu, un fiier de pe disc poate s admit un acces aleator la datele stocate n el, n timp ce imprimanta nu o poate face. Astfel c, putem concluziona, pentru claritate:

92

 Pentru sistemul I/O din C/C++ toate fluxurile sunt la fel dar nu i fiierele. Dac fiierul admite cereri de poziionare, deschiderea fiierului iniializeaz pointerul de fiier la o valoare care indic nceputul fiierului. Pe msur ce se fac operaii de citire/scriere, pointerul de fiier este incrementat corespunztor naturii operaiei. Un fiier se disociaz de un flux n urma operaiei de nchidere. Dac este nchis un fiier deschis n operaii de scriere, coninutul fluxului asociat este scris la dispozitivul extern (acest proces se numete flushing=golire a fluxului ). Toate fiierele se nchid automat cnd programul se termin normal. n caz de blocaj sau dac programul se termin ca urmare a apelului funciei abort() fiierele nu se nchid. Cu meniunea c n fiierul antet stdio.h se gsesc structurile de control de tip FILE, indispensabile pentru lucrul cu fiiere n C, prezentm, n continuare contextul C++ referitor la sistemul I/O.

3.2 Clasele de baz pentru fluxuri n C++


C++ asigur suportul pentru sistemul su de I/O n fiierul antet IOSTREAM.H. n acest fiier antet sunt definite dou ierarhii de clase care admit operaii de I/O. Clasa cu nivelul de abstractizare cel mai nalt se numete streambuf i asigur operaiile de baz de intrare/ieire. Ca programatori, nu folosii streambuf direct dect dac vei deriva propriile clase pentru efectuarea operaiilor I/O. A doua ierarhie pornete cu clasa ios, care accept operaii I/O formatate. Din ea sunt derivate clasele istream, ostream i iostream. Aceste clase sunt folosite pentru a crea fluxuri capabile s citeasc, s scrie, respectiv s citeasc/ s scrie date din/ la echipamentele externe. Clasa ios conine o serie de alte ramuri relativ la lucrul cu fiiere pe care nu ne propunem s le studiem n cadrul acestui curs. Fluxuri predefinite n C++ Cnd i ncepe execuia un program C++, se deschid automat patru fluxuri predefinite, pe care le prezentm n tabelul de mai jos. Flux cin cout cerr clog Semnificaie Echipament implicit Intrare standard Tastatura Ieire standard Ecran Ieire standard pentru eroare Ecran Versiune cu memorie tampon pentru Ecran cerr Tabelul 15. Fluxurile predefinite C++

Fluxurile cin, cout, cerr corespund fluxurilor stdin, stdout, stderr din C. Implicit, fluxurile standard sunt folosite pentru a comunica cu consola. ns, n mediile care admit redirecionarea I/O, fluxurile standard pot fi redirecionate spre alte echipamente sau fiiere.

93

I/O formatate n C++ Sistemul de I/O din C++ v permite s formatai operaiile I/O, aa cum se ntmpla i n cazul utilizrii funciilor C pentru operaii I/O, precum: printf, cprintf, scanf,etc. De exemplu, se poate specifica mrimea unui cmp, baza unui numr, numrul de cifre dup punctul zecimal,etc. Operatorii din C++ utilizai pentru introducerea informaiilor de formatare sunt >> i <<. Exist dou ci nrudite, dar conceptual diferite, prin care se pot formata datele. n primul rnd, putem avea acces direct la diferii membri ai clasei ios. n al doilea rnd, putem folosi funcii speciale numite manipulatori, care pot fi incluse n expresii de I/O. Prezentm, n continuare modul de utilizare a manipulatorilor de formate, datorit accesibilitii mai mari a acestora. Manipulatorii standard sunt prezentai n tabelul de mai jos. Manipulator dec endl ends flush hex oct resetiosflags(long f) setbase(int baza) Exemplu de folosire cout<<dec<<intvar; cout<<endl cout<<ends cout<<flush cout<<hex<<intvar; cin>>hex>>intvar cout<<oct<<intvar; cin>>oct>>intvar; cout<<resetioflags(ios::dec); Efect Convertete ntregi n cifre zecimale; corespunde formatului %d din C Trimite o nou linie n ostream i descarc bufferul Insereaz un caracter nul ntr-un flux Descarc bufferul fluxului ostream Conversie hexazecimal corespunztoare formatului %x din ANSI C Conversie octal (formatul %o din C) Reiniializeaz biii de formatare specificai de argumentul ntreg de tip long Stabilete baza de conversie la argumentul ntreg (trebuie s fie 0,8,10 sau 16). Valoarea 0 este baza implicit. Stabilete caracterul folosit pentru completarea cmpurilor de mrime specificat Stabilete biii de formatare specificai de

cout<<setbase(10); cin>>setbase(8);

setfill(int ch)

cout<<setfill(.); cin>>setfill( ); cout<<setiosflags(ios::dec); cin>> setiosflags(ios::hex);

setiosflags(long f)

94

setprecision(int p)

setw(int w) ws

argumentul ntreg de tip long cout<<setprecision(6); Stabilete precizia cin>>setprecision(10); conversiei n virgul mobil la numrul specificat de zecimale cout<<setw(6)<<var; Stabilete mrimea unui cin>>setw(24)>>buf cmp la numrul specificat de caractere cin ws; Elimin spaiile libere din fluxul de intrare Tabelul 16. Manipulatori de formatare a operaiilor I/O n C++

Toi aceti manipulatori au prototipul n fiierul antet iomanip.h. Pentru a utiliza manipulatorii setiosflags i resetiosflags trebuie cunoscui indicatorii de formatare din tabelul de mai jos. Nume indicator ios :: skipws ios :: left ios :: right ios :: scientific ios :: fixed ios :: dec ios :: hex ios :: oct ios :: uppercase ios :: showbase ios :: showpoint ios :: showpos ios :: unitbuf Ce efect are utilizarea indicatorului Elimin spaiile goale din intrare Aliniaz ieirea la stnga n interiorul limii cmpului Aliniaz ieirea la dreapta Folosete notaia tiinific pentru numerele n virgul mobil. Folosete notaia zecimal pentru numere n virgul mobil Folosete notaia zecimal pentru ntregi Folosete notaia hexazecimal pentru ntregi Folosete notaia octal pentru ntregi Folosete litere mari pentru ieire Indic baza sistemului de numeraie n cadrul ieirii (prefixul 0x pentru hexazecimal i prefixul 0 pentru octal Include un punct zecimal pentru ieiri n virgul mobil Include i semnul + la afiarea valorilor pozitive Golete toate fluxurile dup inserarea caracterlor ntr-un flux Tabelul 16. Indicatori de formatare a operaiilor I/O n C++

Notaia ios :: <Nume_indicator> este folosit pentru a identifica indicatorul ca pe un membru al clasei ios. Exemplificm cele spuse mai sus prin codul C++ de mai jos. #include<iostream.h> #include<iomanip.h> #include<conio.h> #include<stdio.h> void main() {
95

double nr; clrscr(); gotoxy(10,6); nr=7./3.; gotoxy(5,6); cout<<"Afisare numar in virgula mobila/ format implicit..."; gotoxy(10,7); cout<<nr; gotoxy(5,9); cout<<"Afisare numar in virgula mobila/ cu precizia specificata..."; gotoxy(10,10); cout<<setprecision(10)<<nr; gotoxy(5,12); cout<<"Afisare numar in virgula mobila/ format virgula fixa..."; gotoxy(10,13); cout.setf(ios::fixed); cout<<setprecision(10)<<nr; gotoxy(5,15); cout<<"Afisare numar in virgula mobila/ format virgula fixa..."; gotoxy(10,16); cout.setf(ios::scientific); cout<<setprecision(10)<<nr; gotoxy(5,18); cout<<"Afisare numar in virgula mobila/ format virgula fixa..."; gotoxy(10,19); cout.setf(ios::scientific|ios::showpos); cout<<setprecision(10)<<nr; getch(); } Fiiere utilizator n C++ Chiar dac abordarea operaiilor I/O din C++ formeaz un sistem integrat, operaiile cu fiiere (altele dect cele predefinite), sunt suficient de specializate pentru a fi necesar s la discutm separat. Pentru a efectua operaii I/O cu fiiere conform paradigmei C++, trebuie s includei n programul Dvs. fiierul antet FSTREAM.H. Acesta definete mai multe clase, printre care ifstream, ofstream i fstream. Aceste clase sunt derivate din istream i, respectiv, din ostream la care ne-am referit i mai sus. Deschiderea i nchiderea unui fiier Un fiier se deschide n C++ legndu-l de un flux. nainte de a putea s deschidei un fiier, trebuie, pentru nceput, s avei un flux. Exist trei tipuri de

96

fluxuri: de intrare, de ieire i de intrare/ieire. Pentru a crea un flux de intrare, trebuie s-l declarai ca fiind din clasa ifstream. Pentru a crea un flux de ieire, trebuie s-l declarai ca fiind din clasa ofstream. Fluxurile care efectuiaz att operaii de intrare ct i operaii de ieire, trebuie declarate ca fiind din clasa fstream. Odat declarat fluxul, o modalitate de a-i asocia un fiier extern o reprezint utilizarea funciei open() avnd prototipul: void open(const char *nume_fisier , int mod, int acces=filebuf::openprot); nume_fisier este un nume extern de fiier care poate include i specificarea cii de acces. Valoarea parametrului mod determin modul de deschidere a fiierului. Parametrul mod poate avea una sau mai multe din valorile prezentate n tabelul de mai jos. Nume mod ios::app ios::ate Operaie Adaug date n fiier Cnd se deschide pentru prima dat, opereaz poziionarea n fiier la sfritul fiierului (ate nseamn la sfrit) Deschide fiierul n mod binar, inhibnd interpretarea ios::binary caracterelor <CR> <LF> Deschide fiierul pentru citire ios::in Nu efectueaz deschiderea fiierului dac acesta nu exist ios::nocreate deja Dac fiierul exist, ncercarea de a-l deschide pentru ieire ios::noreplace eueaz, cu excepia cazului n care ios::app sau ios::ate sunt operate Deschide fiierul pentru scriere ios::out Trunchiaz fiierul dac el exist deja ios:trunc Tabelul 17. Valorile parametrului care stabilete modul de deschidere a unui fiier

Putei specifica mai mult de un mod de lucru pentru un fiier, folosind operatorul pe bii SAU cu modurile respective. De exemplu, pentru deschiderea unui fiier pentru ieire i poziionarea pointerului la sfritul lui se folosesc modurile ios::out i ios::ate astfel: ofstream oflux(o_fisier,ios::out | ios::ate); ceea ce ne arat al doilea procedeu de deschidere a unui fiier, utiliznd constructorul clasei ofstream sau, de ce nu, ifstream, dac este cazul. Pentru a nchide un fiier, folosii funcia membru close(). Aceast funcie nu preia nici un parametru i nu returneaz nici o valoare. De analizat utilizarea funciei close() n exemplele care vor urma.

97

Scrierea i citirea fiierelor text Sunt dou operaii foarte uoare, realizate apelnd la operatorii >> i << ntr-un mod asemntor operaiilor referitoare la consola sistemului, cu deosebirea c n loc s folosii cin i cout apelai la un flux legat de un fiier . Codul de mai jos arat cum poate fi afiat pe ecranul monitorului coninutul unui fiier text. #include <fstream.h> #include <stdlib.h> #include<conio.h> //Functia principala a programului citeste linia de comanda a programului void main(int argc,char *argv[]) { // Linia de comanda a programului trebuie sa contina doi parametri if (argc!=2) { cerr<<"Mod de utilizare : lisfis <nume fisier CPP>\n"; exit(0); } // Deschidere fisier text de nume specificat in argv[1] ifstream in(argv[1],ios::in); if (!in) { cerr<<"Fisierul nu poate fi deschis!"; exit(0); } char c; clrscr(); while (in.get(c)) { if (wherey()>20) { gotoxy(20,24); cout<<"Press any key to continue..."; getch(); clrscr(); } cout<<c; } in.close(); } I/O n fiiere de tip binar

98

Exist dou modaliti de a scrie i citi date binare ntr-un fiier. Prima modalitate se refer la utilizarea funciilor get() i put(). Aceste funcii sunt orientate pe octei, ceea ce nseamn c get() va citi un octet de date iar put() va scrie un octet de date. Funcia get() are mai multe forme; o prezentm, n continuare, mpreun cu omoloaga ei put(), pe cea mai des folosit: istream &get(char &ch); ostream &put(char ch); Funcia get() citete un singur caracter din streamul asociat i memoreaz valoarea sa n ch. De asemenea, se mai observ c funcia returneaz o referin ctre flux. Funcia put() scrie ch n flux i returneaz fluxului o referin. Ca un exemplu de utilizare a funciilor get() i put() prezentm codul de mai jos, care realizeaz copierea unui fiier n altul. Dac numele executabilului asociat acestui cod este copyf, atunci sintaxa de lansare n execuie a programului este: copyf <fisier_sursa> <fisier_destinatie> #include<stdlib.h> #include <fstream.h> void main(int argc, char *argv) { //Verific daca sunt suficiente argumente if(argc<3) { cerr<<"Mod de utilizare : filecopy <fisier sursa> <fisier destinatie>\n"; exit(0); } //Deschide fisierul de intrare si il conecteaza la fluxul ins ifstream ins(argv[1]); if(!ins) { cerr<<"Nu pot deschide fisierul sursa:"<<argv[1]; exit(1); } //Deschide fisierul de iesire si il conecteaza la fluxul outs ofstream outs(argv[2]); if(!outs) { cerr<<"Nu pot deschide fisierul sursa:"<<argv[2]; exit(1); } //Citeste din sursa si scrie in destinatie char c; while(ins.get(c) && outs) outs.put(c);

99

} A doua modalitate de a citi i scrie blocuri de date n binar este folosirea funciilor din C++ read() i write(). Prototipurile lor sunt: istream &read(unsigned char *buf, int numar); ostream &write(const unsigned char *buf, int numar); Funcia read() citete numar octei din fluxul asociat i l pune n buffer-ul indicat de buf . Funcia write() scrie n fluxul asociat numar octei citii din buffer-ul spre care indic buf. Detectarea EOF Putei s detectai sfritul fiierului folosind funcia membru eof() ,care are acest prototip: int eof(); Ea returneaz o valoare nenul cnd a fost atins sfritul fiierului; altfel, returneaz zero. Utilizarea funciei eof() i alte elemente legate de lucrul cu fiiere, prezentm n codul de mai jos, care realizeaz afiarea coninutului unui fiier att n hexazecimal ct i n cod ASCII, atunci cnd codul asociat este printabil. #include <fstream.h> #include <ctype.h> #include <iomanip.h> #include <stdlib.h> #include<conio.h> void main(int argc,char *argv[]) { if (argc!=2) { cerr<<"Mod de utilizare : lishex <nume fisier CPP>\n"; exit(0); } ifstream in(argv[1],ios::in|ios::binary); if (!in) { cerr<<"Fisierul nu poate fi deschis!"; exit(0); } register int i,j; int count=0; char c[16]; cout.setf(ios::uppercase); clrscr();

100

while(!in.eof()) { for(i=0;i<16 && !in.eof();i++) { in.get(c[i]); } if (i<16) i--; for(j=0;j<i;j++) cout<<setw(3)<<hex<<(int) c[j]; for(;j<16;j++) cout<<"\t"; for(j=0;j<i;j++) if(isprint(c[j])) cout<<c[j]; else cout<<"."; cout<<endl; count++; if(count==16) { count=0; cout<<"Press ENTER to continue!"; getch(); clrscr(); cout<<endl; } } in.close(); } Accesul aleator n fiiere n sistemul de I/O din C++, accesul aleator se efectueaz folosind funciile seekg() i seekp(). Formele lor cele mai uzuale sunt: istream &seekg(streamoff offset, seek_dir origine); ostream &seekp(streamoff offset, seek_dir origine); streamoff este un tip definit n IOSTREAM.H, capabil s conin cea mai mare valoare valid pe care o poate avea offset, iar seek_dir este o enumerare care are aceste valori: ios::beg ios::cur ios::end Sistemul de I/O din C++ opereaz cu doi pointeri asociai unui fiier. Unul este pointerul de get , care specific unde va aprea urmtoarea operaie de intrare n fiier. Cellalt este pointerul de put i specific unde va avea loc urmtoarea

101

operaie de ieire. Dup fiecare operaie de intrare sau ieire, pointerul corespunztor este avansat automat, secvenial. Dar, folosirea funciilor seekg() i seekp() permite un acces nesecvenial la datele din fiier. seekg() i seekp() deplaseaz pointerul de nregistrare cu offset octei fa de origine. Cu meniunea c lucrul cu fiiere n C++ are nenumrate alte faete pentru a cror prezentare nu dispunem de timpul i spaiul necesar, ncheiem aceast scurt excursie n problematica fiierelor. n fine, pentru curioi facem i precizarea c, nsui btrnul C are propria filozofie, extrem de puternic, n ceea ce privete lucrul cu fiiere.

102

3 Programare generic n C++


n programarea profesional apar nenumrate situaii n care reutilizarea codului presupune o soluie de un anumit tip pentru o problem dat. Situaia la care ne referim n aceast seciune este, potenial vorbind, urmtoarea: Ce putem face pentru a comprima codul surs n situaia n care structuri de date, diferite ca tip, suport prelucrri similare. Soluia acestei probleme de stil de programare o reprezint programarea generic. Exprimndu-ne n termenii limbajului C, o funcie generic definete un set general de operaii care vor fi aplicate unor tipuri de date diferite. Ca un exemplu, o soluie generic pentru modelarea unei stive este un pretext ideal pentru precizarea ideilor principale ale programrii generice. Altfel spus, dac dorim o stiv, n care, de la caz la caz, s putem pstra numere ntregi, numere reale sau iruri de caractere (deci tipuri de date diferite), operaiile fiind aceleai ( push() i pop() ), este clar c ne aflm n situaia n care avem nevoie de suport pentru scrierea de cod cu proprieti generice. Dac n Pascal programarea generic se baza pe tipuri procedurale i programarea la nivel de octet, n C++ exist suport evoluat pentru programare generic, sub forma abloanelor. Cu un ablon, n C++ se poate crea o funcie generic sau o clas generic. Funcii TEMPLATE O funcie template este o funcie ablon, avnd unul sau mai muli parametri formali de un tip generic. n funcie de nevoile de utilizare a acestei funcii, compilatorul genereaz funcii propriu-zise, nlocuind tipul generic cu un tip concret. Tipul concret poate fi orice tip fundamental, derivat sau clas predefinit. Considerm un exemplu. Fie funcia max(x,y) care returneaz valoarea maxim a argumentelor sale. Tipul variabilelor x i y trebuie, obligatoriu, specificat n momentul compilrii. Soluia clasic const n redefinirea (over-loading) funciei max pentru fiecare tip al argumentelor x i y (de observat i cele spuse la paragraful 2.2 relativ la funcii suprancrcate n C++). Trebuie, aadar, s definim mai multe versiuni ale funciei max. int max(int x, int y) { return (x>y) ? x : y; } float max(float x, float y) { return (x>y) ? x : y; }

103

Mecanismul template permite definirea o singur dat a ablonului de funcii, dup care se genereaz automat funciile propriu-zise n concordan cu necesitile de utilizare, dar ,evident, n faza de compilare. Sintaxa la specificare este: template <class Nume_tip_generic_1 [,class Nume_tip_generic_n]> Nume_ablon definiie_ablon De precizat urmtoarele: @Caracterele < i > fac parte din sintaxa obligatorie. @Lista de parametri formali ai unei funcii ablon trebuie s utilizeze toate tipurile de date generice. @n cazul funciilor template nu se fac conversii @Funcia care are acelai nume i acelai numr de parametri cu o funcie ablon se numete caz exceptat (Suprancrcarea explicit este prioritar). Sintaxa la utilizare este: Nume ablon (Expresie_1[, ,Expresie_n]); Prezentm, n continuare, definiia funciei ablon max , urmat de o secven client de utilizare. template <class T> T max(T x, T y) { return (x>y) ? x : y; } #include <conio.h> #include<iostream.h> //Definire sablon functie template<class T> T max(T x,T y) { return(x>y) ? x:y; } void main() { int i,j; float k,l;

104

clrscr(); i=10; j=2; k=13; l=-7; //Exemple de utilizare sablon gotoxy(20,10);cout<<"Apel max cu parametri variabili de tip float..."<<max(k,l); gotoxy(20,12);cout<<"Apel max cu parametri variabili de tip int ..."<<max(i,j); gotoxy(20,13);cout<<"Apel max cu parametri valoare float ..."<<max(13.,-7.); getch(); } Prezentm, totodat, un exemplu de funcie generic pentru compararea unor date dup valoarea unei chei ncapsulate n aceste date. #include <conio.h> #include <stdio.h> #include <string.h> #include <iostream.h> #include <ctype.h> //Definirea unei <functii generice> pentru compararea //unor date dupa valoarea unei chei incapsulate //in aceste date template <class T> int comp(T i1,T i2) { if (i1.key<i2.key) return -1; if (i1.key==i2.key) return 0; if (i1.key>i2.key) return 1; }; //Structura aleasa pentru exemplificare //cheia generica incapsulata este campul <key> struct tpers { char np[30]; int key; }; //Instantiere <struct tpers> struct tpers tam,pers[50]; void main() {

105

clrscr(); int i=0; //*************************************** //Citire persoane de la tastatura do { gotoxy(20,12); cout<<"Numele persoanei:";clreol(); cin>>pers[i].np; gotoxy(20,13); cout<<"Matricola:";clreol(); cin>>pers[i].key; gotoxy(20,14); cout<<"Mai aveti(D,N):"; i++; } //*************************************** //Listare persoane pe ecranul monitorului //in ordinea citirii de la tastatura while(toupper(getch())!='N'); clrscr(); cout<<"Listare persoane in ordinea citirii de la tastatura..."<<endl; cout<<"_______________________________________________"<<endl; for(int j=0;j<i;j++) { if (wherey()>10) { cout<<"______________________________________________"<<endl; cout<<"Pentru continuare apasati o tasta..."<<endl; getch(); clrscr(); cout<<"Listare persoane in ordinea citirii de la tastatura..."<<endl; cout<<"_______________________________________________"<<endl; }; cout.width(30);cout.setf(ios::left); cout<<pers[j].np<<" "<<pers[j].key<<endl; }; getch(); //************************************* //Sortare persoane int sortat; do { sortat=1;

106

for(int j=0;j<i-1;j++) { switch(comp(pers[j],pers[j+1])) { case 1: { tam=pers[j]; pers[j]=pers[j+1]; pers[j+1]=tam; sortat=0; }; }; }; } while(!sortat); //**************************************** //Listare persoane dupa sortare in ordinea //crescatoare a matricolelor clrscr(); cout<<"Listare persoane dupa sortare........................."<<endl; cout<<"____________________________________________"<<endl; for(int k=0;k<i;k++) { if (wherey()>10) { cout<<"_____________________________________________"<<endl; cout<<"Pentru continuare apasati o tasta..."<<endl; getch(); clrscr(); cout<<"Listare persoane dupa sortare........................."<<endl; cout<<"______________________________________________"<<endl; }; cout.width(30);cout.setf(ios::left); cout<<pers[k].np<<" "<<pers[k].key<<endl; }; getch(); } Clase TEMPLATE O clas template definete un ablon pe baza cruia se pot genera clase propriu-zise. Din acest motiv, o clas template se mai numete i clas generic , clas generator sau metaclas. Astfel c, o clas template devine o clas de clase, reprezentnd cel mai nalt nivel de abstractizare admis de programarea obiect orientat.

107

n cadrul clasei ablon se pot declara atribute informaionale de un tip ambiguu, care sunt particularizate n cadrul clasei generat pe baza ablonului. Evident, i n acest caz, generarea se face n faza de compilare n concordan cu cerinele clientului. Sintaxa la specificarea clasei: template <class T> class nume_clas { : }; Sintaxa la implementarea funciilor membre ale unei clase template: template <class T> Tip returnat nume_clasa <T>::nume_funcie() { : }; Pentru mai mult claritate prezentm i exemplul de mai jos. #include <iostream.h> #include <stdlib.h> #include <conio.h> const int SIZE = 10; //*************************************** //Definire clasa matrice generica template <class ATip> class genmat { ATip a[SIZE]; public: genmat(); ATip &operator [ ](int i); //Suprancrcare operator [ ] }; //*************************************** //Implementare constructor clasa generica template <class ATip> genmat<ATip>::genmat() { register int i; for(i=0;i<SIZE;i++) a[i]=i; };

108

//*************************************** //Implementare supraincarcare operator [ ] template <class ATip> ATip &genmat<ATip>::operator[ ](int i) { if(i<0 ||i>SIZE-1) { cerr<<"Valoare indice eronata..."; getch(); exit(1); }; return a[i]; }; //*************************************** //Functia principala void main() { genmat<int> intob; genmat<double> doubob; int i; clrscr(); cout<<"Matrice de intregi..."<<endl; for(i=0;i<SIZE;i++) intob[i]=i; for(i=0;i<SIZE;i++) cout<<intob[i]<<endl; getch(); clrscr(); cout<<"Matrice de reali dubla precizie..."<<endl; for(i=0;i<SIZE;i++) doubob[i]=(double)i/3; for(i=0;i<SIZE;i++) cout<<doubob[i]<<endl; getch(); clrscr(); intob[100]=100; }; Tot pentru exemplificare s considerm i o situaie deosebit de simpl. Ni se cere s construim o clas template corespunztoare conceptului de stiv, din care, ulterior, s se poat concretiza clase care simuleaz stiva pentru tipuri de date diferite. //Clasa sablon CSTack template <Class T>

109

class Cstack { T * v; //pointer la varful stivei T * p; //pointer la pozitia curenta din stiva int dim; //dimensiunea stivei public: Cstack(int sz) { v=p=new T [dim=sz]; //alocare dinamica de memorie pentru p i v } ~Cstack() { delete [ ] v; } void push(T a) { *p++=a; }; T pop() { return *-p } } Dup ce o astfel de clas template a fost declarat i definit, se poate trece deja la instanierea de obiecte. Singura deosebire fa de folosirea unei clase obinuite const n faptul c trebuie specificat tipul concret care nlocuiete tipul generic T. Ca un exemplu, s instaniem un obiect stiv (CStack) , n care ncap maximum 100 elemente de tip char. CStack<char> sc(100); n acest caz, compilatorul genereaz clasa ce rezult prin nlocuirea lui T cu char, dup care instaniaz obiectul sc. Constructorul acestuia primete ca argument valoarea 100. Pentru mai multe detalii urmrii exemplul de mai jos. #include <iostream.h> #include <conio.h> // Definirea clasei stack. Se vede c instanele ei nu sunt protejate fa de //excepii. //Despre excepii urmeaz s discutm template <class T> class CStack { T *v;
110

T *top; int dims; public: CStack( int sz) //constructor { v=top=new T[dims=sz]; } ~CStack() { delete [ ] v; } void push(T a) { *top++=a; } T pop() { return *--top; } }; //Functia principala void main() { int i; //Primul exemplu de instantiere a stivei generice ;numere ntregi CStack<int> st1(20); //ncarcare stiva for (i=0;i<=9;i++) { st1.push(i); } //Vizualizare continut stiva clrscr(); for (i=0;i<=9;i++) { gotoxy(35, wherey()+1); cout<<st1.pop()<<"*****"; } getch(); //destructor

//Functia de inserare in stiva //Notatie prescurtata echvalenta cu *top=a; top++

//Notatie prescurtata echibvalenta cu - -top; return *top

111

//Al doilea exemplu de instantiere a stivei generice; caractere, ncepand cu A CStack<char> st2(20); //ncarcare stiva for (i=65;i<75;i++) { st2.push((char) i); } //Vizualizare continut stiva clrscr(); for (i=0;i<10;i++) { gotoxy(35, wherey()+1); cout<<st2.pop()<<"*****"; } getch(); }

112

4 Tratarea excepiilor
Programatorii adevrai trebuie s ia n calcul i posibilitatea de a crea programe robuste, care fac fa att cerinelor specificate dar nerafinate suficient, ct i cerinelor nespecificate dar formulate de utilizator, din diverse motive. Programele care au aceste caliti se numesc robuste. n programarea clasic soluia acestei probleme se putea numi, destul de exact spus, programare defensiv. Seamn puin cu conducerea preventiv din oferie dac ne gndim c programnd defensiv, n fond punem rul nainte, deci nu ne bazm pe cumsecdenia i buna pregtire a utilizatorului. Pentru a face fa cerinelor legate de problema tratrii excepiilor (aa se numesc n jargon profesional erorile care apar n timpul execuiei programelor) anumite limbaje de programare ofer suport adecvat. A include aici limbaje precum Delphi, C++, Java, Visual C. Nu toate compilatoarele de C++ ofer suport, dar standardul ANSI C++ cere acest lucru n mod explicit. Compilatoarele din familia Borland incepnd cu versiunea 4.0 ofer acest suport. Esenialul din punctul de vedere al programatorului C++ este ca el s-i formeze abilitatea de a scrie n jurul aplicaiilor cod C++ care ndeplinete funcia de handler de excepii. Suportul sintactic C++ pentru tratarea excepiilor se rezum la trei cuvinte cheie, a cror semantic preliminar o prezentm n Tabelul xxxxx. Cuvntul cheie try Semnificaie Delimiteaz o poriune de cod n care se instituie controlul sistemului asupra excepiilor n timpul rulrii. Lanseaz o excepie de un anumit tip throw Capteaz o excepie lansat catch Tabelul 18 Cuvintele cheie ale limbajului C++ referitoare la tratarea excepiilor

Forma de baz a tratrii excepiilor


Aadar, atunci cnd programele dumneavoastr efectueaz prelucrarea excepiilor, trebuie s includei n cadrul unui bloc try instruciunile pe care dorii s le monitorizai n vederea unei excepii. Dac execuia unei instruciuni se termin cu o eroare, trebuie s lansai o eroare corespunztoare aciunii funciei n care se afl instruciunea. Programul plaseaz instruciunea throw n cadrul blocului try-catch. Forma generalizat a blocului care capteaz i trateaz erorile este: try{ //blocul try //if(eroare) throw valoare_excepie; } catch (Tip_excepie Nume_variabil ){ //Prelucrarea excepiei }

113

n cadrul acestei forme generalizate, valoarea valoare_excepie lansat trebuie s corespund tipului Tip_excepie .

Scrierea unui handler de excepii simplu


Pentru a nelege mai bine semantica unui handler de excepii, studiai programul de mai jos. #include <iostream.h> void main() { cout<<"Start"<<endl; try { cout<<"In interiorul blocului try"<<endl; throw 100; cout<<"Nu se va executa"; } catch (int i) { cout<<"Am captat o excepie --valoarea este:"; cout<<i <<endl; } cout<<"Sfarsit"; }; Programul de mai sus implementeaz un bloc try-catch simplu. n loc s se atepte ca programul s eueze datorit unei erori, se utilizeaz instruciunea throw pentru lansarea erorii prezumtive. Dup ce blocul try lanseaz eroarea, blocul catch o capteaz i prelucreaz valoarea transmis de instruciunea throw. Este evident i din acest exemplu c mecanismul try-throw-catch ofer suport pentru rezolvarea problemei tratrii excepiilor dar nu rezolv de la sine aceast problem. Altfel spus, tratarea corect a excepiilor unui program este o problem de atitudine ca proiectant i ca programator.

Lansarea excepiilor cu o funcie din cadrul blocului try


Atunci cnd programele apeleaz funcii din cadrul blocurilor try , C++ va transmite excepia aprut ntr-o astfel de funcie n afara funciei dac nu exist un bloc try n interiorul funciei. Exemplul de mai jos arat cum se petrec lucrurile ntr-o astfel de situaie. #include <iostream> void XHandler(int test) { cout<<"Inauntrul functiei valoarea:"<<test<<endl; if(test) throw test;

XHandler,

test

are

114

}; void main() { cout<<"Start:"<<endl; try { cout<<"Inauntrul blocului try"<<endl; XHandler(1); XHandler(2); XHandler(0); } catch(int i) { cout<<"Am captat o exceptie. Valoarea este:"; cout<<i<<endl; }; cout<<"Sfarsit"; };

Plasarea unui bloc try intr-o funcie


Am vzut cum apare un bloc try n funcia principal a unui program. C++ permite blocuri try i n alte funcii ale unui program diferite de funcia principal. Atunci cnd se plaseaz un bloc try ntr-o funcie C++ reiniializeaz blocul de fiecare dat cnd intrai se intr n acea funcie. Programul urmtor ilustreaz cele spuse. #include <iostream.h> void XHandler(int test) { try { if(test) throw test; } catch(int i) { cout<<"Am captat exceptia nr.: "<<i<<endl; } }; void main() { cout<<"Start: "<<endl; XHandler(1); XHandler(2); XHandler(0); XHandler(3); cout<< "Sfarsit";
115

}; Un comentariu pe marginea celor prezentate pn acum ar fi urmtorul: o instruciune catch se execut numai dac programul lanseaz o excepie n cadrul blocului try situat imediat nainte. n caz c o astfel de excepie nu se lanseaz blocul catch va fi ignorat.

Utilizarea mai multor instruciuni catch cu un singur bloc try


Pe msur ce tratrile excepiilor devin tot mai complexe, uneori este necesar i posibil ca un singur bloc try s lanseze excepii de mai multe tipuri. n cadrul programelor dumneavoastr putei construi un handler de excepii astfel nct s accepte captarea mai multor excepii. ntr-o astfel de situaie sintaxa general este: try { //instruciuni } catch (tip1) { //tratare excepie 1 } catch(tip2) { //tratare excepie 2 } : catch(tipn) { //tratare excepie n } Cu acest amendament sintactic deducem c instruciunile catch pot capta orice tip returnat, nu numai tipurile de baz acceptate de C++. Acest "fenomen" este ilustrat n codul de mai jos. #include <iostream.h> void XHandler(int test) { try { if(test==0) throw test; if(test==1) throw "Sir de caractere";

116

} catch(int i) { cout<<"Am captat exceptia #:"<<i<<endl; } catch(char *sir) { cout<<"Am captat exceptia de tip caractere:"<<sir<<endl; } catch(double d) { cout<<"Am captat exceptia #:"<<d<<endl; } }; void main() { XHandler(0); XHandler(1); XHandler(2); cout<<"Sfarsit"; };

if(test==2) throw 121.25;

sir

de

Blocuri catch generice (utilizarea operatorului puncte de suspensie)


Programele scrise de dumneavoastr pot capta excepii din cadrul mai multor blocuri try (de exemplu un bloc try care incapsuleaza mai multe functii care lanseaza exceptii diferite din blocuri try diferite sau s utilizeze mai multe instruciuni catch ntr-un singur bloc try. C++ permite, de asemenea, utilizarea operatorului puncte de suspensie () pentru a capta orice tip de eroare care apare ntr-un singur bloc try. Sintaxa care permite captarea tuturor erorilor care apar ntr-un bloc try este prezentat mai jos. try { //Instructiuni } catch() { //tratarea exceptiei }

117

Pentru exemplificare propun codul de mai jos. #include <iostream.h> void XHandler(int test) { try { if(test==0) throw test; if(test==1) throw 'a'; if(test==2) throw 121.25; } catch() { cout<<"Am captat o exceptie"<<endl; } }; void main() { cout<<"Start:"<<endl; XHandler(0); XHandler(1); XHandler(2); cout<<"Sfarsit"; }; Evident, prelucrrile din cadrul blocului catch generic trebuie s fie independente de tipul erorii. Mecanismul captrii excepiilor explicite poate fi combinat cu mecanismul excepiilor generice ca n exemplul de mai jos. #include <iostream.h> void XHandler(int test) { try { if(test==0) throw test; if(test==1) throw 'a'; if(test==2) throw 121.25; } catch(int i)

118

{ } catch() { cout<<"Am captat o exceptie generica"<<endl; } }; void main() { cout<<"Start:"<<endl; XHandler(0); XHandler(1); XHandler(2); cout<<"Sfarsit"; }; cout<<"Am captat o exceptie de tip intreg"<<endl;

Restrictionarea exceptiilor
Pe msur ce programele dumneavoastr devin mai complexe, ele vor apela frecvent funcii din cadrul unui bloc try. Atunci cnd programele dumneavoastr apeleaz funcii dintr-un bloc try, putei restriciona tipurile de excepii pe care funcia apelat le poate lansa. De asemenea putei preveni lansarea oricrei excepii dintr-o anumit funcie. Sintaxa pentru restricionare este: tip_returnat nume_functie(lista_arg) throw(lista_tipuri ) { //Cod functie } Sintaxa care inhib lansare oricrei excepii este: tip_returnat nume_functie(lista_arg) throw() { //Cod functie } Este bine s subliniem c atunci cnd declarai o funcie cu clauza throw ea poate s lanseze doar acele tipuri precizate n list. Dac funcia lanseaz orice al tip programul este abortat. Un exemplu n continuare.

119

#include <iostream.h> void XHandler(int test) throw(int, char, double) { if(test==0) throw test; if(test==1) throw 'a'; if(test==2) throw 121.25; } void main() { cout<<"Start:"<<endl; try { XHandler(0); } catch(int i) { cout<<"Am captat un intreg"<<endl; } catch(char c) { cout<<"Am captat un caracter"<<endl; } catch(double d) { cout<<"Am captat un double"<<endl; } cout<<"Sfarsit"; };

Relansarea unei excepii


n anumite situaii poate fi necesar s se relanseze o excepie din interiorul unui handler de excepii. Dac relansai o excepie, C++ o va transmite unui bloc try exterior dac acesta exist. Cea mai probabil situaie n care putei opta pentru aceast variant este atunci cnd dorii s tratai o excepie n cadrul a dou programe handler distincte. Pentru mai mult claritate urmrii exemplul de mai jos. #include <iostream.h> void XHandler(void) { try {
120

throw "Salve"; } catch(char *) { cout<<"Am captat char* in XHandler "<<endl; throw; } void main() { cout<<"Start"<<endl; try { XHandler(); } catch(char *) { cout<<"Am captat char * in main"<<endl; } cout<<"Sfarsit"; };

Mod de utilizare a excepiilor


Toate elementele prezentate au ncercat s demonstreze c C++ are o atitudine activ fa de problema tratrii excepiilor. Suportul oferit de C++ l ajut pe programator s defineasc un comportament al programului cnd se produc evenimente anormale sau neateptate. O idee mai pragmatic de utilizare a suportului C++ n situaii efective o putei desprinde din exemplul de mai jos. #include <iostream.h> void div (double a, double b) { try { if(!b) throw b; cout<<"a/b="<<a/b<<endl; } catch(double b) { cout<<"Nu se poate imparti la zero"<<endl; } } void main() { double i,j; do

121

{ cout<<Introduceti numaratorul (0 pentru stop):"<<endl; cin i; cout<<Introduceti numitorul :"<<endl; cin j; div(i,j); } while (i!=0); };

122

III Cteva aplicaii care au n spate C sau C++

123

1 Sortarea elementelor unui ir de numere reale prin metoda bulelor. Program pretext pentru modularizare. Se utilizeaz fiierul antet facilcrt.h prezentat n continuarea acestei seciuni. #include <stdio.h> #include <conio.h> #include <stdlib.h> #include"facilcrt.h" #include<string.h> #define _lms 100 #define _optiune "123" void prelsir(); char prelopt(); void sort(); int dims; float x[ _lms]; //Functia principala.. void main() { char op; do { op=prelopt(); switch (op) { case '1': prelsir(); break; case '2': sort(); break; } } while (op!='3'); } //Preluare optiuni program... char prelopt() { char c; do { textcolor(RED);

124

textbackground(CYAN); clrscr(); gotoxy(20,8) ;cprintf("Optiunile programului sunt:"); gotoxy(20,9) ;cprintf("1-Preluare elemente sir"); gotoxy(20,10);cprintf("2-Sortare/afisare sir"); gotoxy(20,11);cprintf("3-Terminare program"); makewin2(20,8,60,11); makewin2(20,14,60,14); gotoxy(20,14);cprintf("Optiunea Dvs.:"); c=getch(); } while (strchr(_optiune,c)==NULL); return c;

};

//Preluare elemente sir... void prelsir() { int i; clrscr(); makewin2(20,5,60,5); gotoxy(20,5); printf("Dimensiune sir..."); gotoxy(20+strlen("Dimensiune sir..."),5); scanf("%d",&dims); makewin2(20,8,60,7+dims); gotoxy(20,8); for (i=0;i<dims;i++) { gotoxy(20,wherey()); printf("Element[%d]=",i); scanf("%f",&x[i]); } }; //Determinare maxim... void sort() { float aux; int i,sortat; //Sortare elemnete sir do { sortat=1; for(i=0;i<dims-1;i++) {

125

if (x[i]>x[i+1]) { aux=x[i]; x[i]=x[i+1]; x[i+1]=aux; sortat=0; } } } while(!sortat); //Afisare sir sortat clrscr(); makewin2(20,5,60,5); gotoxy(20,5); printf("Sirul sortat este..."); makewin2(20,8,60,7+dims); gotoxy(20,7); for (i=0;i<dims;i++) { gotoxy(20,wherey()+1); printf("Element[%d]= %6.2f",i,x[i]); } getch(); };

126

2 Fier antet care ncapsuleaz cteva faciliti de lucru n mod text, utile pentru realizarea unor interfee agreabile ale programelor cu utilizatorii. Numit n exemplul precedent prin facilcrt.h. #include <conio.h> #include <stdio.h> #include <string.h> #define css 1 //Afisare text dinspre capete... void converg(int end,int linie,char *mesaj) { int i,j,ls,cs; ls=(end-strlen(mesaj))/2; cs=ls+strlen(mesaj)-1; for (i=ls,j=cs;i<=j;i++,j--) { gotoxy(i,linie); printf("%c",mesaj[i-ls]); delay(50); //prototipul in fisierul antet <dos.h> gotoxy(j,linie); printf("%c",mesaj[strlen(mesaj)-1-cs+j]); } getch();

//Activare video-invers void avideo() { textcolor(BLACK); textbackground(WHITE); } //dezactivare video-invers void dvideo() { textcolor(WHITE); textbackground(BLACK); } //Afisare centrata text in interiorul unei linii void acentext(int ls, int ld, int linia, char *sir) { int sw; int col; sw=(ls>=1) && (ls<=79) && (ld<=80) && (ld>=2) && (ls<ld);

127

sw=sw && ((ld-ls+1)>=strlen(sir)); if (sw) { col=ls+(ld-ls+1-strlen(sir))/2; gotoxy(col,linia); cprintf(sir); } } //Construire fereastra cu rama dreptunghiulara de //dimensiuni specificate void makewin2(int ass, int oss, int adj, int odj) { short int i; int sw; sw=(ass>0) && (ass<81) && (adj>0) && (adj<81) && (ass<=adj); sw=sw && (oss>0) && (oss<25) && (odj>0) && (odj<25) && (oss<=odj); if(sw) { for(i=ass;i<=adj;i++) { gotoxy(i,oss-1); printf("\315"); gotoxy(i,odj+1); printf("\315"); } for(i=oss;i<=odj;i++) { gotoxy(ass-1,i); printf("\272"); gotoxy(adj+1,i); printf("\272"); } gotoxy(ass-1,oss-1); printf("\311"); gotoxy(adj+1,oss-1); printf("\273"); gotoxy(ass-1,odj+1); printf("\310"); gotoxy(adj+1,odj+1); printf("\274"); } else { gotoxy(1,24);

128

} }

printf("Coordonate ecran eronate!!!"); getch();

129

3 Aplicaie pretext pentru lucru cu tablouri, folosind pointeri. Mai precis, codul prezentat mai jos rezolv urmtoarele probleme: -alocarea dinamic a memoriei pentru un tablou bidimensional cu numr de linii i coloane precizate; -citirea elementelor unui tablou bidimensional; -transpunerea unei matrice; -afiarea elementelor unei matrice; #include <conio.h> #include<iostream.h> int i,j; //Alocare dinamica a memoriei pentru o matrice de intregi //Numarul de linii si coloane sunt precizate prin lista de //parametri void almemmat(int **p,int nl,int nc) { for(i=0;i<nl;i++) p[i]=new int[nc]; }; //Citirea elementelor matricei de intregi de la tastatura void citmat(int **p,int nl,int nc) { for(i=0;i<nl;i++) for(j=0;j<nc;j++) { gotoxy(10,12); cout<<"Elementul["<<i<<","<<j<<"]="; cin>>p[i][j]; }; }; //Afisarea matricei de intregi void afismat(int **p,int nl,int nc) { for(i=0;i<nl;i++) { for(j=0;j<nc;j++) cout<<p[i][j]<<" | "; cout<<endl; }; } //Transpunerea matricei void transpun(int **p,int **q,int nl,int nc)

130

{ for(i=0;i<nl;i++) for(j=0;j<nc;j++) q[j][i]=p[i][j];

};

//Functia principala void main() { int **pmat; int **q; int nl=6,nc=2; clrscr(); almemmat(pmat,nl,nc); almemmat(q,nc,nl); citmat(pmat,nl,nc); transpun(pmat,q,nl,nc); afismat(q,nc,nl); getch(); };

131

4 Sortarea unui ir de numere ntregi prin metoda HEAP_SORT. Se utilizeaz reprezentarea liniar a unui arbore binar. #include<iostream.h> #include<conio.h> const int MAX_LIST_SIZE=100; typedef int element; typedef element list_type[MAX_LIST_SIZE]; typedef int boolean; void walk_down(list_type list, int j, int n) { int i,k; element ref; boolean found_spot=0; i=j; ref=list[i]; k=2*i+1; while((k<n) && !found_spot) { if(k<n-1) if(list[k+1]>list[k]) ++k; if (list[k]>ref) { list[i]=list[k]; i=k; k=2*i+1; } else found_spot=1; } list[i]=ref; } void heap_sort(list_type list, int n) { int y; element temp; y=n/2-1; while (y>=0) { walk_down(list,y,n); --y; } y=n;

132

while(y>0) { temp=list[0]; list[0]=list[y-1]; list[y-1]=temp; --y; walk_down(list,0,y); } } void main() { list_type list; int i,nrel; clrscr(); gotoxy(20,10); cout<<"Numar de elemente in lista:"; cin>>nrel; clrscr(); for(i=0;i<nrel;i++) { gotoxy(20,11); clreol(); cout<<"Elementul[ "<<i<<" ]="; cin>>list[i]; } heap_sort(list,nrel); clrscr(); for(i=0;i<nrel;i++) cout<<list[i]<<endl; getch(); }

133

5 Rasfoirea unui fiier text i contorizarea apariiilor unui caracter ntr-un fisier text utiliznd fluxuri binare. #include <fstream.h> #include <stdlib.h> #include "facilcrt.h" char optiuni[]="1234"; char *nf; int sfs; //Semnatura functie prelopt() char prelopt(); //Semnatura functie selnumef() char *selnumef(); //Semnatura functie rasfft() void rasfft(char *n); //Semnatura functie detstatcar() void detstatcar(); //Implementare functie principala void main() { sfs=0; do { switch(prelopt()) { case '1': nf=selnumef(); break; case '2': rasfft(nf); break; case '3': detstatcar(); break; case '4': exit(0); } } while (2>1); } //Implementare functie detstatcar() void detstatcar() { char c;

134

int cont; clrscr(); gotoxy(20,10); cout<<"Caracterul:"; makewin2(20,10,60,11); gotoxy(20+strlen("Caracterul:"),10); cin>>c; if(sfs) { ifstream in(nf,ios::in | ios::binary); if(!in) { sfs=0; gotoxy(1,24); textcolor(RED+BLINK); cprintf("Fisierul indicat nu poate fi deschis..."); getch(); sfs=0; textcolor(WHITE); } else { char ch; cont=0; while(in) { in.get(ch); if(ch==c) cont++; } gotoxy(20,11); cout<<"Numar de aparitii:"<<cont; getch(); } } else { gotoxy(1,24); textcolor(RED+BLINK); cprintf("Selectati mai intai fisierul..."); getch(); textcolor(WHITE); }

//Implementare functie selnumef() char *selnumef()

135

{ char *n; sfs=1; clrscr(); gotoxy(20,10); cout<<"Numele fisierului de rasfoit:"; makewin2(15,10,65,10); do { strcpy(n,""); gotoxy(20+strlen("Numele fisierului de rasfoit:"),10); gets(n); } while(strlen(n)==0); return n;

//Implementare functie rasfft() void rasfft(char *n) { if(sfs) { //Deschid fluxul binar in in citire ifstream in(n, ios::in | ios::binary); if(!in) { sfs=0; gotoxy(1,24); textcolor(RED+BLINK); cprintf("Fisierul indicat nu poate fi deschis..."); getch(); sfs=0; textcolor(WHITE); } else { clrscr(); char ch; while(in) { //Citesc un caracter din flux in.get(ch); if (wherey()>20) { gotoxy(1,24); cout<<"Pentru continuare apasati o tasta..."; getch();

136

clrscr(); } cout<<ch; } getch(); } } else { gotoxy(1,24); textcolor(RED+BLINK); cprintf("Selectati mai intai fisierul..."); getch(); textcolor(WHITE); }

//Implementare functie prelopt() char prelopt() { clrscr(); gotoxy(20,8); cout<<"Optiunile programului..."; makewin2(20,8,60,8); gotoxy(20,11); cout<<"1-Selectare fisier text"; gotoxy(20,12); cout<<"2-Rasfoire fisier text"; gotoxy(20,13); cout<<"3-Contorizare aparitii caracter specificat"; gotoxy(20,14); cout<<"4-Terminare program"; gotoxy(20,15); cout<<" Alegerea Dvs.:"; makewin2(20,11,60,15); gotoxy(20+strlen(" Alegerea Dvs.:"),15); char op; do op=getch(); while (strchr(optiuni,op)==NULL); return op; }

137

6 Modelarea C a unor faciliti de lucru cu memoria video n mod text. Exemplul de cod prezentat mai jos ilustreaz, n alt context, utilitatea pointerilor, precum i o serie de elemente tehnice referitoare la scrierea/citirea n/din memoria video, atunci cnd se lucreaz n mod text. #ifndef _stdio_h #include<stdio.h> #define _stdio_h #endif #ifndef _conio_h #include<conio.h> #define _conio_h #endif #ifndef _string_h #include<string.h> #define _string_h #endif #define startcol1 16 #define startcol2 36 #define startcol3 56 #include<dos.h> #include<iostream.h> #include<stdlib.h> //Variabila in care se pastreaza adresa memoriei video char far *mem_vid; int afcent(int lin,char s[80]); void codasc(); void antet(); int mod_video(); void setvptr(); void scrie_car(int x,int y,char ch,int atrib); void scrie_sir(int x,int y,char *p,int atrib); //Functia returneaza modul video corespunzator //echipamentului pe care se ruleaza programul int mod_video() { union REGS r; r.h.ah=15; return int86(0x10,&r,&r)&255; }

138

//Functia seteaza adresa memoriei video in functie de //modul video curent void setvptr() { int vmod; vmod=mod_video(); if((vmod!=2)&&(vmod!=3)&&(vmod!=7)) { cout<<"Video trebuie sa fie in modul text cu 80 de coloane."; exit(1); } if(vmod==7) mem_vid=(char far *) 0xB0000000; else mem_vid=(char far *)0xB8000000; } //Functia permite scrierea unui caracter direct //in memoria video pe linie si coloana specificate void scrie_car(int x,int y,char ch,int atrib) { char far *v; v=mem_vid; v+=(y*160)+(x-1)*2; *v++=ch; *v=atrib; } //Functia permite scrierea unui sir de caractere //in memoria video pe linie si coloana specificata void scrie_sir(int x,int y,char *p,int atrib) { register int i; char far *v; v=mem_vid; v+=(y*160)+(x-1)*2; for(i=y;*p;i++) { *v++=*p++; *v++=atrib; } } //Afisarea antetului tabelei de coduri ASCII void antet() {

139

gotoxy(1,1); for(int i=0;i<78;i++) printf("\xf"); gotoxy(16,2);printf("Cod gotoxy(36,2);printf("Cod gotoxy(56,2);printf("Cod for(i=0;i<78;i++) printf("\xf"); }

Caracter"); Caracter"); Caracter\n");

//Afisare coduri ASCII pe trei coloane void codasc() { int nrcan=2; int i,j,cc; int lin=1; int col=1; clrscr(); antet(); for(i=0;i<256;i++) { if(lin>19) if(col==3) { gotoxy(1,24); gotoxy(1,23); for(j=0;j<78;j++) printf("\xf"); printf("\nESC-pentru terminare\\Alta tasta pentru continuare..."); if(getch()=='\033') return; col=lin=1; clrscr(); antet(); } else { lin=1; col++; } switch(col) { case 1: { cc=startcol1; break; }

140

case 2: { cc=startcol2; break; } case 3: { cc=startcol3; break; }; } lin++; gotoxy(cc,nrcan+lin);printf("%i",i); gotoxy(cc+7,nrcan+lin);printf("%c",(char)i); }; gotoxy(1,23); for(j=0;j<78;j++) printf("\xf"); gotoxy(1,24); printf("Pentru terminare apasati o tasta...."); getch(); } //Afisare centrata text pe o linie specificata int afcent(int lin,char s[80]) { int col; int parer=0; if((lin<1)||(lin>24)) parer++; if(!strlen(s)) parer=parer+2; if(parer) return parer; col=(80-strlen(s))/2; gotoxy(col,lin); printf("%s",s); return parer; }

141

7 Modelarea C++ a operaiilor cu iruri de caractere. Problem pretext pentru aplicarea corect a principiului ncapsulrii, pentru utilizarea n funcie de cerine a constructorilor n specificarea comportamentului unei clase, pentru redefinirea unor operatori uzuali n lucru cu iruri de caractere (concatenarea polimorfic, adresarea unui caracter al tabloului de caractere, etc.). #include <stdio.h> #include<string.h> //***************************************************** //Clasa modeleaza lucrul cu siruri de caractere in C++ //class string { int lsir; //lungimea sirului char *psir; //tablou de caractere alocat dinamic public: //Constructor clasic; stringul contine sirul vid string(int l=1); //Constructor de copiere a unui obiect deja creat string( string& x); //Constructor de copiere care utilizeaza un sir //obisnuit string(char sir[]); //Redefinire operator atribuire pentru copiere obiect void operator=(string x); //Redefinire operator atribuire pentru copiere sir void operator=(char sir[]); //Redefinire operator de testare egalitate intre doua //siruri int operator==(string & x); //Redefinire operator de comparare > int operator>(string & x); //Redefinire operator de comparare < int operator<(string & x); //Concatenarea a doua siruri

142

string operator+(string & x); //Redefinire operator + ca functie prietena friend string operator+(string &x, char s[]); //Redefinire operator + ca functie prietena friend string operator+(char s[],string &x); //Destructor clasa ~string(); //Returnare lungime sir ca functie prietena friend int length(string &x); //Extragere caracter din sir char & operator[](int i); //Extragere sir din obiect ca functie prietena friend char * sir(string& x); }; //***************************************************** //Implementare functii membre //Implementare constructor creare sir vid string::string(int) { lsir=1; psir=new char[1]; *psir=0; } //Implementare constructor de copiere obiect deja creat string::string( string & x) { lsir=x.lsir; psir=new char[lsir]; strcpy(psir,x.psir); } //**************************************************** //Implementare constructor de copiere sir obisnuit string::string(char sir[]) { lsir=strlen(sir)+1; psir=new char[lsir]; strcpy(psir,sir); }

143

//Implementare redefinire operator atribuire obiect void string::operator=(string x) { delete psir; lsir=x.lsir; psir=new char[lsir]; strcpy(psir,x.psir); } //Implementare redefinire operator atribuire pentru copiere //sir void string::operator=(char sir[]) { delete psir; lsir=strlen(sir)+1; psir=new char[lsir]; strcpy(psir,sir); } //Implementare redefinire operator de testare egalitate //siruri int string::operator==(string & x) { if(!strcmp(psir,x.psir)) return 1; else return 0; } //Implementare redefinire operator de comparare > int string::operator>(string & x) { if (strcmp(psir,x.psir)>0) return 1; else return 0; } //Implementare redefinire operator de comparare < int string::operator<(string & x) { if (strcmp(psir,x.psir)<0) return 1; else return 0; };

144

//Implementare concatenare a doua siruri string string::operator+(string & x) { string s; strcat(s.psir,psir); strcat(s.psir,x.psir); s.lsir=lsir+x.lsir-1; return s; } //Implementare destructor string::~string() { delete psir; } //Implementare redefinire operator [] char &string::operator[](int i) { return psir[i-1]; } //implementare extragere sir ca functie prietena char * sir(string &x) { return x.psir; }; //Implementare redefinire operator de concatenare //Varianta obiect+sir string operator+(string &x, char s[]) { string a; strcat(a.psir,x.psir); strcat(a.psir,s); a.lsir=x.lsir+strlen(s); return a; }; //Implementare redefinire operator de concatenare //Varianta sir+obiect string operator+(char s[],string &x) { string a; strcat(a.psir,s); strcat(a.psir,x.psir); a.lsir=x.lsir+strlen(s); return a;

145

};

146

8 Aplicatie care ilustreaza lucrul cu fisiere cu tip in C++ Din nou o problem pretext, care se refer la actualizarea unui fiier n care se pstreaz ntrebrile unui test i rspunsurile corecte, presupunnd c o ntrebare are mai multe alternative de rspuns. #include <conio.h> #include <stdio.h> #include <iostream.h> #include <ctype.h> #include<stdlib.h> #include <string.h> #include <fstream.h> char optiuni[]="1234567"; char *nf; int sfs,nri; //Tipul inregistrare typedef struct struct_test { int nr_intrebare; int alt_corecta; } t_inr_fis; //Variabila inregistrare t_inr_fis inr; //Functia permite preluarea optiunilor programului char prelopt(); //Functia permite selectarea numelui fisierului de lucru char *selnumef(); //Functia permite crearea fisierului void crefis(); //Functia permite adaugarea de inregistrari in fisier void adinfis(); //Functia permite steregerea unei inregistrari din fisier void steinfis(int nrint); //Functia permite vizualizarea inregistrarilor fisierului void vizfis(); void selin(int ni); //Implementare functie principala

147

void main() { char op; sfs=0; do { op=prelopt(); switch(op) { case '1': nf=selnumef(); break; case '2': crefis(); break; case '3': adinfis(); break; case '4': { clrscr(); gotoxy(10,12); cout<<"Numar intrebare inregistrare de sters:"; cin>>nri; steinfis(nri); break; }; case '5': vizfis(); break; case '6': { clrscr(); gotoxy(10,12); cout<<"Numar inregistrare de selectat:"; cin>>nri; selin(nri); break; }; case '7': break; } } while (op!='7'); }; void selin(int ni) { if (sfs) { ifstream ftestin; ftestin.open(nf,ios::in|ios::binary); ftestin.seekg((ni-1)*sizeof(t_inr_fis),ios::beg);

148

} else { gotoxy(1,24); cout<<"Selectati sau creati mai intai un fisier!...."; getch(); } }; void steinfis(int nrint) { ifstream ftestin; ofstream fmanout; if(!sfs) { cerr<<"Fisier neselectat...."; getch(); } else { fmanout.open("man",ios::out|ios::binary); clrscr(); ftestin.open(nf,ios::in|ios::binary); while (!ftestin.eof()) { ftestin.read((unsigned char*)&inr,sizeof(t_inr_fis)); if ((inr.nr_intrebare!=nrint)&&(!ftestin.eof())) fmanout.write((unsigned char*) &inr, sizeof(t_inr_fis)); }; fmanout.close();ftestin.close(); }; };

ftestin.read((unsigned char*) &inr,sizeof(t_inr_fis)); if(!ftestin.eof()) { clrscr(); gotoxy(10,12); cout<<"Nr. intrebare :"; cout<<inr.nr_intrebare; gotoxy(10,13); cout<<"Alternativa corecta:"; cout<<inr.alt_corecta; getch(); ftestin.close(); };

149

//Implementare crefis() void crefis() { ofstream ftestout; clrscr(); gotoxy(20,10); cout<<"Nume fisier de creat:"; cin>>nf; ftestout.open(nf,ios::out|ios::binary); sfs=1; char ras; do { clrscr(); gotoxy(20,10);cout<<"Numar intrebare:"; cin>>inr.nr_intrebare; gotoxy(20,11);cout<<"Raspuns corect :"; cin>>inr.alt_corecta; ftestout.write((unsigned char *) &inr, sizeof(t_inr_fis)); gotoxy(20,12);cout<<"Mai aveti de introdus(D,N):"; ras=getch(); } while (!(toupper(ras)=='N')); ftestout.close(); } //Implementare adinfis() void adinfis() { if (sfs) { ofstream ftestout; clrscr(); gotoxy(20,10); ftestout.open(nf,ios::app|ios::binary); char ras; do { clrscr(); gotoxy(20,10);cout<<"Numar intrebare:"; cin>>inr.nr_intrebare; gotoxy(20,11);cout<<"Raspuns corect :"; cin>>inr.alt_corecta; ftestout.write((unsigned char *) &inr, sizeof(t_inr_fis));

150

} while (!(toupper(ras)=='N')); ftestout.close();

gotoxy(20,12);cout<<"Mai aveti de introdus(D,N):"; ras=getch();

} else { gotoxy(1,24); cout<<"Selectati sau creati mai intai un fisier!...."; getch(); } } //Implementare vizfis() void vizfis() { if (sfs) { ifstream ftestin; ftestin.open(nf,ios::in|ios::binary); while (!ftestin.eof()) { clrscr(); ftestin.read((unsigned char*)&inr,sizeof(t_inr_fis)); if(!ftestin.eof()) { gotoxy(10,12); cout<<"Nr. intrebare :"; cout<<inr.nr_intrebare; gotoxy(10,13); cout<<"Alternativa corecta:"; cout<<inr.alt_corecta; getch(); }; }; ftestin.close(); } else { gotoxy(1,24); cout<<"Selectati sau creati mai intai un fisier!...."; getch(); } }

151

//Implementare selnumef() char *selnumef() { char *n; sfs=1; clrscr(); gotoxy(20,10); cout<<"Numele fisierului de lucru:"; do { strcpy(n,""); gotoxy(20+strlen("Numele fisierului de lucru:"),10); gets(n); } while(strlen(n)==0); return n; } //Implementare prelopt() char prelopt() { clrscr(); gotoxy(20,8); cout<<"Optiunile programului..."; gotoxy(20,9); cout<<"__________________________________________"; gotoxy(20,11); cout<<"1-Selectare fisier structura test"; gotoxy(20,12); cout<<"2-Creare fisier structura test"; gotoxy(20,13); cout<<"3-Adaugare date in fisierul structura test"; gotoxy(20,14); cout<<"4-Stergerea unei inregistrari din fisier"; gotoxy(20,15); cout<<"5-Vizualizare date fisier structura"; gotoxy(20,16); cout<<"6-Afisare inregistrare de numar specificat"; gotoxy(20,17); cout<<"7-Terminare program"; gotoxy(20,19); cout<<"__________________________________________"; gotoxy(20,20); cout<<" Alegerea Dvs.:"; gotoxy(20+strlen(" Alegerea Dvs.:"),20); char op;

152

};

do op=getch(); while (strchr(optiuni,op)==NULL); return op;

153

BIBLIOGRAFIE MINIMAL
[1]. N. Barkakati Borland C++ 4, Ghidul programatorului, Editura Teora, 1997 [2]. K., Jamsa, L. Klander Totul despre C i C++, Editura Teora, 2000 [3]. I. Mulea C++ pentru avansai, Editura Microinformatica, 1994 [4]. L. Negrescu Limbajele C i C++ pentru nceptori, Limbajul C++, volumul 2, Editura Albastr, Cluj-Napoca, 2000 [5]. L. Negrescu Limbajele C i C++ pentru nceptori, Limbajul C, volumul 1, Editura Albastr, Cluj-Napoca, 2000 [6]. D. M. Popovici, C++. Tehnologia orientat pe obiecte. I. M. Popovici, Aplicaii, I. Tnase Teora, 1996 [7]. Herbert Schildt [8]. B.Silaghi C++. Manual complet, Teora, 1998 Din tainele programrii n C++, Editura Albastr, 1996

Lista crilor din care se poate nva cte ceva despre C++ si lumea POO este deschis, la fel de deschis ca mintea oricrui student care nu i-a greit culoarul pe care alearg pentru a deveni un profesionist.

154

Subiecte pentru examenul la disciplina Programare Obiect Orientat, Anul II INFO


FUNDAMENTE Locul limbajului C/C++ printre celelalte limbaje de programare. Elemente caracteristice. Tipuri de date fundamentale. Modificarea tipurilor de date fundamentale. Variabile n C/C++. Variabile locale. Variabile globale. Modelatori de acces. Specificatori de clase de stocare. Operatori n C/C++. Reprezentarea structurilor de prelucrare n C/C++. Structuri secveniale. Reprezentarea structurilor de prelucrare n C/C++. Structuri alternative. Reprezentarea structurilor de prelucrare n C/C++. Structuri repetitive. Tablouri C/C++. Pointeri. memoriei. Relaia pointeri-matrice. Alocarea dinamic a

Structuri de date n programarea C/C++. nregistrri i cmpuri de bii.

155

Uniuni. PARADIGMA POO Concepte n programarea obiect orientat. Principii n programarea obiect orientat. Implementarea conceptelor i principiilor OO n C++. Constructori, destructori, crearea obiectelor n C++. Metodele virtuale i polimorfismul n programarea OO n C++. PROGRAMARE AVANSAT N C/C++ Suprancrcarea funciior n programarea C/C++. Suprancrcarea operatorilor n programarea C++. Funcii inline. Funcii prietene. Fluxuri C/C++. Programarea generic n C/C++. Funcii generice. Programarea generic n C/C++. Clase generice. Programarea excepiilor n C/C++.

156

Scurte consideraii relativ la problema evalurii la disciplina POO

 Exemplu de formulare a subiectelor pentru


examenul scris la disciplina POO II INFO
Suprancrcarea operatorilor n programarea C++. Ce este o clas abstract? Definii noiunea de flux. Tratarea excepiilor n C++. Definii o clas care ncapsuleaz proprietile necesare pentru
a simula operaii cu iruri de caractere. Implementai trei metode, dintre care una s fie inline.

 De citit, cel puin, cu rbdare


Cteva cuvinte despre felul n care vede autorul prezentului suport de curs ntlnirea cu studentul n timpul sesiunii. Indiferent de forma de evaluare, ntlnirea student- profesor, n sesiune, ar trebui s fie reciproc avantajoas. Profesorul are de ctigat multe, dac face efortul de a vedea n student nu doar un subiect al examenului, ci un partener care s-a hotrt s se confrunte cu el nsui. Studentul trebuie s aib nelepciunea de a vedea n profesor, n cel mai ru caz, un sftuitor care nu-i cunoate limitele. Autorul prezentului suport de curs i cunoate limitele i ncearc permanent s i le depeasc. Acest exerciiu de autodepire i-l poate asuma i studentul, cu ncredinarea c o

157

face pentru a confirma superioritatea omului n raport cu alte vieuitoare i nu numai. Pentru a m face mai bine neles, n cele ce urmeaz voi arunca cteva idei care ar vrea s sugereze modul n care, ca profesor, neleg s evaluez lucrarea unui student. ...Lucrarea unui student este o mic oper care vorbete despre limitele autorului ei n momentul n care acesta a realizat-o. Limitele acestei opere nu se refer, exclusiv, la ortografie, ci i la: Modul n care este structurat discursul de rspuns la fiecare subiect n parte. Calitatea viziunii punctuale a studentului, pentru fiecare subiect n parte. Calitatea viziunii sistemice a studentului; conexiunile ntre subiect i restul universului nu fac dect s arate, odat n plus, inteligena acestuia. Aadar, ideea este c, pentru un profesror normal, fiecare student este un potenial creator de bijuterii n materie de cugetare pe teme impuse, condiie necesar pentru a realiza n viitor bijuterii n materie de cugetare i de alt natur, pe teme liber alese sau impuse de viitoarea profesie.

158

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