pg 003-085 cap3a1aedituradp.ro/site_img/downloads/2016/05/pagini-download-info-xii-m3.pdf · Un...

37

Transcript of pg 003-085 cap3a1aedituradp.ro/site_img/downloads/2016/05/pagini-download-info-xii-m3.pdf · Un...

Informatică 221

Cuprins 3. Programare orientată pe obiecte şi programare vizuală ...........................................................................3 3.1. Programarea orientată pe obiecte......................................................................................................3 3.1.1. Principiile programării orientate pe obiecte ...............................................................................6 3.1.2. Proiectarea aplicaţiei în programarea orientată pe obiecte ......................................................8 3.1.3. Structura unei aplicaţii orientate pe obiecte ........................................................................... 10 3.1.4. Clase şi obiecte ...................................................................................................................... 11 3.1.4.1. Definirea claselor şi a obiectelor ............................................................................... 11 3.1.4.2. Supraîncărcarea funcţiilor ......................................................................................... 17 3.1.4.3. Crearea şi distrugerea obiectelor .............................................................................. 18 3.1.4.4. Supraîncărcarea operatorilor..................................................................................... 22 3.1.4.5. Funcţii prietene.......................................................................................................... 27 3.1.4.6. Membrii statici ai unei clase ...................................................................................... 31 3.1.4.7. Modificatorul const..................................................................................................... 36 3.1.5. Clase şi funcţii şablon............................................................................................................. 38 3.1.5.1. Necesitatea utilizării claselor şi a funcţiilor şablon ..................................................... 38 3.1.5.2. Declararea şi utilizarea unei funcţii şablon................................................................. 39 3.1.5.3. Declararea şi utilizarea unei clase şablon.................................................................. 43 3.1.5.4. Biblioteca de clase şablon STL .................................................................................. 46 3.1.6. Derivarea claselor................................................................................................................... 53 3.1.6.1. Moştenirea simplă şi moştenirea multiplă .................................................................. 53 3.1.6.2. Declararea unei clase derivate................................................................................... 54 3.1.6.3. Drepturi de acces în clase derivate............................................................................ 57 3.1.6.4. Constructori şi destructori în clase derivate ............................................................... 61 3.1.7. Tratarea erorilor....................................................................................................................... 65 3.1.7.1. Generarea erorilor ...................................................................................................... 65 3.1.7.2. Tratarea şi propagarea erorilor................................................................................... 66 3.1.7.3. Ierarhia claselor de erori............................................................................................. 72 3.1.8. Polimorfism.............................................................................................................................. 76 3.1.8.1. Funcţii virtuale ............................................................................................................ 76 3.1.8.2. Clase abstracte şi funcţii virtuale pure ....................................................................... 81 3.2. Programarea vizuală ....................................................................................................................... 86 3.2.1. Concepte de bază ale programării vizuale............................................................................. 87 3.2.2. Mediul de programare vizuală C# .......................................................................................... 89 3.2.2.1. Diferenţe faţă de limbajul C++.................................................................................... 91 3.2.2.2. Structura unei aplicaţii .............................................................................................. 100 3.2.2.3. Interfaţa aplicaţiei ..................................................................................................... 104 3.2.3. Elementele programării orientate pe obiecte în context vizual ............................................ 117 3.2.4. Construirea interfeţei utilizator.............................................................................................. 126 3.2.4.1. Interfaţa .................................................................................................................... 126 3.2.4.2. Proiectarea interfeţei ................................................................................................ 134 3.2.4.3. Ferestrele ................................................................................................................. 135 3.2.4.2. Controale pentru executarea acţiunilor .................................................................... 151 3.2.4.3. Controale pentru introducerea şi afişarea datelor.................................................... 159 3.2.4.3. Controale care permit utillizatorului să aleagă ......................................................... 175 3.2.4.3. Meniurile ................................................................................................................... 187 3.2.4.4. Bara cu instrumente ................................................................................................. 196 3.2.5. Accesarea şi prelucrarea datelor.......................................................................................... 200 3.2.5.1. Administrarea datelor din baza de date ................................................................... 200 3.2.5.2. Grila .......................................................................................................................... 203 3.2.5.3. Interogarea datelor din baza de date ....................................................................... 211

3. Programare orientată pe obiecte şi programare vizuală

3.1. Programarea orientată pe obiecte

Generale Specifice programării orientate pe obiecte Identificarea datelor care intervin într-o problemă şi aplicarea algoritmilor fundamentali de prelucrare a acestora

Analizarea unei probleme în scopul identificării şi clasificării datelor ne-cesare

Analizarea problemei care trebuie rezolvată, identificarea sarcinilor aplicaţiei şi a datelor care vor fi prelucrate, şi alegerea claselor adecvate pentru realizarea fiecărei sarcini.

Identificarea relaţiilor dintre date Identificarea relaţiilor care există între datele aplicaţiei şi, pe baza acestor relaţii, stabilirea grupurilor de date care vor face parte din aceeaşi clasă.

Identificarea modalităţilor adecvate de structurare a datelor care inter-vin într-o problemă

Identificarea relaţiilor care există între clasele unei aplicaţii şi stabilirea ierarhiei de clase a aplicaţiei.

Utilizarea funcţiilor specifice de pre-lucrare a datelor structurate

Folosirea metodelor pentru a prelucra datele unei aplicaţii.

Elaborarea algoritmilor de rezolvare a problemelor

Identificarea tehnicilor de programare adecvate rezolvării unei probleme şi aplicarea creativă a acestora

Identificarea aplicaţiilor care necesită folosirea metodei programării orientate pe obiecte.

Elaborarea strategiei de rezolvare a unei probleme

Structurarea aplicaţiei pornind de la sarcinile pe care trebuie să le rezolve. Identificarea obiectelor care vor rezolva sarcinile aplicaţiei, iar, pentru fiecare obiect, a datelor şi metodelor pe care le va conţine.

Analizarea comparativă a eficienţei diferitelor tehnici de rezolvare a pro-blemei respective şi alegerea celei mai eficiente variante

Analizarea rezolvării unei probleme folosind o aplicaţie realizată prin metoda programării structurate şi o aplicaţie realizată prin metoda programării orientate pe obiecte. Stabilirea metodei celei mai eficiente.

Implementarea algoritmilor într-un limbaj de programare

Utilizarea instrumentelor de dezvol-tare a unei aplicaţii

Utilizarea instrumentelor specifice programării orientate pe obiecte, folosind limbajul C++.

Elaborarea şi realizarea unei apli-caţii, folosind un mediu de progra-mare specific

Construirea unei aplicaţii orientate pe obiecte folosind limbajul C++.

Prezentarea unei aplicaţii Prezentarea unei aplicaţii orientate pe obiecte pornind de la sarcinile pe care trebuie să le realizeze.

4 Programare orientată pe obiecte şi programare vizuală

În programarea clasică rezolvarea unei probleme se face cu ajutorul algoritmilor descrişi prin cele trei structuri de control: structura secvenţială, structura alternativă şi structura repetitivă. Această metodă de programare este orientată pe prelucrarea datelor, iar progra-matorul trebuie să construiască un program care este format din ansamblul de date şi algoritmul folosit pentru descrierea metodei alese pentru rezolvarea problemei. Algoritmul este implementat în calculator cu ajutorul instrucţiunilor puse la dispoziţie de limbajul de programare. Un astfel de limbaj se numeşte limbaj procedural (cum este limbajul C++).

Programul este format din instrucţiuni care operează cu date (date elementare sau structuri de date), adică execută acţiuni asupra acestor date. De exemplu, cu structura de date de tip fişier se pot executa operaţii de scriere în fişier sau de citire din fişier.

Programele de calculator prelucrează informaţii din lumea care ne înconjoară. Lumea reală este formată din obiecte, iar omul interacţionează cu ea prin intermediul acestor obiecte. O fotografie prezintă şi ea un ansamblu de obiecte care sunt caracterizate însă numai de proprietăţi (formă, dimensiuni, culoare etc.), nu şi de acţiuni. Ea este o reprezentare statică a lumii reale. Dar lumea care ne înconjoară este o lume dinamică în care fiecare obiect este determinat de proprietăţi, metode şi evenimente. Putem să ne gândim la proprietăţi ca la atributele obiectului, la metode ca la acţiuni ale obiectului, iar la evenimente ca la răspunsuri ale obiectului la acţiunile omului asupra lui.

Obiectele care diferă între ele numai prin valorile proprietăţilor şi au acelaşi comportament (reacţionează la aceleaşi evenimente prin aceleaşi metode) formează o clasă de obiecte.

Scop: identificarea proprietăţilor, metodelor şi evenimentelor asociate unui obiect.

Obiectul 1: Balonul unui copil.

Proprietăţile balonului includ atât atributele vizibile, cum sunt înălţimea, diametrul, culoarea, cele care descriu starea lui (umflat sau dezumflat), cât şi atribute invizibile, cum este vârsta. Prin definiţie, toate baloanele au aceste proprietăţi. Valorile acestor proprietăţi diferă de la un balon la altul. Balonului i se pot asocia de asemenea şi acţiuni pe care le poate executa, adică metode: metoda de umflare a balonului (acţiunea prin care se umflă balonul cu aer), metoda de dezumflare a balonului (acţiunea prin care se scoate aerul din balon), metoda de înălţare a balonului (acţiunea prin care se ridică balonul). Toate baloanele sunt capabile să execute aceste acţiuni, deci au asociate aceste metode. Baloanele au şi răspunsuri predefinite la anumite evenimente externe (la evenimentul înţepătură, răspunsul este dezumflarea automată, iar la evenimentul eliberarea balonului răspunsul este înălţarea lui în aer) sau interne (la evenimentul presiune prea mare a aerului din interior, răspunsul este distrugerea balonului).

Aşadar, baloanele diferă între ele numai prin valorile proprietăţilor, pot să execute aceleaşi acţiuni (cunosc aceleaşi metode) şi au acelaşi comportament (reacţionează la aceleaşi evenimente prin aceleaşi metode). Putem spune că balonul este un tip de obiect din lumea reală, iar mulţimea baloanelor formează o clasă de obiecte.

Obiectul 2: Câinele.

Proprietăţile câinelui includ atât atributele vizibile, cum sunt rasa, înălţimea, greutatea, culoarea, lungimea părului, cât şi atribute invizibile, cum sunt numele şi vârsta. Prin definiţie, toţi câinii au aceste proprietăţi. Valorile acestor proprietăţi diferă de la un câine la altul. Câinelui i se pot asocia de asemenea şi acţiuni pe care le poate executa, adică

Informatică 5

metode: lătratul, alergatul, mersul, muşcatul, îngropatul mâncării etc. Toţi câinii sunt capabili să execute aceste acţiuni, deci au asociate aceste metode. Câinii au şi răspunsuri predefinite la anumite evenimente externe (la evenimentul prezenţa unui străin, răspunsul este lătratul, la evenimentul asmuţirea lui asupra unui duşman, răspunsul este muşcatul duşmanului) sau interne (la evenimentul sătul, răspunsul este îngroparea mâncării).

Aşadar, câinii diferă între ei numai prin valorile proprietăţilor, pot să execute aceleaşi acţiuni (cunosc aceleaşi metode) şi au acelaşi comportament (reacţionează la aceleaşi evenimente prin aceleaşi metode). Putem spune că un câine este un tip de obiect din lumea reală, iar mulţimea câinilor formează o clasă de obiecte.

Obiectul 3: Fişierul.

Proprietăţile fişierului includ atributele: nume, tipul informaţiei memorate, dimensiunea, protec-ţia prin parolă, data la care a fost creat, data la care a fost modificat ultima dată, tipul de acces la informaţia memorată. Prin definiţie, toate fişierele au aceste proprietăţi. Valorile acestor proprietăţi diferă de la un fişier la altul. Fişierului i se pot asocia de asemenea şi acţiuni pe care le poate executa, adică metode: crearea, copierea, mutarea, redenumirea, ştergerea. Toate fişierele sunt capabile să execute aceste acţiuni, deci au asociate aceste metode. Fişierele au şi răspunsuri predefinite la anumite evenimente externe: la evenimentul deschi-derea fişierului, răspunsul este alocarea unei zone de memorie internă în care sunt transfera-te înregistrările din fişier care se vor prelucra; la evenimentul închiderea fişierului, răspunsul este eliberarea zonei de memorie alocată fişierului pentru lucru; iar la evenimentul comunica-rea parolei, răspunsul este deschiderea fişierului, în cazul în care parola este corectă.

Aşadar, fişierele diferă între ele numai prin valorile proprietăţilor, pot să execute aceleaşi acţiuni (cunosc aceleaşi metode) şi au acelaşi comportament (reacţionează la aceleaşi evenimente prin aceleaşi metode). Putem spune că fişierul este un tip de obiect din lumea reală, iar mulţimea fişierelor formează o clasă de obiecte.

Aşa cum o fotografie descrie starea unui obiect la un moment dat, şi o structură de date folosită pentru descrierea obiectului nu poate să caracterizeze decât starea acelui obiect la un moment dat, deci este o descriere statică a obiectului. Tendinţa limbajelor moderne de programare este de a pune în corespondenţă obiectele reale cu obiectele virtuale – pentru a putea evidenţia transformările pe care acestea le suferă în mod continuu. Obiectul va încap-sula atât datele care descriu proprietăţile, cât şi subprogramele care prelucrează aceste date şi care vor defini metodele obiectului.

Această metodă de programare se numeşte programarea orientată pe obiecte –deoarece rezolvarea problemei se face cu ajutorul obiectelor. Ea este orientată pe definirea obiectelor. Folosind această metodă, programatorul trebuie să construiască obiecte care sunt formate din date şi subprogramele care prelucrează aceste date.

Aceeaşi problemă poate fi rezolvată cu oricare dintre cele două metode. Metoda programării orientate pe obiecte are însă următoarele avantaje: permite un control mai bun al programelor (în special, al programelor de dimensiuni mari) şi permite o mai uşoară dezvoltare a programelor.

Pentru metoda programării orientate pe obiecte poate fi folosit orice limbaj de progra-mare. Limbajele care sunt orientate pe programarea pe obiecte (cum este limbajul C++) prezintă avantajul că au implementate structuri de date care permit încapsularea datelor şi a subprogramelor într-o singură variabilă de memorie.

6 Programare orientată pe obiecte şi programare vizuală

Domeniul în care programarea orientată pe obiecte a ajuns indispensabilă este realizarea interfeţei. Aşa cum obiectele cu care venim în contact reprezintă interfaţa între noi şi lumea reală, şi interfaţa grafică dintre utilizator şi programul de aplicaţie este construită cu diferite obiecte virtuale, afişate pe ecran, care pot fi acţionate cu mouse-ul şi tastatura. Putem spune că, în urma acţionării, obiectul declanşează un anumit eveniment. Deoarece utilizatorul poate veni în contact cu mai multe aplicaţii, s-a căutat o standardizare a acestor obiecte, atât din punct de vedere al proprietăţilor, cât şi din punct de vedere al răspunsului la evenimentul declanşat. De exemplu, dacă un utilizator vede pe ecranul calculatorului un buton pe care scrie OK, ştie că dacă îl va acţiona se va închide caseta de dialog şi se va continua execuţia aplicaţiei.

3.1.1. Principiile programării orientate pe obiecte

Conceptele folosite în programarea orientată pe obiecte sunt următoarele:

Obiectul reprezintă un ansamblu de date (un număr fix de variabile) şi subprograme necesare pentru prelucrarea datelor.

Datele poartă numele de proprietăţi, iar subprogramele – numele de metode sau evenimente. Proprietăţile, metodele şi evenimentele sunt membrii unui obiect. Interfaţa de acces la obiect este realizată numai prin intermediul metodelor. În acest mod, subprogramele membre ale unui obiect prelucrează datele membre ale obiectului care apelează metoda.

Proprietatea reprezintă un atribut al unui obiect care defineşte una dintre caracteristicile sau unul dintre aspectele sale. De exemplu, un obiect vizual al interfeţei poate avea proprietatea visible – care determină dacă obiectul este vizibil la un moment dat.

Metoda reprezintă acţiunea pe care o poate executa un obiect. Utilizatorul unui obiect are acces la datele obiectului numai prin intermediul metodelor obiectului, iar metoda are acces implicit la membrii unui obiect. Metodele ascund celui care utilizează obiectul amănunte despre modul în care a fost implementat. De exemplu, un obiect vizual de tip listă are implementate metode necesare pentru întreţinerea listei: adăugarea unui articol la listă, ştergerea unui articol din listă etc.

Evenimentul reprezintă o acţiune recunoscută de obiect pentru care se poate scrie un program ca răspuns. Evenimentele pot fi externe, adică generate de acţiuni ale utilizatorului (un clic cu mouse-ul, mişcarea mouse-ului, apăsarea unei taste) sau interne, adică generate printr-un cod de program sau de sistem.

Clasa reprezintă definiţia unui anumit tip de obiect. Definiţia cuprinde descrierea proprietăţilor şi a metodelor obiectului.

Folosirea claselor permite gestionarea mai multor obiecte de acelaşi tip. Clasa este însă doar un termen abstract, un şablon care defineşte caracteristicile unui obiect (cum arată şi cum se comportă).

Instanţa reprezintă un obiect creat pornind de la definiţia unei clase.

Spre deosebire de clasă, care este doar o definiţie a obiectului, o instanţă există ca un obiect care poate fi folosit pentru a executa anumite acţiuni. De exemplu, o casetă de text dintr-un formular este o instanţă a clasei TextBox care descrie acest tip de obiecte vizuale, fişerul alfa.txt este o instanţă a clasei fişier, iar câinele Azor este o instanţă a clasei Câine.

Informatică 7

Instanţe multiple reprezintă mai multe obiecte create de aceeaşi clasă. Obiectele au propriile proprietăţi şi date private, dar folosesc împreună codul de program.

Încapsularea reprezintă un mecanism prin care, folosind o singură definiţie, sunt incluse într-o singură structură de date toate componentele unui obiect:

datele şi metodele.

Datele membre ale unui obiect nu pot fi modificate decât prin intermediul metodelor proprii obiectului respectiv. Încapsularea izolează complexitatea internă a unui obiect de restul aplicaţiei.

Moştenirea este o facilitate, oferită de programarea pe obiecte, prin care o clasă nouă, numită clasă derivată, se poate construi pornind de la o clasă existentă,

numită clasă de bază, prin preluarea caracteristicilor clasei de bază.

Clasa derivată se mai numeşte şi clasă copil, iar clasa de bază – clasă părinte. O clasă derivată poate fi folosită pentru a obţine din ea o altă clasă derivată, ea devenind clasa de bază pentru noua clasă. Folosind facilitatea de moştenire, se poate crea o ierarhie de clase şi se poate dezvol-ta uşor un software existent. Clasa copil moşteneşte accesul la datele şi metodele strămoşilor (de exemplu, dacă o clasă Text are proprietatea ca textul să fie scris îngroşat, orice urmaş al acestei clase va avea această proprietate).

La proprietăţile şi metodele clasei de bază, programatorul poate adăuga proprietăţi şi metode noi, definite explicit în noua clasă. De exemplu, din clasa de bază Câine se poate obţine clasa derivată Câine de rasă. Clasa Câine de rasă va moşteni toate proprietăţile şi toate metodele clasei Câine, la care se vor adăuga proprietăţi şi metode noi.

Orice modificare într-un strămoş se reflectă şi la urmaşi.

Principalele avantaje ale folosirii programării orientate pe obiecte sunt: Reutilizarea unui software deja scris. Acest avantaj creşte eficienţa programatorilor.

În programarea clasică, se realiza prin crearea bibliotecilor de subprograme. În programarea orientată pe obiecte se realizează prin crearea bibliotecilor de obiecte. Astfel, un obiect creat pentru o aplicaţie poate fi folosit şi într-o altă aplicaţie.

Dezvoltarea mai uşoară a aplicaţiilor. În programarea orientată pe obiecte creşte gradul de modularizare al unei aplicaţii faţă de programarea clasică. Adăugarea sau modificarea unor module de aplicaţie se va face mai uşor datorită organizării obiectelor în clase de obiecte şi a facilităţii de moştenire.

Controlul mai bun al programelor de dimensiuni mari. Aplicaţia este mai bine structurată decât în cazul programării clasice, şi mult mai abstractizată, permiţând o mai uşoară citire şi urmărire a codului.

Dezvoltarea programării vizuale ca aplicaţie a programării orientate pe obiecte. În varianta clasică, descrierea unui obiect grafic de pe ecran se făcea precizând printr-un set de instrucţiuni caracteristicile obiectului (coordonatele obiectului, dimensiunile, cu-

Câine

Câine de rasă

Proprietăţi noi rasă pedigree concursuri premii

Metode noi participarea la

concursuri

8 Programare orientată pe obiecte şi programare vizuală

loarea, textul afişat etc.), iar prin subprograme – modul în care putea fi manipulat obiec-tul. În programarea vizuală există deja obiecte vizuale predefinite care pot fi folosite pentru construirea unei interfeţe. Obiectul vizual conţine proprietăţile obiectului grafic (cu valori implicite, care însă pot fi modificate) şi metodele şi evenimentele la care poate reacţiona obiectul vizual, fără să mai necesite scrierea codului de către programator.

a. Definiţi următoarele clase de obiecte: pisică, elev, casă, autoturism, carte.

Pentru fiecare clasă de obiecte veţi preciza proprietăţile, metodele şi evenimentele.

b. Descompuneţi obiectul autoturism în obiecte distincte, mai simple. Precizaţi pentru fiecare obiect proprietăţile, metodele şi evenimentele.

c. Descompuneţi obiectul şcoală în obiecte distincte, mai simple. Precizaţi pentru fiecare obiect proprietăţile, metodele şi evenimentele.

d. Pornind de la clasa de obiecte fişier, definiţi clasele derivate fişier de date şi fişier executabil. Precizaţi pentru fiecare clasă derivată metodele noi pe care le conţine.

e. Pornind de la clasa de obiecte fişier de date, definiţi clasele derivate fişier document şi fişier foaie de calcul. Precizaţi pentru fiecare clasă derivată metodele noi pe care le conţine.

3.1.2. Proiectarea aplicaţiei în programarea orientată pe obiecte

Pentru a proiecta o aplicaţie, folosind metoda de programare clasică, problema care tre-buie rezolvată se descompune în subprobleme (module), fiecare subproblemă descom-punându-se la rândul ei în subprobleme mai simple, până când se obţin subprobleme cu rezolvare imediată. Subproblemele sunt rezolvate cu ajutorul subprogramelor.

În programarea orientată pe obiecte, pentru a proiecta o aplicaţie, se execută următorii paşi: PAS1. Pornind de la funcţiile aplicaţiei, se identifică obiectele principale care alcătuiesc

aplicaţia. PAS2. Obiectele complexe sunt descompuse în obiecte distincte, mai simple. PAS3. Pentru fiecare obiect se identifică sarcina pe care o are în realizarea aplicaţiei. PAS4. Pentru fiecare obiect, pornind de la sarcina pe care o are în cadrul aplicaţiei, se

identifică operaţiile pe care le execută obiectul sau operaţiile pe care le execută programul asupra obiectului. Aceste operaţii vor deveni metodele obiectului.

PAS5. Pentru fiecare obiect, pornind de la metodele pe care trebuie să le cunoască, se identifică informaţiile pe care trebuie să le conţină pentru a putea executa metodele. Aceste informaţii vor deveni proprietăţile obiectului.

Scop: proiectarea unei aplicaţii folosind metoda programării orientate pe obiecte.

Enunţul problemei. Să se proiecteze o aplicaţie de tip Editor de texte.

O aplicaţie de tip editor de texte are următoarele funcţii: editarea textului; tipărirea textului; păstrarea textului în fişier.

Principalele obiecte ale unei aplicaţii de tip Editor de texte vor fi: Editorul, Tipărirea şi Administratorul de fişiere.

Informatică 9

Sarcina editorului este de a asigura scrierea textului de la tastatură, afişarea textului pe ecran, operaţii de corectură în text prin ştergerea şi inserarea caracterelor, operaţii cu blocuri de text (selectarea, copierea, mutarea şi ştergerea blocului de text) şi operaţii de căutare a unui şir de caractere, respectiv de căutare şi înlocuire a unui şir de caractere cu un alt şir de caractere.

Sarcina tipăririi este de a asigura pregătirea textului pentru tipărire (dimensiunea paginii fizice, dimensiunea paginii utile, orientarea la tipărire, antetul şi subsolul paginii), numărul de copii, ce se tipăreşte din text (tot textul, zona selectată sau paginile precizate), imprimanta la care se tipăreşte şi tipărirea textului.

Sarcina administratorului de fişiere este de a asigura salvarea textului într-un fişier, încărcarea textului din fişier pentru a fi prelucrat şi operaţiile obişnuite cu fişiere (copiere, ştergere, redenumire etc.).

Pornind de la sarcinile definite anterior, fiecare dintre cele trei obiecte poate fi descompus la rândul său în alte obiecte.

După ce au fost identificate toate obiectele aplicaţiei, se trece la identificarea proprietăţilor şi metodelor fiecărui obiect. De exemplu, scopul obiectului fişier de date în cadrul unei aplicaţii este de a memora o colecţie de date. Din această cauză, el va trebui să cunoască atât metodele folosite de orice obiect fişier cât şi metodele de deschidere, de închidere şi de salvare a informaţiilor. Pentru a putea executa aceste metode, are nevoie de următoarele informaţii: nume, dimensiune, parolă de protecţie, data la care a fost creat, data la care a fost actualizat, tipul de acces. .

a. Pentru exemplul precedent, enumeraţi câteva dintre proprietăţile şi meto-

dele obiectului editor şi ale obiectului tipărire. b. Proiectaţi o aplicaţie pentru exploatarea listelor.

fişier copiază mută redenumeşte şterge deschide închide salvează nume dimensiune parolă data creării data ultimei

actualizări tipul de acces tipul informaţiei

memorate

numele obiectului

metodele membre ale obiectului

proprietăţile membre ale obiectului

Editor de texte

Editor Tipărire Administrator fişiere

Tex

t

Tas

tatu

Ecr

an

Co

men

zi

Tex

t

Imp

rim

antă

Co

men

zi

Tex

t

Fiş

ier

Co

men

zi

10 Programare orientată pe obiecte şi programare vizuală

3.1.3. Structura unei aplicaţii orientate pe obiecte

În metoda clasică de programare, programul conţine instrucţiuni pentru definirea datelor şi instrucţiuni care implementează algoritmul folosit pentru rezolvarea problemei. Algorit-mul precizează modul în care sunt prelucrate datele pentru a obţine rezultatele dorite.

În metoda programării orientate pe obiecte se folosesc obiecte care încapsulează atât datele, cât şi o serie de subprograme care reprezintă metodele cunoscute de obiect. În programul care lucrează cu aceste obiecte nu mai sunt necesare instrucţiuni pentru a descrie algoritmul de rezolvare a problemei ci, pentru a rezolva problema, se apelează metodele obiectului. Cu alte cuvinte, programul nu mai este o listă de instrucţiuni prin care se precizează ce operaţii trebuie să se execute cu datele definite, ci cereri către obiect de a executa singur anumite prelucrări.

Structura unui program orientat pe obiecte este următoarea: 1. Se defineşte o structură de tip clasă de obiecte care conţine proprietăţile şi metodele

obiectului. În exemplul următor s-a definit clasa de obiecte persoana care conţine proprietăţile (datele) nume, pren şi v, în care se memorează numele, prenumele şi, respectiv, vârsta unei persoane, şi două metode (subprogramele) – afiseaza() şi varsta() – care afişează numele şi prenumele persoanei, respectiv furnizează vârsta persoanei.

2. În program se creează instanţe ale obiectului, adică variabile de tipul clasei de obiecte definită. În exemplu, în subprogramul main() s-a creat o singură instanţă a obiectului prin variabila de memorie p de tip persoana.

3. În program se rezolvă problema apelând metodele obiectului. În exemplu, în subpro-gramul main(), s-au atribuit valori proprietăţilor obiectului, s-au afişat numele şi prenumele persoanei apelând metoda afiseaza() şi s-a afişat vârsta persoanei furnizată de metoda varsta().

#include<iostream.h> class persoana {public: char nume[20],pren[20]; int v; void afiseaza() {cout<<nume<<" "<<pren<<endl;} int varsta() {return v;} }; void main() {persoana p; cout<<"Numele persoanei: "; cin.get(p.nume,20); cin.get(); cout<<"Prenumele persoanei: "; cin.get(p.pren,20); cout<<"Varsta: "; cin>>p.v; p.afiseaza(); cout<<"are "<<p.varsta()<<" ani"; }

se defineşte clasa de obiecte persoana

se creează obiectul p

se atribuie valori proprietăţilor obiectului p

proprietăţile clasei de obiecte

metodele clasei de obiecte

se apelează metodele obiectului p

Informatică 11

3.1.4. Clase şi obiecte 3.1.4.1. Definirea claselor şi a obiectelor Sintaxa instrucţiunii pentru definirea unei clase este următoarea:

Definirea unei clase de obiecte se termină cu caracterul ; deoarece este o instrucţiune.

Specificatorii de acces precizează modul în care programul are acces la membrii obiectului. Un specificator de acces are efect începând cu declararea lui şi până la întâlnirea unui alt specificator de acces sau până la sfârşitul definiţiei clasei. Există următorii specificatori de acces la membrii obiectului: 1. public – Programul are acces la aceşti membri ai obiectului. Metodele publice se mai

numesc şi metode de interfaţă deoarece ele definesc operaţiile pe care programul le poate executa asupra obiectului.

2. private – Programul nu are acces direct la aceşti membri ai obiectului. La aceşti membri accesul direct este permis numai din interiorul obiectului. Programul are acces la aceşti membri numai prin intermediul metodelor publice.

3. protected – Pentru clasa de bază programul are acces direct la aceşti membri ai obiectului. Pentru o clasă derivată programul nu are acces direct la aceşti membri ai obiectului, accesul fiind permis numai din interiorul obiectului.

Specificatorul de acces implicit este potected. Altfel spus, dacă nu este precizat niciun specificator de acces, programul nu are acces la membrii obiectului.

Printr-o instrucţiune de definire a unei clase nu se creează o variabilă de memorie, ci un tip de dată utilizator (tipul de dată clasă). Pentru a folosi în program o variabilă de memorie de acest tip, ea trebuie declarată:

nume clasă nume variabilă;

Aşadar, pentru a putea manipula un obiect trebuie să definiţi: 1. un tip de dată clasă de obiecte prin care descrieţi colecţia de membri ai obiectului şi 2. o variabilă de memorie care va avea ca tip de dată numele clasei (instanţa obiectului).

Se pot folosi următoarele două variante de declarare a tipului clasă de obiecte şi a variabilei de acest tip:

Varianta 1

class nume_clasă {//definire membri clasă}; nume clasă nume variabilă;

Varianta 2

class nume_clasă {//definire membri clasă} nume variabilă;

class nume_clasa {//date şi subprograme private specificator_de_acces: //date şi subprograme ................................ specificator_de_acces: //date şi subprograme };

Atenţie

Clasa

12 Programare orientată pe obiecte şi programare vizuală

Accesul la membrii obiectului se face folosind operatorul punct (.):

nume_variabilă_obiect.nume_membru Operatorul punct este operatorul de selecţie a membrului unui obiect şi leagă numele obiectului de numele membrului, adică, în expresia a.b – a trebuie să fie de tip obiect, iar b trebuie să fie de tip membru al acelui obiect. Punctul este un operator binar şi are prioritate maximă.

Observaţie. Definirea metodelor unei clase de obiecte se poate face fie în interiorul clasei, fie în exteriorul ei. În programul din paragraful precedent, membrii clasei persoana au fost definiţi în interiorul clasei. În programul următor, membrii clasei sunt definiţi în afara clasei. În acest caz, definiţia clasei va conţine prototipurile funcţiilor membre.

Operatorul :: se numeşte operator de rezoluţie a domeniului de vizibilitate a unui identificator şi leagă numele domeniului de vizibilitate de un identificator (numele unei variabile de memorie sau numele unui subprogram). Acest operator este un operator binar şi are prioritate maximă. El permite accesul la un identificator declarat într-un domeniu închis. În exemplul precedent operatorul :: leagă numele clasei (care este un domeniu închis) de numele unui membru al clasei (numele unui subprogram).

În definirea unei clase se pot folosi ambele metode; fiecare metodă are avantaje şi dezavantaje: 1. Dacă funcţia este declarată în interiorul clasei, va fi tratată de compilator ca funcţie

inline, adică fiecare apel al funcţiei este înlocuit cu codul său. Acest mod de tratare are avantajul că se reduce timpul de execuţie a programului prin eliminarea timpilor de încărcare a stivei şi de revenire după execuţie, care apar la fiecare apel al unui subprogram. Are însă dezavantajul unui consum mai mare de memorie deoarece creşte codul programului. În cazul în care metoda este definită în interiorul clasei, pentru fiecare instanţă a obiectului care apelează metoda se creează o copie a codului metodei. În cazul în care metoda este definită în afara clasei, instanţele multiple ale obiectelor îşi partajează o singură copie a codului metodei.

#include<iostream.h> class persoana {public: char nume[20],pren[20]; int v; void afiseaza(); int varsta(); }; void persoana::afiseaza() {cout<<nume<<" "<<pren<<endl;} int persoana::varsta() {return v;} void main() {persoana p; cout<<"Numele persoanei: "; cin.get(p.nume,20); cin.get(); cout<<"Prenumele persoanei: "; cin.get(p.pren,20); cout<<"Varsta: "; cin>>p.v; p.afiseaza(); cout<<"are "<<p.varsta()<<" ani"; }

metodele sunt declarate în interiorul clasei de obiecte

metodele sunt definite în exteriorul

clasei de obiecte

Informatică 13

2. Definirea metodelor în afara clasei face programul mai uşor de citit atunci când clasa conţine un număr mare de metode.

În programul din paragraful precedent, toţi membrii clasei au fost declaraţi publici. În programul următor, care rezolvă aceeaşi problemă ca şi cel precedent, unii membri ai clasei persoana au fost declaraţi publici, iar alţii privaţi. În acest exemplu programul are acces direct numai la membrii declaraţi publici (datele pren şi v şi metodele atribuie_nume, afiseaza şi varsta). Programul are acces la membrii privaţi (data nume şi metoda afisează) prin metodele publice atribuie_nume şi afiseaza.

Observaţie. Un obiect poate fi definit şi cu ajutorul unei înregistrări (tipul de dată struct). Cele două tipuri de date au sintaxe asemănătoare şi permit memorarea unor date cu legătură logică de conţinut:

#include<iostream.h> struct persoana {char nume[20],pren[20]; int v; void afiseaza() {cout<<nume<<" "<<pren<<endl;} int varsta() {return v;}}; void main() {persoana p; cout<<"Numele persoanei: "; cin.get(p.nume,20); cin.get();

#include<iostream.h> #include<string.h> class persoana {public: char pren[20]; int v; void atribuie_nume (char sir[20]) {strcpy(nume,sir);} void afiseaza() {afiseaza_nume(); cout<<" "<<pren<<endl;} int varsta() {return v;} private: char nume[20]; void afiseaza_nume() {cout<<nume;} }; void main() {persoana p; char nume[20]; cout<<"Numele persoanei: "; cin.get(nume,20); cin.get(); p.atribuie_nume(nume); cout<<"Prenumele persoanei: "; cin.get(p.pren,20); cout<<"Varsta: "; cin>>p.v; p.afiseaza(); cout<<"are "<<p.varsta()<<" ani"; }

membrii publici ai clasei de obiecte

membrii privaţi ai clasei de obiecte

14 Programare orientată pe obiecte şi programare vizuală

cout<<"Prenumele persoanei: "; cin.get(p.pren,20); cout<<"Varsta: "; cin>>p.v; p.afiseaza(); cout<<"are "<<p.varsta()<<" ani";}

Deosebirea dintre cele două metode de definire a unui obiect constă în faptul că în tipul de dată înregistrare toţi membrii sunt publici, iar tipul de dată clasă de obiecte permite definirea membrilor privaţi.

Observaţie. Se poate defini un pointer către tipul de date clasă de obiecte. În programul următor se foloseşte clasa de obiecte persoana.

#include<iostream.h> class persoana { …… }; // definiţia clasei void main() {persoana *p = new persoana; cout<<"Numele persoanei: "; cin.get(p->nume,20); cin.get(); cout<<"Prenumele persoanei: "; cin.get(p->pren,20); cout<<"Varsta: "; cin>>p->v; p->afiseaza(); cout<<"are "<<p->varsta()<<" ani";}

Observaţie. Se poate atribui unui obiect, un alt obiect, de acelaşi tip cu el (care face parte din aceeaşi clasă de obiecte). Operaţia constă în atribuirea datelor unui obiect sursă celuilalt obiect (obiectul destinaţie). În programul următor se defineşte un grup de n persoane (vectorul p care conţine mai multe instanţe ale clasei de obiecte persoana) şi se afişează o listă cu persoanele din grup, ordonată crescător după vârstă.

#include<iostream.h> class persoana { …… }; // definiţia clasei void main() {persoana p[20], aux; // 21 de instanţe ale obiectului persoana int n,i,j; cout<<"Numarul de persoane: "; cin>>n; cin.get(); for (i=1;i<=n;i++) {cout<<"Numele persoanei: "; cin.get(p[i].nume,20); cin.get(); cout<<"Prenumele persoanei: "; cin.get(p[i].pren,20); cout<<"Varsta: "; cin>>p[i].v; cin.get();} for (i=1;i<n;i++) for (j=i+1;j<=n;j++) if (p[i].v>p[j].v) {aux=p[i]; p[i]=p[j]; p[j]=aux;} // operaţii de atribuire for (i=1;i<=n;i++) {p[i].afiseaza(); cout<<"\b cu varsta "<<p[i].varsta()<<" ani"<<endl;}}

Scop: prelucrarea datelor cu ajutorul obiectelor.

Enunţul problemei 1. Să se definească un nou tip de dată care să permită implemen-tarea numerelor complexe şi, folosind tipul de dată definit, să se compare modulul a două numere complexe.

Informatică 15

Noul tip de date va fi clasa de obiecte complex care va încapsula proprietăţile x şi y (date reale folosite pentru a memora partea reală şi partea imaginară a numărului complex) şi metodele citeste(), afiseaza() şi modul() (subprogramele pentru citirea, afişarea şi calculul modulului unui număr complex). #include<iostream.h> #include<math.h> class complex {public: float x,y; void citeste() {cout<<"partea reala: "; cin>>x; cout<<"partea imaginara: "; cin>>y;} void afiseaza() {if (x==0) if (y!=0) cout<<y<<"i"; else cout<<0; else if (y>0) cout<<x<<"+"<<y<<"i"; else if (y<0) cout<<x<<y<<"i"; else cout<<x;} float modul() {return sqrt(x*x+y*y); } }; void main() {complex a,b; // două instanţe ale obiectului complex char semn; a.citeste(); b.citeste(); if (a.modul()>b.modul()) semn='>'; else if (a.modul()==b.modul()) semn='='; else semn='<'; cout<<"|"; a.afiseaza(); cout<<"|"<<semn<<"|"; b.afiseaza(); cout<<"|";}

Enunţul problemei 2. Să se definească un nou tip de dată care să permită implemen-tarea fracţiilor şi, folosind tipul de dată definit, să se citească o fracţie şi să se afişeze fracţia simplificată.

Noul tip de date va fi clasa de obiecte fractie care va încapsula proprietăţile x şi y (date întregi folosite pentru a memora numărătorul şi numitorul fracţiei), metodele publice citeste(), afiseaza() şi simplifica() (subprogramele pentru citirea, afişarea şi simplificarea unei fracţii) şi metoda privată cmmdc() (subprogramul pentru calcularea celui mai mare divizor comun a două numere). #include<iostream.h> #include<math.h> class fractie {public: int x,y; void citeste() {cout<<"numaratorul fractiei: "; cin>>x; do {cout<<"numitorul fractiei: "; cin>>y; if (y==0) cout<<"Numitorul diferit de 0"<<endl;} while (y==0); }

16 Programare orientată pe obiecte şi programare vizuală

void afiseaza() {if (x*y<0) cout<<"-"; if (abs(x)==abs(y)) cout<<1; else if (x%y==0) cout<<abs(x)/abs(y); else {cout<<abs(x)<<"/"<<abs(y); if (abs(x)>abs(y)) {cout<<" adica "; if (x*y<0) cout<<"-"; cout<<abs(x)/abs(y)<<" si "; if (x*y<0) cout<<"-"; cout<<(abs(x)%abs(y))<<"/"<<abs(y);}}} void simplifica() {int c=cmmdc(abs(x),abs(y)); if (x!=0) {x=x/c; y=y/c;}} private: int cmmdc(int a, int b) {while (a!=b) if (a>b) a-=b; else b-=a; return a;} }; void main() {fractie f; f.citeste(); f.simplifica(); f.afiseaza();}

Enunţul problemei 3. Folosind tipul de date fractie definit anterior, să se compare două fracţii.

Se va folosi clasa de obiecte fractie definită anterior. #include<iostream.h> #include<math.h> class fractie { ...}; // definiţia clasei de obiecte fractie void main() {fractie f1,f2; // două instanţe ale obiectului fractie char semn; f1.citeste(); f2.citeste(); if (f1.x*f2.y>f2.x*f1.y) semn='>'; else if (f1.x*f2.y==f2.x*f1.y) semn='='; else semn='<'; if (f1.x*f1.y<0) cout<<"-"; cout<<abs(f1.x)<<"/"<<abs(f1.y)<<semn; if (f2.x*f2.y<0) cout<<"-"; cout<<abs(f2.x)<<"/"<<abs(f2.y); }

a. Rescrieţi programele anterioare definind metodele în afara claselor. b. Scrieţi un program care să citească un şir de n numere complexe şi

care să afişeze numărul complex care are modulul cel mai mare. c. Scrieţi un program care să ordoneze crescător un şir de n fracţii. d. Scrieţi un program care să citească informaţii despre un grup de n persoane

(numele, prenumele şi vârsta) şi care să afişeze persoanele care au vârsta mai mare decât vârsta medie a grupului.

Temă

Informatică 17

3.1.4.2. Supraîncărcarea funcţiilor Limbajul C++ permite supraîncărcarea funcţiilor.

Supraîncărcarea funcţiilor este o facilitate a limbajului prin care se pot declara şi folosi în acelaşi program (fişier sursă) mai multe funcţii cu acelaşi nume.

Compilatorul trebuie să poată să aleagă una dintre aceste funcţii. Din această cauză, trebuie să existe posibilitatea de a identifica o funcţie după modul în care este apelată. Astfel, două funcţii care au acelaşi nume trebuie să se deosebească prin: număr diferit de parametri, sau în cazul în care au acelaşi număr de parametri, aceştia trebuie să fie de tipuri diferite,

iar conversiile implicite nu trebuie să ducă la parametri de acelaşi tip.

Scop: identificarea cazurilor de supraîncărcare a funcţiilor.

Enunţul problemei 1. Să se construiască două funcţii cu acelaşi nume – schimb()– care să interschimbe valorile din două variabile de memorie; cele două variabile de memorie sunt de tip:

a. pentru prima funcţie – int, b. pentru a doua funcţie – float.

#include <iostream.h> void schimb (int &x, int &y) //prima funcţie {int z; z=x; x=y; y=z;} void schimb (float &x, float &y) //a doua funcţie {float z; z=x; x=y; y=z;} void main() {int a=4,b=5; float c=1.5, d=2.5; schimb(a,b); //parametrii de tip int – alege prima funcţie schimb(c,d); //parametrii de tip float – alege a doua funcţie cout<<a<<" "<<b<<endl; cout<<c<<" "<<d;}

Enunţul problemei 2. Să se construiască două funcţii cu acelaşi nume – aduna()– care să adune numere întregi:

a. prima funcţie – două valori, b. a doua funcţie – trei valori.

#include <iostream.h> int aduna (int x, int y) {return x+y;} //prima funcţie int aduna (int x, int y, int z) {return x+y+z;} //a doua funcţie void main() {int a=4,b=5,c=6; float d=1.5, e=2.5; char f='a',g='b'; cout<<aduna(a,b)<<endl; //afişează 9 cout<<aduna(a,b,c)<<endl; //afişează 15 cout<<aduna(d,e)<<endl; //afişează 3 cout<<aduna(f,g)<<endl;} //afişează 195

Su

praîn

cărcarea fun

cţiilor

18 Programare orientată pe obiecte şi programare vizuală

1. Construiţi două funcţii cu acelaşi nume – aduna()– care să adune numere naturale – şi pe care să le apelaţi din subprogramul main(): a. prima funcţie – adună primele n numere pare (valoarea lui n se

citeşte de la tastatură în funcţia main()), b. a doua funcţie – adună n numere pare mai mari decât numărul m (valorile lui n, m

şi şirul de numere se citesc de la tastatură în funcţia main()).

2. Construiţi două funcţii cu acelaşi nume – media() – care să furnizeze media aritmetică a numerelor memorate într-o matrice pătrată. Apelaţi-le din subprogramul main() (dimensiunea matricei şi elementele ei se citesc de la tastatură în funcţia main()): a. prima funcţie – matricea conţine numere întregi; b. a doua funcţie – matricea conţine numere reale.

3.1.4.3. Crearea şi distrugerea obiectelor Tipul de dată clasă de obiecte are implementată o metodă publică numită constructor implicit cu care sunt construite instanţele obiectelor şi o metodă publică numită destructor implicit cu care sunt distruse instanţele obiectelor. Aceste două metode sunt create la declararea clasei. Metoda constructor se execută automat atunci când în program se creează o instanţă a unui obiect. Ea alocă spaţiu de memorie obiectului creat şi asigură accesul pro-gramului la membrii lui. În cazul obiectelor statice, metoda destructor se execută la termina-rea subprogramului pentru obiectele locale, respectiv la terminarea programului, pentru obiectele globale. În cazul obiectelor dinamice, metoda destructor se execută la eliberarea zonei de memorie alocată obiectului (folosirea operatorului delete).

Metoda constructor implicită nu are parametri şi de aceea nu permite iniţializarea obiectelor. Se poate crea o metodă constructor care să iniţializeze obiectele. Pentru a crea un construc-tor trebuie respectate următoarele reguli: 1. Metoda constructor trebuie să aibă acelaşi nume ca şi clasa. 2. Constructorul este un subprogram fără tip, pentru care nu se precizează tipul void şi nu

poate returna o valoare. 3. Constructorul este un subprogram care poate să aibă sau poate să nu aibă parametri

formali. 4. Folosind supraîncărcarea funcţiilor, pentru o clasă de obiecte se pot defini mai mulţi

constructori.

De exemplu, pentru clasa de obiecte persoana se poate defini metoda constructor persoana.

#include <iostream.h> #include <string.h> class persoana {public: persoana (char nume_i[], char pren_i[], int v_i); //constructorul char nume[20],pren[20]; int v; void afiseaza(); int varsta(); }; persoana::persoana (char nume_i[], char pren_i[], int v_i) {strcpy(nume,nume_i); strcpy(pren,pren_i); v=v_i;} void persoana::afiseaza() {cout<<nume<<" "<<pren<<endl;}

Temă

Co

nst

ruct

ori

i D

estr

uct

oru

l

Informatică 19

int persoana::varsta() {return v;} void main() {persoana p1("Popa","Ana",17),p2("Alexe","Mihai",18); p1.afiseaza(); cout<<"are "<<p1.varsta()<<" ani"<<endl; p2.afiseaza(); cout<<"are "<<p2.varsta()<<" ani";}

sau #include <iostream.h> #include <string.h> class persoana {public: persoana (char nume[], char pren[], int v); //constructorul char nume[20],pren[20]; int v; void afiseaza(); int varsta(); }; persoana::persoana (char nume[], char pren[], int v) {strcpy(persoana::nume,nume); strcpy(persoana::pren,pren); persoana::v=v;} void persoana::afiseaza() {cout<<nume<<" "<<pren<<endl;} int persoana::varsta() {return v;} void main() {persoana p1("Popa","Ana",17),p2("Alexe","Mihai",18); p1.afiseaza(); cout<<"are "<<p1.varsta()<<" ani"<<endl; p2.afiseaza(); cout<<"are "<<p2.varsta()<<" ani";}

Se poate crea o metodă destructor respectând următoarele reguli: 1. Metoda destructor trebuie să aibă acelaşi nume ca şi clasa, precedat de caracterul ~. 2. Destructorul nu are parametri formali. 3. Pentru o clasă de obiecte nu poate fi definit decât un singur destructor.

Scop: folosirea metodelor constructor şi a metodei destructor pentru crearea, respectiv distrugerea instanţelor unei clase.

Enunţul problemei 1. Folosind metoda constructor se pot atribui valori implicite proprietăţilor obiectelor. De exemplu, pentru clasa de obiecte numere se defineşte metoda constructor numere() care iniţializează cele două proprietăţi ale clasei a şi b.

#include <iostream.h> class numere {public: numere (int,int); int a,b; void afiseaza();}; numere::numere (int a=1, int b=2) {numere::a=a; numere::b=b;}

20 Programare orientată pe obiecte şi programare vizuală

void numere::afiseaza() {cout<<a<<" "<<b<<endl;} void main() {numere n1,n2(3,4),n3=n2; n1.afiseaza(); //afişează 1 2 n2.afiseaza(); //afişează 3 4 n3.afiseaza();} //afişează 3 4

Enunţul problemei 2. În cazul în care există obiecte locale şi obiecte globale, mai întâi sunt create obiectele globale şi apoi obiectele locale, iar pentru mai multe obiecte globale, respectiv locale, ordinea este de sus în jos şi de la stânga la dreapta. De exemplu, pentru clasa de obiecte numar s-au definit metoda constructor numar() care iniţializează obiectele şi afişează valoarea cu care au fost iniţializate şi metoda destructor ~numar() care afişează valoarea obiectului care se distruge. Programul permite urmărirea ordinii în care sunt create şi distruse obiectele.

#include <iostream.h> class numar {public: numar(int); ~numar(); int a; void afiseaza(); }; numar::numar(int a) {cout<<"S-a construit obiectul "<<a<<endl; numar::a=a;} numar::~numar() //destructorul {cout<<"Se distruge obiectul "<<a<<endl;} void numar::afiseaza() {cout<<a<<endl;} numar n1(1),n2(2); void sb() {numar n3(3),n4(4); n3.afiseaza(); n4.afiseaza();} void main() {numar n5(5),n6(6); n1.afiseaza(); n2.afiseaza(); sb(); n5.afiseaza(); n6.afiseaza();}

Enunţul problemei 3. Folosind supraîncărcarea funcţiilor se pot defini mai multe meto-de constructor. De exemplu, pentru clasa de obiecte numere s-au definit trei metode constructor numere() care diferă prin numărul de parametri.

#include <iostream.h> class numere {public: numere(int,int); numere(int); numere(); int a,b; void afiseaza();}; numere::numere (int a, int b) {numere::a=a; numere::b=b;} numere::numere (int a) {numere::a=a; numere::b=4;} numere::numere() {cout<<"S-a creat un obiect neinitializat"<<endl;}

Informatică 21

void numere::afiseaza() {cout<<a<<" "<<b<<endl;} void main() {numere n1,n2(1,2),n3(3); n1.afiseaza(); //afişează mesajul şi valori reziduale pentru //proprietăţile a şi b n2.afiseaza(); //afişează 1 2 n3.afiseaza();} //afişează 3 4

Observaţie. Folosind pentru constructori funcţii cu parametri iniţializaţi se poate evita supraîncărcarea funcţiilor constructor. De exemplu, în loc să se definească pentru clasa numerelor complexe doi constructori: #include <iostream.h> #include <math.h> class complex {public: float x,y; complex(float, float); complex(); void citeste(); void afiseaza(); float modul(); }; complex::complex(float a, float b) {x=a; y=b;} complex::complex() {x=0; y=0;} ............................................................. //celelalte definiţii ale metodelor membre ale clasei complex void main() {complex c1,c2; ...}

se poate defini un singur constructor folosind iniţializarea parametrilor: #include <iostream.h> #include <math.h> class complex {public: float x,y; complex(float, float); void citeste(); void afiseaza(); float modul(); }; complex::complex(float a=0, float b=0) {x=a; y=b;} ............................................................. //celelalte definiţii ale metodelor membre ale clasei complex void main() {complex c1,c2; ...}

Adăugaţi metode constructor claselor complex şi fractie care să vă permită iniţializarea numerelor complexe, respectiv a fracţiilor. Numerele complexe vor fi iniţializate cu valoarea implicită numărul real 1 sau cu

valori precizate la crearea obiectului, iar fracţiile cu valoarea implicită 1 sau cu valori precizate la crearea obiectului. În ambele cazuri se vor realiza două variante de program: într-o variantă veţi folosi două metode constructor, iar în cealaltă variantă o singură metodă constructor.

Temă

22 Programare orientată pe obiecte şi programare vizuală

3.1.4.4. Supraîncărcarea operatorilor

Fiecare clasă de obiecte definită este înzestrată cu metode care să permită manipularea obiectelor pentru a obţine rezultatele cerute de diferite probleme. Metodele sunt implementate cu ajutorul subprogramelor. De exemplu, clasa de obiecte complex a creat un nou tip de date. Pentru a implementa cu ajutorul acestei clase numerele complexe, clasa trebuie înzestrată cu noi metode care să permită executarea operaţiilor obişnuite din matematică cu numere complexe: adunarea, scăderea, înmulţirea şi împărţirea a două numere complexe. De exemplu, putem înzestra această clasă cu metoda de adunare a două numere complexe:

#include <iostream.h> #include <math.h> class complex {public: float x,y; complex(float, float); void citeste(); void afiseaza(); float modul(); complex aduna(complex);}; complex complex::aduna(complex z) {complex c; c.x=x+z.x; c.y=y+z.y; return c;} ............................................................. //celelalte definiţii ale metodelor membre ale clasei complex void main() {complex a,b,c; a.citeste(); b.citeste(); c=a.aduna(b); c.afiseaza(); }

Acest mod de lucru este greoi. Mult mai simplu ar fi dacă am putea opera cu numerele complexe în acelaşi mod în care operăm cu numerele reale sau întregi, folosind operatorii definiţi în matematică. Acest lucru este posibil prin supraîncărcarea operatorilor.

Supraîncărcarea operatorilor este o facilitate a limbajului prin care se pot folosi operatorii definiţi pentru a executa operaţii cu alte tipuri de date decât

cele pentru care au fost implementaţi în limbaj.

Supraîncărcarea unui operator se face prin includerea în clasa de obiecte a unei funcţii operator membre, cu sintaxa următoare:

unde operator este un cuvânt cheie, iar op reprezintă simbolul sau cuvântul cheie folosit pentru operator.

Observaţie. Valoarea din partea stângă a operatorului este trecută automat către funcţia operatorului supraîncărcat. Din această cauză, dacă operatorul supraîncărcat este unar, lista de parametri nu conţine niciun parametru, iar dacă operatorul este binar, lista de parametri conţine un singur parametru, care corespunde operandului din partea dreaptă.

Pentru a supraîncărca un operator trebuie respectate următoarele reguli:

tip_rezultat nume_clasă :: operator op (listă_parametri) { //corpul funcţiei }

Su

pra

încă

rcar

ea o

per

ato

rilo

r

Informatică 23

1. Nu pot fi supraîncărcaţi decât operatorii definiţi în limbaj. Altfel spus, nu pot fi definiţi operatori noi (simboluri sau cuvinte cărora să li se asocieze operaţii cu instanţele unei clase de obiecte).

2. Operatorul supraîncărcat trebuie să utilizeze formatul standard (formatul cu care este implementat în limbaj). De exemplu, dacă formatul standard este operand operator operand, operatorul supraîncărcat trebuie să respecte acest format.

3. Operatorul supraîncărcat păstrează prioritatea şi asociativitatea operatorului implementat în limbaj.

4. Operatorul supraîncărcat se aplică numai pe instanţele clasei pentru care a fost creat. Altfel spus, compilatorul alege operaţia care se va executa în funcţie de tipul operanzilor. Astfel, dacă supraîncărcaţi operatorul + pentru clasa complex şi, în acelaşi program, aplicaţi operatorul pe două instanţe ale clasei complex, se va realiza adunarea a două numere complexe, iar dacă aplicaţi operatorul pe două date reale, se va realiza adunarea a două numere reale.

5. Nu pot fi supraîncărcaţi operatorii: . (operatorul membru al clasei), .* (operatorul pointer la membru), :: (operatorul de rezoluţie a domeniului de vizibilitate) şi ?: (operatorul condiţional).

Observaţie. Un caz special îl prezintă operatorii de incrementare care pot fi prefixaţi şi post-fixaţi. Pentru a supraîncărca operatorul de incrementare se foloseşte funcţia operator ++, astfel:

nume_clasă nume_clasă :: operator ++ () {...} prefixat

nume_clasă nume_clasă :: operator ++ (int) {...} postfixat

Pentru a supraîncărca operatorul de decrementare se foloseşte funcţia operator --, astfel:

nume_clasă nume_clasă :: operator ++ () {...} prefixat

nume_clasă nume_clasă :: operator ++ (int) {...} postfixat

Scop: identificarea operatorilor ce trebuie supraîncărcaţi pentru o clasă de obiecte.

Clasa de obiecte şiruri de caractere.

În marea majoritate a limbajelor de programare, pentru a manipula şiruri de caractere, exis-tă implementat un tip special de date. De obicei, prin această implementare, şirul de carac-tere poate avea o lungime de maxim 255 de caractere (ocupând 256 de octeţi, din care unul se foloseşte pentru a memora lungimea şirului de caractere). Pe acest tip de dată se pot aplica operatorul de concatenare, operatorul de atribuire şi operatorii relaţionali. În lim-bajul C++ şirul de caractere nu este implementat ca un tip de dată şi nu pot fi aplicaţi opera-torii menţionaţi. Realizarea acestor operaţii se poate face folosind funcţiile de sistem, astfel:

Operatorul Operaţia Funcţia de atribuire s1=s2 strcpy(s1,s2)

de concatenare s1+s2 strcat(s1,s2) relaţional s1==s2 sau s1!=s2 sau s1>s2 sau

s1<s2 sau s1>=s2 sau s1<=s2 strcmp(s1,s2)

Pentru a putea folosi aceşti operatori şi în limbajul C++, trebuie creată o clasă de obiecte care să implementeze tipul de date şir de caractere.

24 Programare orientată pe obiecte şi programare vizuală

Scop: supraîncărcarea operatorilor pentru diferite clase de obiecte.

Enunţul problemei 1. Să se implementeze tipul de date sir de caractere care să fie înzestrat cu operatorii de concatenare (+), de atribuire, relaţionali (>, < şi ==), de incrementare (++) şi de decrementare (--). Operatorul de incrementare va transforma şirul de caractere înlocuind literele mici cu litere mari, iar operatorul de decrementare va transforma şirul de caractere înlocuind literele mari cu litere mici.

Operatorul de atribuire este implementat implicit deoarece două şiruri de caractere sunt două instanţe ale clasei şiruri de caractere şi este permisă atribuirea unei instanţe a obiectului altei instanţe din aceeaşi clasă de obiecte. Operatorii de concatenare şi relaţionali (>, < şi ==) sunt operatori binari şi funcţiile operator au un parametru (cel de al doilea operand). Operatorii de incrementare şi de decrementare sunt operatori unari şi funcţiile operator nu au parametru. Programul se va testa pentru şirurile de caractere ”ALfa” şi beTA”.

#include <iostream.h> #include <string.h> class sir {public: char s[256]; //şirul de caractere poate avea maxim 255 de caractere sir(); //constructorul void citeste(); //metoda pentru citirea unui şir de caractere void afiseaza(); //metoda pentru afişarea unui şir de caractere sir operator +(sir); sir operator ++(); sir operator ++(int); sir operator --(); sir operator --(int); int operator >(sir); int operator <(sir); int operator ==(sir);}; sir::sir() {strcpy(s,"");} void sir::citeste() {cin.get(s,256); cin.get();} void sir::afiseaza() {cout<<s<<endl;} sir sir:: operator +(sir s1) {sir r; strcpy(r.s,s); strcat(r.s,s1.s); return r;} sir sir:: operator ++() //prefixat {sir r; strcpy(r.s,s); strupr(r.s); strcpy(s,r.s); return r;} sir sir:: operator ++(int x) //postfixat {sir r; strcpy(r.s,s); strupr(r.s); strcpy(s,r.s); return r;} sir sir:: operator --() //prefixat {sir r; strcpy(r.s,s); strlwr(r.s); strcpy(s,r.s); return r;} sir sir:: operator --(int x) //postfixat

Informatică 25

{sir r; strcpy(r.s,s); strlwr(r.s); strcpy(s,r.s); return r;} int sir:: operator <(sir s1) {if (strcmp(s,s1.s)<0) return 1; else return 0;} int sir:: operator >(sir s1) {if (strcmp(s,s1.s)>0) return 1; else return 0;} int sir:: operator ==(sir s1) {if (strcmp(s,s1.s)==0) return 1; else return 0;} void main() {sir s1,s2,s3; cout<<"Primul sir de caractere: "; s1.citeste(); (--s1).afiseaza(); cout<<"Al doilea sir de caractere: "; s2.citeste(); s3=s1+s2++; s2.afiseaza(); s3.afiseaza(); s3=++s1+s2; s1.afiseaza(); s3.afiseaza(); s3=s1--; s3.afiseaza(); s3=--s2; s3.afiseaza(); s1.afiseaza(); s2.afiseaza(); if (s1>s2) cout<<"Primul sir mai mare ca al doilea"; else if (s1<s2) cout<<"Primul sir mai mic ca al doilea"; else cout<<"Siruri gale";}

Enunţul problemei 2. Să se înzestreze clasa de obiecte complex cu operatorii matematici pentru numere complexe (+, -, *, / ), operatorul ! pentru conjugatul numărului şi operatorii de incrementare şi decrementare. Operatorul de incrementare prefixat va incrementa partea reală a numărului, iar operatorul de incrementare postfixat va incrementa partea imaginară a numărului. Operatorul de decrementare prefixat va decrementa partea reală a numărului, iar operatorul de decrementare postfixat va decrementa partea imaginară a numărului.

Operatorii matematici sunt operatori binari şi funcţiile operator au un parametru (cel de al doilea operand). Operatorii conjugatul numărului, de incrementare şi de decrementare sunt operatori unari şi funcţiile operator nu au parametru.

#include <iostream.h> #include <math.h> class complex {public: float x,y; complex(float, float); //constructorul void citeste(); //metoda pentru citirea unui număr complex void afiseaza(); //metoda pentru afişarea unui număr complex float modul(); //metoda pentru calculul modulului unui număr complex complex operator +(complex); complex operator -(complex); complex operator *(complex); complex operator /(complex); complex operator !();

26 Programare orientată pe obiecte şi programare vizuală

complex operator ++(); complex operator ++(int); complex operator --(); complex operator --(int); }; void complex::citeste() {cout<<"partea reala: "; cin>>x; cout<<"partea imaginara: "; cin>>y;} void complex::afiseaza() {if (x==0) if (y!=0) cout<<y<<"i"; else cout<<0; else if (y>0) cout<<x<<"+"<<y<<"i"; else if (y<0) cout<<x<<y<<"i"; else cout<<x; } float complex::modul() {return sqrt(x*x+y*y);} complex::complex(float a=0, float b=0) {x=a; y=b;} complex complex:: operator +(complex c) {complex r; r.x=x+c.x; r.y=y+c.y; return r;} complex complex:: operator -(complex c) {complex r; r.x=x-c.x; r.y=y-c.y; return r;} complex complex:: operator *(complex c) {complex r; r.x=x*c.x-y*c.y; r.y=y*c.x+x*c.y; return r;} complex complex:: operator /(complex c) {complex r; float z=c.x*c.x+c.y*c.y; if (z) {r.x=(x*c.x+y*c.y)/z; r.y=(y*c.x+x*c.y)/z;} else cout<<"Impartire la 0"; return r;} complex complex:: operator ++() //prefixat {complex r; r.x=x++; r.y=y; return r;} complex complex:: operator ++(int a) //postfixat {complex r; r.x=x; r.y=y++; return r;} complex complex:: operator --() //prefixat {complex r; r.x=x--; r.y=y; return r;} complex complex:: operator --(int a) //postfixat {complex r; r.x=x; r.y=y--; return r;} complex complex:: operator!() {complex r; r.x=x; r.y=-y; y=-y; return r;} void main() {complex c1,c2,c3; float x=4,y=3,z; cout<<"Operatii cu un numar complex"<<endl;

Informatică 27

c1.citeste(); ++c1; c1.afiseaza(); cout<<endl; c1++; c1.afiseaza(); cout<<endl; --c1; c1.afiseaza(); cout<<endl; c1--; c1.afiseaza(); cout<<endl; !c1; c1.afiseaza(); cout<<endl; cout<<"Modulul= "; cout<<c1.modul()<<endl; cout<<"Operatii cu doua numere complexe"<<endl; c1.citeste(); c2.citeste(); c3=++c1+c2; cout<<"Suma= "; c3.afiseaza(); cout<<endl; c3=c1-c2; cout<<"Diferenta= "; c3.afiseaza(); cout<<endl; c3=c1*c2; cout<<"Produsul= "; c3.afiseaza(); cout<<endl; c3=c1/c2; cout<<"Catul= "; c3.afiseaza(); cout<<endl; //operatorii care au fost supraîncărcaţi pentru numerele complexe //sunt folosiţi acum pentru numere reale: z=++x+y; cout<<z; } //afişează 8

1. Înzestraţi tipul de dată implementat pentru şiruri de caractere cu operatorul - care să concateneze două şiruri de caractere, eliminând spaţiile suplimentare dintre cuvinte, şi cu operatorii re-laţionali >=, <= şi !=. Modificaţi funcţia operatorului de concate-nare + astfel încât, dacă suma lungimilor celor două şiruri de ca-

ractere care se concatenează este mai mare de 255, la primul şir să se adauge, din cel de al doilea şir, caractere – până se ajunge la lungimea maximă a unui şir de caractere.

2. Supraîncărcaţi pentru clasa complex operatorii de atribuire multiplă (+=, -=, *=, /=) şi operatorii relaţionali de egalitate == şi de inegalitate !=.

3. Înzestraţi tipul de dată implementat pentru fracţii cu operatorii matematici +, -, *, /, operatorul inversul unei fracţii ! şi operatorii relaţionali >, <, ==, >=, <= şi !=.

4. Construiţi clasa de obiecte matrice care să vă permită implementarea tipului de dată matrice cu numere reale. Clasa trebuie să conţină metode pentru citirea şi afişarea unei matrice şi funcţii operator pentru adunarea, scăderea şi înmulţirea a două matrice şi pentru transpusa unei matrice prin supraîncărcarea operatorilor +, -, * şi !.

5. Construiţi clasa de obiecte multime care să vă permită implementarea tipului de dată mulţime de numere întregi. Clasa trebuie să conţină metode pentru citirea şi afişarea elementelor unei mulţimi şi pentru verificarea apartenenţei unui număr la mulţime, funcţii operator pentru reuniunea, intersecţia şi diferenţa a două mulţimi prin supraîncărcarea operatorilor +, * şi – şi funcţii operator pentru operatorii relaţionali dintre două mulţimi: este inclusă (<), include (>) şi sunt egale (==).

3.1.4.5. Funcţii prietene

Funcţia prietenă este o funcţie care nu aparţine clasei de obiecte şi care are acces la membrii privaţi ai obiectului numai prin intermediul obiectului

transmis ca parametru.

Pentru a construi o funcţie prietenă a unei clase de obiecte se scrie în interiorul clasei prototipul funcţiei precedat de cuvântul cheie friend care prefixează antetul:

friend tip_rezultat nume_funcţie (listă_parametri);

Fu

ncţia

prieten

28 Programare orientată pe obiecte şi programare vizuală

De exemplu, funcţia prietenă aduna() a clasei real are acces la datele private ale clasei prin intermediul parametrului r care este de tipul obiect al clasei real: #include <iostream.h> class real {float x,y; //membri privaţi public: real(float, float); //constructorul friend float aduna(real); }; //funcţie prietenă real::real(float a=0,float b=0) {x=a; y=b;} float aduna(real r) //obiectul este transmis ca parametru {return r.x+r.y;} void main() {real z(2.5,3.7); cout<<"suma= "<<aduna(z)<<endl;} //se afişează 6.2

Observaţie. O funcţie prietenă are acces la toţi membrii privaţi şi protejaţi ai clasei de obiecte pentru care a fost declarată prietenă.

Funcţiile prietene sunt utile pentru supraîncărcarea operatorilor. Atunci când se utilizează o funcţie operator membru pentru supraîncărcarea unui operator binar, operandul din partea stângă a operatorului generează apelarea funcţiei membru operator supraîncărcată. Să presupunem că dorim să supraîncărcăm operatorul + pentru a aduna un număr complex cu un număr real. În cazul în care primul operand este un număr complex şi al doilea operand un număr real, se poate folosi o funcţie membru a clasei pentru supraîncărcarea operatorului complex, astfel: class complex {float x,y; //membri privaţi public: complex(float, float); //constructorul ......................................................... //metodele pentru clasa de obiecte complex definite anterior //funcţiile operator membre ale clasei complex definite anterior complex operator +(float);}; complex::complex(float a=0, float b=0) {x=a; y=b;} complex complex::operator +(float z) {complex r; r.x=x+z; r.y=y; return r;} ......................................................... //definiţia metodelor clasei de obiecte complex //definiţia funcţiilor operator membre ale clasei complex void main() {complex c1,c2; float a; c1.citeste();cout<<"numar real ="; cin>>a; c2=c1+a; c2.afiseaza(); cout<<endl;}

Dar, operaţia de adunare între un număr complex şi un număr real este comutativă. Dacă primul operand ar fi un număr real, ar trebui să se execute operaţia de adunare între două numere reale, şi prezenţa unui operand complex după operator ar genera eroare de compilare. Soluţia în acest caz este să se folosească pentru supraîncărcarea operatorului funcţii prietene.

Informatică 29

Observaţie. Folosind funcţii prietene nu pot fi supraîncărcaţi operatorii: = (operatorul de atribuire), ( ) (operatorul apel de funcţie), [ ] (operatorul indice al tabloului) şi -> (operatorul de selecţie indirectă a membrului) .

Scop: folosirea funcţiilor prietene pentru supraîncărcarea operatorilor.

Enunţul problemei 1. Să se înzestreze clasa de obiecte complex cu operatorul matematic + pentru adunarea unui număr complex cu un număr real.

Se va folosi o pereche de funcţii prietene: una pentru a aduna un număr real cu un număr complex şi una pentru a aduna un număr complex cu un număr real.

#include <iostream.h> #include <math.h> class complex {float x,y; //membri privaţi public: complex(float, float); //constructorul ......................................................... //metodele pentru clasa de obiecte complex definite anterior //funcţiile operator membre ale clasei complex definite anterior friend complex operator +(complex, float); friend complex operator +(float,complex);}; complex::complex(float a=0, float b=0) {x=a; y=b;} complex operator +(complex c, float z) {complex r; r.x=c.x+z; r.y=c.y; return r;} complex operator +(float z,complex c) {complex r; r.x=c.x+z; r.y=c.y; return r;} ......................................................... //definiţia metodelor clasei de obiecte complex //definiţia funcţiilor operator membre ale clasei complex void main() {complex c1,c2,c3; float a; cout<<"numar real ="; cin>>a; c1.citeste(); c2=c1+a; c2.afiseaza(); cout<<endl; c3=a+c1; c3.afiseaza(); cout<<endl;}

Enunţul problemei 2. Să se înzestreze clasa de obiecte complex cu operatorii de incre-mentare şi decrementare prefixaţi care să incrementeze, respectiv să decrementeze, atât partea reală, cât şi partea imaginară.

Parametrul funcţiilor prietene pentru supraîncărcarea operatorilor este de tip complex şi este instanţa asupra căreia acţionează operatorul şi care îşi va modifica valoarea proprietăţilor. Din această cauză transferul se face prin referinţă.

#include <iostream.h> #include <math.h>

30 Programare orientată pe obiecte şi programare vizuală

class complex {float x,y; //membri privaţi public: complex(float, float); //constructorul ......................................................... //metodele pentru clasa de obiecte complex definite anterior //funcţiile operator membre ale clasei complex definite anterior //mai puţin funcţiile operator de incrementare şi decrementare friend complex operator ++(complex &); friend complex operator --(complex &);}; complex::complex(float a=0, float b=0) {x=a; y=b;} complex operator ++(complex &c) {c.x++; c.y++; return c;} complex operator --(complex &c) {c.x--; c.y--; return c;} ......................................................... //definiţia metodelor clasei de obiecte complex //definiţia funcţiilor operator membre ale clasei complex void main() {complex c; c.citeste(); ++c; c.afiseaza(); cout<<endl; --c; c.afiseaza(); cout<<endl;}

Enunţul problemei 3. Să se înzestreze clasa de obiecte persoana cu operatorii de intrare (<<) şi de ieşire (>>) astfel încât prin fluxul cin să se poată citi datele unei instanţe persoana, iar prin fluxul cout să se poată afişa datele unei instanţe persoana.

În general, o funcţie care supraîncarcă operatorul de intrare are forma:

istream & operator >>(istream & flux, tip_clasă nume_obiect) {//corpul funcţiei return flux;}

Operatorul de intrare are doi operanzi: primul este o referinţă către fluxul de intrare, iar al doilea este obiectul care va fi primit prin flux. Tipul de dată istream (flux de intrare) este implementat în limbaj; este o clasă de obiecte derivată din clasa ios. În acest exemplu, fluxul de intrare vine de la tastatură (cin).

În general, o funcţie care supraîncarcă operatorul de ieşire are forma:

ostream & operator <<(ostream & flux, tip_clasă nume_obiect) {//corpul funcţiei return flux;}

Operatorul de ieşire are doi operanzi: primul este o referinţă către fluxul de ieşire, iar al doilea este obiectul care va fi afişat în flux. Tipul de dată ostream (flux de ieşire) este implementat în limbaj; este o clasă de obiecte derivată din clasa ios. În acest exemplu, fluxul de ieşire este pe monitor (cout).

#include <iostream.h> #include <math.h> class persoana {char nume[20],pren[20]; //membri privaţi

Informatică 31

int v; public: persoana (); //constructorul persoana (char nume[], char pren[], int v); int varsta(); friend ostream & operator <<(ostream & cout, persoana p); friend istream & operator >>(istream & cin, persoana & p);}; persoana::persoana (char nume[], char pren[], int v) {strcpy(persoana::nume,nume); strcpy(persoana::pren,pren); persoana::v=v;} persoana::persoana() {;} int persoana::varsta() {return v;} ostream & operator <<(ostream & cout, persoana p) {cout<<p.nume<<" "<<p.pren<<" are "<<p.varsta()<<" ani"<<endl; return cout;} istream & operator >>(istream & cin, persoana & p) {cout<<"Numele persoanei: "; cin.get(p.nume,20); cin.get(); cout<<"Prenumele persoanei: "; cin.get(p.pren,20); cout<<"Varsta: "; cin>>p.v; return cin;} void main() {persoana p; cin>>p; cout<<p;}

1. După exemplul de mai sus, implementaţi pentru clasa complex funcţii prietene pentru operatorii de incrementare şi decre-mentare postfixaţi.

2. Înzestraţi tipul de obiect implementat pentru fracţii cu operatorii matematici +, -, *, / care să se poată aplica pe o fracţie şi un

număr întreg. 3. Înzestraţi clasa de obiecte matrice cu operatorii + care să adune un număr întreg la

elementele matricei şi * care să înmulţească un număr întreg cu elementele matricei. 4. Înzestraţi clasa de obiecte mulţime cu operatorii + care să adauge la mulţime un nou

element. Operaţia trebuie să fie comutativă. 5. Înzestraţi tipul de obiect implementat pentru şiruri de caractere cu operatorul de

concatenare + care să concateneze un şir de caractere cu o valoare numerică (valoarea numerică, pentru a putea fi concatenată, trebuie convertită în şir de caractere).

3.1.4.6. Membrii statici ai unei clase

Ca o variabilă locală să păstreze valoarea care i-a fost atribuită la ultima execuţie a subprogramului, ea trebuie declarată statică. Ca variabilă statică nu va mai fi memorată în stivă, ci în segmentul de date, la fel ca şi variabilele globale, valoarea sa putând fi păstrată şi după ce s-a terminat execuţia funcţiei. Spre deosebire de variabilele automatice, care la fiecare apel al funcţiei sunt iniţializate cu valoarea precizată, variabila statică va fi iniţializată cu valoarea precizată numai la primul apel al funcţiei, după care va avea ca valoare iniţială valoarea atribuită la ultima execuţie a subprogramului. În majori-tatea cazurilor, domeniul de vizibilitate al obiectelor cărora compilatorul le alocă memorie în segmentul de date este întreg programul. Altfel spus, aceste obiecte sunt globale.

Mem

bru

static

32 Programare orientată pe obiecte şi programare vizuală

Definiţia unei clase de obiecte poate să conţină membri statici: atât proprietăţi (date), cât şi metode (subprograme). Membrii statici sunt gestionaţi după reguli diferite decât cele pentru membrii care nu sunt statici.

Declararea variabilelor statice se face cu cuvântul cheie static: static tip dată nume variabilă;

De exemplu: static int a; Variabilele statice pot fi şi ele iniţializate la declarare: static int a=10;

Dacă o dată membru nu este statică, se creează câte o copie a ei pentru fiecare instanţă a clasei şi fiecare instanţă îşi gestionează propria copie. Dacă o dată membru este statică, se creează o singură copie şi toate instanţele clasei vor partaja această copie. Orice modificare a valorii acestei date făcută de o instanţă a clasei va fi valabilă pentru toate instanţele clasei respective.

Când se declară în definiţia clasei o dată membru statică, data va fi numai definită şi compilatorul nu-i alocă zonă de memorie. Pentru a i se aloca zonă de memorie se poate folosi una dintre următoarele metode: 1. Se iniţializează data la declararea ei. 2. Se redeclară variabila statică ca variabilă globală, în afara clasei, utilizând operatorul

de rezoluţie al domeniului de vizibilitate. În acest caz, compilatorul va iniţializa data cu valoarea 0, atunci când se creează prima instanţă a clasei.

Declararea funcţiilor statice se face cu cuvântul cheie static care prefixează antetul: static tip rezultat nume_funcţie (listă_parametri);

Regulile pentru gestionarea unei funcţii statice sunt următoarele: 1. Funcţiile statice pot accesa numai alţi membri statici ai clasei. Altfel spus, ele se

pot folosi numai pentru a manipula date statice. 2. Nu se poate supraîncărca o funcţie statică cu o funcţie care nu este statică, şi invers.

Scop: exemplificarea modului în care pot fi folosiţi membrii statici ai unei clase.

Exemplul 1. Clasa de obiecte intreg conţine un membru static: data a care este iniţializată la declararea ei pentru a i se aloca spaţiu de memorie. Constructorul incrementează valoarea variabilei statice, iar destructorul o decrementează. În acest exemplu se poate urmări modul în care instanţele clasei îşi partajează data statică.

Se creează trei instanţe ale clasei: x, y şi z. Iniţial, variabila statică a are valoarea 2. La crearea fiecărei instanţe, constructorul clasei incrementează valoarea datei statice, astfel încât ea ajunge la valoarea 5.

#include <iostream.h> class intreg {public: static int a=2; int b; intreg(int); ~intreg();}; intreg::intreg(int x) {a++; b=x;} //constructor

Informatică 33

intreg::~intreg() {a--;} //destructor void main() {intreg x(1),y(2),z(3); cout<<"Prima instanta= "<<x.a<<" "<<x.b<<endl; //afişează 5 1 cout<<"A doua instanta= "<<y.a<<" "<<y.b<<endl; // afişează 5 2 cout<<"A treia instanta= "<<z.a<<" "<<z.b<<endl;} // afişează 5 3

Pentru a urmări modul în care îşi modifică valoarea variabila statică a se poate modifica subprogramul main() pentru a afişa valoarea datei statice după crearea fiecărui obiect.

void main() {intreg x(1); cout<<"Pima instanta= "<<x.a<<" "<<x.b<<endl; // afişează 3 1 intreg y(2); cout<<"A doua instanta= "<<y.a<<" "<<y.b<<endl; // afişează 4 2 intreg z(3); cout<<"A treia instanta= "<<z.a<<" "<<z.b<<endl;} // afişează 5 3

Instanţele partajează copia variabilei statice, indiferent de subprogramul în care au fost create. Se modifică programul anterior astfel: cele trei instanţe ale clasei se creează în subprograme diferite (sb1(), sb2() şi sb3()) care sunt apelate din funcţia main(). La crearea fiecărei instanţe, constructorul incrementează valoarea variabilei statice a, iar la terminarea execuţiei subprogramului, destructorul o decrementează.

void sb1() {intreg x(1); cout<<"Pima instanta= "<<x.a<<" "<<x.b<<endl;} //afişează 3 1 void sb2() {intreg x(2); cout<<"A doua instanta= "<<x.a<<" "<<x.b<<endl;} //afişează 3 2 void sb3() {intreg x(3); cout<<"A treia instanta= "<<x.a<<" "<<x.b<<endl;} //afişează 3 3 void main() {sb1(); sb2(); sb3();}

Exemplul 2. Iniţializarea proprietăţilor (datelor membre) statice se poate face fie în pro-gram, înainte de a se crea prima instanţă, fie la crearea instanţei, cu ajutorul constructorului.

Iniţializarea datei statice în program se face atribuind datei valoarea iniţială. Referirea la data statică se face utilizând operatorul de rezoluţie al domeniului de vizibilitate. Pentru exemplificare se poate modifica subprogramul main() din exemplul precedent, astfel:

void main() {intreg::a=10; intreg x(1); cout<<x.a<<" "<<x.b<<endl;} //afişează 11 1

Pentru a exemplifica iniţializarea variabilei statice cu ajutorul constructorului, se defineşte un nou constructor care va avea doi parametri: cele două date membre ale clasei:

#include <iostream.h> class intreg {public: static int a=2;

34 Programare orientată pe obiecte şi programare vizuală

int b; intreg(int); //constructor intreg(int,int); //constructor ~intreg(); }; //destructor intreg::intreg(int x,int y) {a=x; b=y;} ......................................................... //definiţiile celorlalte metode ale clasei de obiecte intreg void main() {intreg x(1); cout<<"Prima instanta: "<<x.a<<" "<<x.b<<endl; //afişează 3 1 intreg y(10,2); cout<<"A doua instanta: "<<y.a<<" "<<y.b<<endl;} //afişează 10 1

Exemplul 3. Proprietăţile (datelor membre) statice pot fi private. S-a modificat clasa prin declararea proprietăţilor a şi b ca membri privaţi. Accesul la datele private nu este permis decât prin metode publice ale clasei.

Pentru a avea acces la proprietăţile private ale clasei (data statică a şi data b) s-au adăugat două metode noi la clasa intreg: citeste() – pentru citirea valorilor lor de la tastatură şi afişează() – pentru afişarea valorilor lor.

#include <iostream.h> class intreg {public: intreg(int); //constructor intreg(int,int); //constructor ~intreg(); //destructor void citeste(); //metoda de citire a datelor void afiseaza(); //metoda de afişare a datelor private: static int a=2; int b;}; ......................................................... //definiţiile constructorului şi destructorului void intreg::citeste() {cout<<"a= "; cin>>a; cout<<"b= "; cin>>b;} void intreg::afiseaza() {cout<<a<<" "<<b<<endl;} void main() {intreg x(1); cout<<"Prima instanta: "; x.afiseaza(); //afişează 3 1 intreg y(10,2); cout<<"A doua instanta: "; y.afiseaza(); //afişează 10 2 intreg z(3); cout<<"A treia instanta: "; z.afiseaza();} //afişează 11 3

Exemplul 4. Metodele (funcţiile membre) statice nu pot fi folosite decât pentru a manipula proprietăţile statice ale clasei. Metodele statice pot fi apelate înainte de a crea un obiect. Referirea la funcţia statică se poate face şi utilizând operatorul de rezoluţie al domeniului de vizibilitate. Pentru a se aloca spaţiu de memorie, data a este redeclarată ca variabilă globală.

Informatică 35

Pentru exemplificare, s-au adăugat la clasa de obiecte intreg metodele statice: mesaj() – pentru afişarea unui mesaj, valoare() – pentru afişarea valorii variabilei statice a şi init() – pentru atribuirea unei valori variabilei statice a (iniţializarea variabilei cu o valoare transmisă ca parametru):

#include <iostream.h> class intreg {public: intreg(int); //constructor intreg(int,int); //constructor ~intreg(); //destructor void citeste(); //metoda de citire a datelor void afiseaza(); //metoda de afişare a datelor static void mesaj(); //metoda statică de afişare a mesajului static void valoare(); //metoda statică de afişare a valorii static void init(int); //metoda statică de iniţializare private: static int a; int b;}; ......................................................... //definiţia metodelor pentru clasa de obiecte intreg //din exemplele anterioare void intreg::init(int x) {a=x;} void intreg::mesaj() {cout<<"Se creeaza obiecte"<<endl;} void intreg::valoare() {cout<<"Data a are valoarea "<<a<<endl;} int intreg::a; //afişează mesajul void main() {intreg::mesaj(); //afişează mesajul intreg::valoare(); //afişează 0 – valoarea variabilei statice a 0 intreg x(1),y(2); intreg::valoare(); //afişează 0 intreg::init(10); intreg::valoare(); //afişează 10 x.afiseaza(); y.afiseaza(); //afişează 10 1 şi 10 2 x.init(100); x.afiseaza(); y.afiseaza(); //afişează 100 1 şi 100 2 y.init(200); x.afiseaza(); y.afiseaza();} //afişează 200 1 şi 200 2

Creaţi o clasă de obiecte care să conţină următorii membri: a. trei date (două date reale şi o dată întreagă statică); datele vor fi

private; b. trei constructori şi un destructor; c. cinci metode, dintre care două metode statice.

Scrieţi două programe care să folosească această clasă de obiecte. În primul program, variabilei statice i se va aloca memorie prin iniţializare la declarare, iar în al doilea program prin redeclararea ei ca variabilă globală.

36 Programare orientată pe obiecte şi programare vizuală

3.1.4.7. Modificatorul const

Instanţa, fiind o dată (de tipul clasei de obiecte definită), poate fi variabilă (se pot modifica valorile proprietăţilor) sau constantă (nu se pot modifica valorile proprietăţilor). Pentru instan-ţele constante se foloseşte modificatorul const.

Mecanismul prin care se poate folosi o instanţă constantă este următorul. 1. Se declară instanţa ca fiind constantă, astfel:

const nume clasă nume variabilă; 2. În definiţia clasei, pentru fiecare metodă (funcţie) care modifică valorile proprietăţilor

obiectului (numită metodă neconstantă), trebuie să se creeze perechea sa, cu acelaşi nume (numită metodă constantă), care să se refere la instanţele constante. Definirea metodei constante se face astfel: tip rezultat nume_funcţie_neconstantă (listă_parametri) const;

Instanţele variabile vor apela prima metodă (neconstantă), iar instanţele constante vor apela a doua metodă (constantă).

Dacă pentru o metodă neconstantă nu se include în clasa de obiecte şi perechea sa (metoda constantă), iar o instanţă declarată constantă apelează acea metodă, se va genera un mesaj de avertizare din

partea compilatorului, iar programul se va executa. Dacă metoda neconstantă apelată de instanţa constantă modifică valorile proprietăţilor, acele valori vor fi modificate şi în instanţa declarată constantă.

Scop: exemplificarea modului în care pot fi folosite instanţele constante.

Exemplu. Clasa de obiecte numar permite folosirea instanţelor constante. Metoda modifica modifică valoarea proprietăţii a (data privată a de tip întreg). S-au definit două metode modifica : una neconstantă pentru instanţele variabile şi una constantă pentru instanţele constante.

Se creează trei instanţe ale clasei: x, y şi z. Iniţial variabila statică a are valoarea 2. La crearea fiecărei instanţe, constructorul clasei incrementează valoarea datei statice, astfel încât ea ajunge la valoarea 5.

#include <iostream.h> class numar {int a; public: numar(int); //constructorul void modifica(int) const; //metoda constantă void modifica(int); //metoda neconstantă void test(int); void afiseaza();}; numar::numar(int n = 0) {a = n;} void numar::modifica(int n) const {cout<<"Nu se modifica valoarea datei private"<<endl; } void numar::modifica(int n) {cout<<"Se modifica valoarea datei private"<<endl; a=n;}

Atenţie

Mo

dif

icat

oru

l const

Informatică 37

void numar::test(int n) {cout<<"Metoda neconstanta apelata de instanta "<<n<<endl;} void numar::afiseaza() {cout<<"a= "<<a<<endl;} void main() {numar x; //instanţa care apelează metoda neconstantă const numar y; //instanţa care apelează metoda constantă x.modifica(1); //se modifică valoarea variabilei a x.afiseaza(); //afişează a=1 – s-a modificat valoarea x.test(1); y.modifica(2); //nu se modifică valoarea variabilei a y.afiseaza(); //afişează a=0 y.test(2);} //generează mesaj de atenţionare la compilare

Pentru a evidenţia faptul că mecanismul instanţei constante este format din două elemente (declararea instanţei ca fiind constantă şi definirea metodei constante), se adaugă la clasa de obiecte numar metoda adaug() care modifică valoarea datei a, fără a se adăuga şi perechea sa, metoda constantă. Valoarea datei a din instanţa declarată constantă va fi modificată de această metodă.

#include <iostream.h> class numar {int a; public: numar(int); //constructorul void modifica(int) const; //metoda constantă void modifica(int); //metoda neconstantă void test(int); void afiseaza(); void adauga(int); }; ......................................................... //definiţia metodelor pentru clasa de obiecte numar //din exemplele anterioare void numar::adaug(int n) {a=a+n;} void main() {numar x(1); // instanţa declarată variabilă const numar y(2); // instanţa declarată constantă x.adaug(1); x.afiseaza(); //se afişează a=2 y.adaug(2); //generează mesaj de atenţionare la compilare y.afiseaza();} //se afişează a=4

Modificaţi clasa de obiecte şir creată pentru a putea implementa şiruri de caractere, astfel încât să puteţi folosi şi instanţe constante pentru această clasă de obiecte.

Temă