0% au considerat acest document util (0 voturi)
39 vizualizări120 pagini

Poo 04

Documentul prezintă evoluția tehnicilor de programare, de la programarea nestructurată la cea orientată pe obiecte. De asemenea, introduce noțiunea de tipuri abstracte de date și prezintă concepte ale orientării pe obiecte, cum ar fi clase, obiecte și mesaje. Documentul conține și exemple de implementare a acestor concepte în limbaje precum TurboPascal și C++.

Încărcat de

nic_ilie
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca DOC, PDF, TXT sau citiți online pe Scribd
0% au considerat acest document util (0 voturi)
39 vizualizări120 pagini

Poo 04

Documentul prezintă evoluția tehnicilor de programare, de la programarea nestructurată la cea orientată pe obiecte. De asemenea, introduce noțiunea de tipuri abstracte de date și prezintă concepte ale orientării pe obiecte, cum ar fi clase, obiecte și mesaje. Documentul conține și exemple de implementare a acestor concepte în limbaje precum TurboPascal și C++.

Încărcat de

nic_ilie
Drepturi de autor
© © All Rights Reserved
Respectăm cu strictețe drepturile privind conținutul. Dacă suspectați că acesta este conținutul dumneavoastră, reclamați-l aici.
Formate disponibile
Descărcați ca DOC, PDF, TXT sau citiți online pe Scribd
Sunteți pe pagina 1/ 120

Programare orientat pe obiecte

Suport de curs redactat de lector dr. Mihail Cherciu


Universitatea din Bucureti, Facultatea de Matematic i Informatic, Catedra de
Fundamentele Informaticii
10 noiembrie 2008

Cuprins
Introducere
1 O trecere n revist a tehnicilor de programare
1.1 Programare nestructurat
1.2 Programare procedural
1.3 Programare modular
1.4 Un exemplu cu structuri de date
1.4.1 Lucrul cu liste simplu nlnuite
1.4.2 Lucrul cu mai multe liste
1.5 Probleme specifice programrii modulare
1.5.1 Creare i distrugere explicite
1.5.2 Date i operaii decuplate
1.5.3 Lipsa precizrii tipurilor
1.5.4 Strategii i reprezentare
1.6 Programare orientat pe obiecte
1.7 Exerciii
2 Tipuri abstracte de date
2.1 Tratarea problemelor
2.2 Proprieti ale tipurilor abstracte de date
2.3 Tipuri abstracte de date generice
2.4 Notaii
2.5 Tipuri abstracte de date i orientarea pe obiecte
2.6 Exerciii
3 Concepte ale orientrii pe obiecte
3.1 Implementarea tipurilor abstracte de date
3.2 Clase
3.3 Obiecte
3.4 Mesaje
3.5 Rezumat
3.6 Exerciii
4 Alte concepte ale orientrii pe obiecte
4.1 Relaii
4.1.1 Relaia "un fel de"
4.1.2 Relatia "este un/o"
4.1.3 Relaia "parte din"
4.1.4 Relaia "are un/o"

4.2 Motenire
4.3 Motenire multipl
4.4 Clase abstracte
4.5 Exerciii
5 Din nou concepte ale orientrii pe obiecte
5.1 Tipuri generice
5.2 Legare static i dinamic
5.3 Polimorfism
6 Implementarea conceptelor orientate pe obiecte n limbaje de programare
6.1 Programare orientat pe obiecte n TurboPascal
6.2 C++, limbaj creat pentru programarea orientat pe obiecte
6.2.1 Limbajul de programare C
6.2.1.1 Tipuri de date
6.2.1.2 Instruciuni
6.2.1.3 Operatori i expresii
6.2.1.4 Funcii
6.2.1.5 Pointeri i tablouri
6.2.1.6 Pointeri la funcii
6.2.1.7 Un prim program
6.2.2 De la C la C++
6.2.2.1 Incompatibiliti ntre limbajele C i C++
6.2.2.2 Extensii de baz
6.2.2.2.1 Tipuri de date
6.2.2.2.2 Funcii
6.2.2.2.3 Alte extensii de baz
6.2.2.3 Extensii orientate pe obiecte
6.2.2.3.1 Clase i obiecte
6.2.2.3.2 Constructori
6.2.2.3.3 Destructori
6.2.2.3.4 Motenire
6.2.2.3.4.1 Tipuri de motenire
6.2.2.3.4.2 Construcie
6.2.2.3.4.3 Distrugere
6.2.2.3.4.4 Motenire multipl
6.2.2.3. 5 Polimorfism
6.2.2.3. 6 Clase abstracte
6.2.2.3. 7 Suprancrcarea operatorilor
6.2.2.3. 8 Prieteni
6.2. 3 Cum se scrie un program
6.2.4 Exerciii
7 Rezolvrile exerciiilor

7.1 O trecere n revist a tehnicilor de programare


7.2 Tipuri abstracte de date
7.3 Concepte ale orientrii pe obiecte
7.4 Alte concepte ale orientrii pe obiecte
7.5 Mai mult despre C++
Bibliografie

Introducere
4

Orientarea pe obiecte (OO) este o tehnologie de proiectare i programare bazat pe un sistem de concepte
informatice apropiat felului n care este perceput i neleas n mod obinuit lumea real. Aceast abordare
s-a dovedit a fi deosebit de fructuoas pentru elaborarea unor produse informatice performante i uor de
nvat i utilizat. Aceast tehnologie este de asemenea foarte adecvat pentru elaborarea mai uoar a
versiunilor succesive i ale diferitelor variante ale unui produs informatic, utilizarea ei avnd ca rezultat o
cretere semnificativ a eficienei activitii de dezvoltare de software.
Caracteristic acestei metodologii de dezvoltare este analiza prealabil a problemelor puse de o anumit
aplicaie, activitate urmrind elaborarea, printr-un proces de abstractizare, a unor tipuri abstracte de date,
nglobnd date i operaii asupra datelor. Ulterior aceste tipuri abstracte de date se implementeaz n limbaje
de programare adecvate.
Capitolul 1 prezint pe scurt evoluia tehnicilor de programare, scopul fiind acela de a se reliefa diferenele
eseniale dintre programarea OO i celelalte tehnici de programare. n capitolul 2 sunt definite tipurile
abstracte de date i sunt expuse ideile de baz legate de aceast noiune. Capitolele 3-5 prezint in mod
sistematic conceptele orientrii pe obiecte i problemele ridicate de implementarea acestor concepte.
Capitolul 6, cel mai amplu i cu structura cea mai complex, este dedicat prezentrii a dou limbaje n care
sunt implementare conceptele OO: TurboPascal i C++. TurboPascal este tratat pe scurt doar n seciunea
6.1, restul capitolului fiind ocupat cu prezentarea limbajului C++, creat special pentru OO.
Toate capitolele de mai sus conin exerciii. Rezolvrile exerciiilor sunt date n capitolul 7, ultimul al
prezentului curs. Cursul conine i o bibliografie cu 5 titluri.
Expunerea este bazat n mare msur pe prezentarea i tratarea amnunit a unor exemple. Seciunea 6.2.5
prezint n ntregime un proiect privind lucrul cu liste simplu nlanuite.
Cursul urmeaz cu unele modificri i adaptri linia lucrrii [web2], publicat pe Internet, care este un curs
pe care l gsim foarte adecvat scopului nostru. De asemenea, prezentul curs este apropiat spiritului lucrrii
[6]. Dei prezint multe elemente ale limbajelor de programare discutate, mai ales C i C++, cursul de fa
nu poate suplini manualele pentru aceste limbaje. In consecin recomandm consultarea de astfel de
manuale, dintre care n bibliografie sunt trecute [1], [2], [3], [4], [5] i [web4].

1 O trecere n revist a tehnicilor de programare

Acest capitol este o scurt trecere n revist a tehnicilor de programare. Utilizm un exemplu simplu pentru
a ilustra proprietile lor particulare i a indica ideile i problemele cele mai importante care apar n legtur
cu ele.
n linii mari, cineva care nva s programeze parcurge urmtoarele etape:
Programare nestructurat,
Programare procedural,
Programare modular i
Programare orientat pe obiecte.
Prezentul capitol este organizat dup cum urmeaz. Seciunile 1.1, 1.2, 1.3 descriu pe scurt primele trei
tehnici de programare. Prezentm apoi un exemplu simplu de utilizare a programrii modulare pentru a
implementa un modul pentru liste simplu nlnuite (seciunea 1.4). Legat de acesta, enunm cteva
probleme privind acest tip de tehnic n seciunea 1.5. n final, seciunea 1.6 descrie cea de-a patra tehnic
de programare.

1.1 Programare nestructurat


De obicei, cei care nva s programeze ncep cu scrierea unor programe mici i simple constnd numai
dintr-un program principal. Aici, prin "program principal" se nelege o secven de comenzi, sau
instruciuni care modific date care sunt globale n ntregul program. Putem ilustra aceasta ca n Figura 1.1.

program

program
programprincipal
principal
date
date

Figura 1.1
Dup cum desigur tii, aceast tehnic de programare are dezavantaje considerabile dac programele devin
destul de mari. De exemplu, dac aceeai secven de instruciuni este necesar n diferite locuri ale
programului, secvena trebuie copiat. Aceasta a dus la ideea de a se extrage aceste secvene s li se dea
nume i s se ofere o tehnic pentru a le apela i a se reveni din aceste proceduri.

1.2 Programare procedural


Cu programarea procedural putem s grupm secvenele de instruciuni folosite de mai multe ori ntr-un
singur loc. O apelare de procedur este utilizat pentru a se invoca procedura.
Dup ce secvena este prelucrat, execuia programului continu exact dup poziia n care a fost fcut
apelarea (Figura 1.2).

Program principal
Procedur

Figura 1.2
Prin introducerea argumentelor i a procedurilor de proceduri ( subproceduri ) programele pot fi scrise mai
structurat i cu risc mai mic de eroare, deoarece, dac o procedur este corect, de fiecare dat cnd este
utilizat produce rezultate corecte. Prin urmare, n caz de eroare, cutarea acesteia se poate reduce la acele
locuri despre care nu se tie dac sunt corecte.
Un program poate s fie acum considerat ca o secven de apelri de procedur. Programul principal trebuie
s transfere datele la fiecare apelare, datele sunt prelucrate de proceduri i, cnd programul se termin,
datele

care

rezult sunt furnizate. Astfel, fluxul de date poate s fie ilustrat ca

program

un graf ierarhic (arbore) ca n Figura 1.3 (n care


procedurile nu au subproceduri).

program
programprincipal
principal
date
date

procedura
procedura11

procedura
procedura22

procedura
procedura33

Figura 1.3
n rezumat: Avem acum un singur program care este mprit n pri mai mici numite proceduri. Pentru a
face posibili utilizarea procedurilor generale sau a grupurilor de proceduri i n alte programe, ele trebuie
s fie disponibile separat. Din acest motiv s-a conceput programarea modular care permite gruparea
procedurilor n module.

1.3 Programare modular


n programarea modular proceduri care realizeaz mpreun o anumit funcionalitate sunt grupate n
module separate.
Un program nu mai const deci dintr-o singur parte. El este constituit acum din mai multe pri mai mici
care interacioneaz prin intermediul apelrilor de procedur (Figura 1.4).

program
program
programprincipal
principal
date
date

modul
modul1 1

modul
modul2 2

date+date
date+date1 1

date
date++date
date2 2

procedura
procedura11

procedura
procedura22

8
procedura
procedura
33

Figura 1.4
Fiecare modul poate s aib propriile sale date i apare cel mult o dat n ntregul program. Aceasta permite
fiecrui modul s gestioneze o stare intern care este modificat prin apelri ale procedurilor acestui modul.
Pe parcursul executrii programului n fiecare moment fiecare modul are numai o singur stare.

1.4 Un exemplu cu structuri de date


Programele utilizeaz structurile de date pentru a memora datele. Exist mai multe structuri de date, de
exemplu liste, arbori, tablouri, stive, cozi etc. Fiecare din aceste structuri de date pot s fie caracterizate prin
structur i metode de acces.

1.4.1 Lucrul cu liste simplu nlnuite


Bine cunoscute i destul de mult utilizate sunt listele simplu nlnuite care utilizeaz o structur foarte
simpl, constnd din elemente care sunt legate succesiv unul de altul, ca n Figura 1.5.

Figura 1.5
Listele simplu nlnuite furnizeaz metode de acces pentru a aduga un nou element i pentru a erge
elementul din fa. Structurile de date mai complicate pot s utilizeze unele structuri deja existente. De
exemplu, o coad poate s fie structurat ca o list simplu nlnuit. Cozile furnizeaz metode de acces
pentru a aduga un element la sfrit i de a obine primul element (comportare numit n englez "first-in
first-aut", prescurtat FIFO).
Vom prezenta acum un exemplu pe care l utilizm pentru expunerea unor concepte de proiectare. Deoarece
acest exemplu este utilizat numai pentrua ilustra aceste concepte i probleme, el nu este nici complet, nici
optim. n capitolul 6, seciunea 6.2.5 se va face o discuie orientat pe obiecte complet avnd ca scop
proiectarea structurilor de date.
S presupunem c dorim s programm o list ntr-un limbaj de programare ca C sau Modula-2. Deoarece
considerm c lista este o structur de date frecvent utilizat, decidem s o implementm ntr-un modul
separat. n mod normal, aceasta ne oblig s scriem dou fiiere: definiia interfeei i fiierul de
implementare. n acest capitol vom utiliza un pseudocod simplu care poate fi neles fr dificultate. Vom
utiliza comentarii cuprinse n "/*...*/". Definiia interfeei ar putea arta ca mai jos.
/*
* Definitia interfetei pentru un modul care implementeaza
* o lista simplu inlantuita pentru a memora date de orice tip
*/
MODULE Lista-Simplu_Inlantuita-1
BOOL list_initialize();
BOOL list_append(ANY data);
BOOL list_delete();
list_end();
ANY list_getFirst();
10

ANY list_getNext();
BOOL list_isEmpty();
END Lista-Simplu_Inlantuita-1
Declarrile din interfa descriu numai ce parte a informaiei este disponibil i nu cum aceast parte
devine disponibil. Se ascunde informaia privind implementarea n fiierul de implementare. Aplicm
astfel un principiu fundamental n ingineria programrii care const din ascunderea informaiei despre
implementarea efectiv. Aceasta ne d posibilitatea s schimbm implementarea, de exemplu s utilizm un
algoritm mai rapid, dar care consum mai mult memorie, fr s apar necesitatea de a schimba alte
module ale programului ntruct apelrile de proceduri rmn aceleai. Este de asemenea posibil ca
declarrile din interfa s fie fcute ntr-un limbaj de programare, iar implementarea s fie fcut ntr-un alt
limbaj de programare, cu condiia s existe o anumit compatibilitate ntre cele dou limbaje. De exemplu,
n mediul vizual de programare Delphi interfaa i implementarea s-au fcut ntr-o extensie a limbajului
Pascal, iar n mediul de programare C++Builder s-a pstrat implementarea din Delphi, dar s-a creat o
interfa scris ntr-o extensie a limbajului C++.
Revenind la interfaa propus pentru liste, observm c ideea acestei interfee este urmtoarea: nainte de
utilizarea listei trebuie s se apeleze list_initialize() pentru a se iniializa variabilele locale din modul.
Urmtoarele dou proceduri implementeaz operaiile de accesare i modificare a structurii artate mai sus:
append (pentru adugare) i delete (pentru tergere). Se observ c toate procedurile din interfa ntorc
valori al cror tip este specificat naintea antetului declarrii de procedur, deci aceste proceduri pot fi
utilizate ca funcii. Tipul BOOL ntors de unele dintre dintre ele are dou valori i este introdus pentru a
modela valorile de adevr TRUE i FALSE, iar ntoarcerea unei valori de acest tip de o funcie are
semnificaia terminrii cu succes sau cu eec a execuiei funciei. Procedura append necesit o discuie mai
detaliat. Funcia list_append() are un argument date de un tip arbitrar. Acest lucru este necesar deoarece
dorim s utilizm lista n mai multe contexte diferite, deci tipul elementelor care se introduc n list nu este
cunoscut dinainte. Prin urmare, trebuie s utilizm un tip special ANY care ne permite s i atam orice tip
de date (nu orice limbaj de programare are un astfel de tip; n C acesta poate fi introdus cu pointeri). A treia
procedur list_end() trebuie s fie apelat cnd programul nceteaz, pentru a permite modulului s tearg
coninutul variabilelor sale interne. De exemplu putem dori s eliberm memoria alocat.
Cu urmtoarele proceduri list_getFfirst() i list_getNext() este oferit un mecanism simplu de traversare prin
list. Traversarea poate fi fcut utilizndu-se urmtoarea ciclare:
11

ANY data;
data <- list_getFirst();
WHILE data IS VALID DO
faceCeva(data);
data <- list_getNext();
END
Avem acum un modul pentru liste care ne permite s utilizm o list cu orice tip al elementelor. Ce s-ar
ntmpla ns dac am avea nevoie de mai mult de o list ntr-unul din programe?

1.4.2 Lucrul cu mai multe liste


Decidem s reproiectm modulul nostru pentru liste pentru a putea s gestioneze mai mult dect o list.
Prin urmare crem o nou descriere a interfeei care include acum o definiie pentru un tip numit
reprezentant de list. Aceast reprezentant este utilizat n orice procedur dat pentru a identifica n mod
unic lista cu care se lucreaz. Definiia interfeei pentru acest nou modul arat aa:
/*
* Un modul de liste pentru mai mult de o lista.
*/
MODULE Lista-Simplu-Inlantuita-2
DECLARE TYPE reprez_lista_t;
reprez_lista_t list_create();
list_destroy(reprez_lista_t this);
BOOL list_append(reprez_lista_t this, ANY data);
ANY list_getFirst(reprez_lista_t this);
ANY list_getNext(reprez_lista_t this);
BOOL list_isEmpty(reprez_lista_t this);
END Lista-Simplu-Inlantuita-2;
12

Utilizm DECLARE TYPE pentru a introduce noul tip pe care l numim reprez_lista_t. Nu specificm cum
este reprezentat i implementat n mod efectiv acest tip. i de aceast dat ascundem detaliile de
implementare n fiierul de implementare. Se observ diferena fa de versiunea precedent, n care
ascundeam numai funcii, respectiv proceduri. Acum ascundem i informaiile pentru un tip de date definit
de utilizator, numit reprez_lista_t. Utilizm list_create() pentru a obine reprezentanta unei noi liste (vid).
Fiecare procedur conine acum argumentul special this care identific chiar lista respectiv. Toate
procedurile opereaz acum asupra acestei reprezentante n loc s opereze asupra unei liste globale a
modulului. Am putea spune acum c putem crea obiecte list. Fiecare astfel de obiect poate fi identificat n
mod unic prin reprezentanta sa i sunt aplicabile numai acele metode care sunt definite pentru a opera
asupra acestei reprezentante.

1.5 Probleme specifice programrii modulare


Seciunea precedent arat c deja programm avnd n minte unele concepte orientate pe obiecte.
Exemplul listelor care a fost expus evideniaz anumite probleme specifice pe are le vom prezenta acum.

1.5.1 Creare i distrugere explicite


n exemplu, de cte ori dorim s utilizm o list, trebuie s declarm explicit o reprezentant i s executm
o apelare a list_create() pentru a obine una valid. Dup utilizarea listei trebuie s apelm n mod explicit
list_destroy() pentru reprezentanta listei pe care dorim s o distrugem. Dac dorim s utilizm o list n
interiorul unei proceduri, s-i spunem foo(), trebuie s-o facem n urmtorul cadru:
PROCEDURE foo()
BEGIN
reprez_lista_t lista_mea;
lista_mea <- list_create();
/* Se face ceva cu lista_mea */
...
list_destroy(lista_mea);
13

END
S comparm lista cu alte tipuri de date, de exemplu cu un ntreg. ntregii sunt declarai n interiorul unui
domeniu particular (de exemplu, n interiorul unei proceduri). Odat ce au fost definii, ei pot fi utilizai.
Dac domeniul este prsit (de exemplu procedura unde ntregul a fost definit), ntregul este pierdut. El este
creat i distrus n mod automat. Unele compilatoare chiar iniializeaz ntregii nou creai cu o anumit
valoare, de obicei 0 (zero). Care este diferena fa de "obiectele" list? Timpul de via al unei liste este i
el definit de domeniul su, deci ea poate fi creat de ndat ce se intr n domeniu i poate fi distrus de
ndat ce el este prsit. La momentul crerii o list trebuie iniializat ca vid. Prin urmare, am dori s
putem defini o list ca i pe un ntreg. Un cadru pentru aceasta ar putea arta aa:
PROCEDURE foo() BEGIN
reprez_lista_t lista_mea; /* Lista este creata si
initializata */
/* Se face ceva cu lista_mea */
...
END /* lista_mea este distrusa */
Avantajul este c acum compilatorul se ocup de apelrile procedurilor de iniializare i terminare, dup
cum este necesar. De exemplu, aceasta asigur c lista este tears corect, resursele fiind redate
programului.

1.5.2 Date i operaii decuplate


Decuplarea datelor i operaiilor duce n mod obinuit la o structur bazat pe operaii mai degrab dect pe
date: modulele grupeaz mpreun operaiile des folosite (cum sunt operaiile list_...() ). Utilizm
aceste operaii furnizndu-le explicit datele asupra crora ele trebuie s opereze. Structura rezultat pentrru
module este deci orientat spre operaii, mai degrab dect spre datele efective. Se poate spune c operaiile
definite specific datele care s fie utilizate. n orientarea pe obiecte, structura este caracterizat de ctre
date. Alegem reprezentrile de date care corespund cel mai bine cerinelor noastre. n consecin,
programele devin structurate prin date, mai degrab dect prin operaii. Astfel, se urmeaz exact cealalt
cale: datele specific operaiile valide. Acum modulele grupeaz mpreun reprezentrile datelor.

14

1.5.3 Lipsa precizrii tipurilor


n exemplul nostru privind listele trebuie s utilizm tipul special ANY pentru a permite unei liste s conin
datele pe care le dorim. Aceasta implic faptul c tipul utilizat efectiv nu este stabilit de compilator, deci
corectitudinea utilzrii tipului nu poate fi verificat n cursul procesului de compilare. S considerm
urmtorul exemplu n care compilatorul nu poate s verifice corectitudinea:
PROCEDURE foo() BEGIN
UnTipdeDate data1;
UnAltTipdeDate data2;
reprez_lista_t lista_mea;
lista_mea <- list_create();
list_append(lista_mea, data1);
list_append(lista_mea, data2); /* Surpriza! */
...
list_destroy(lista_mea);
END
Este responsabilitatea noastr de a asigura c lista este utilizat n mod corect. O posibil soluie este s se
adauge informaii suplimentare despre tipul fiecrui element al listei. Totui, aceasta implic mai mult efort
i oblig programatorul s cunoasca multe detalii despre datele cu care se lucreaz.
Ar fi de dorit s avem un mecanism care s ne permit s specificm cu ce date va fi definit lista. Funcia
principal a listei este mereu aceeai, indiferent dac ea conine date privind persoane, numere, maini, sau
chiar liste. Ar fi deci convenabil s declarm o nou list cam aa:
reprez_lista_t<Persoana> lista1; /* o lista de persoane */
reprez_lista_t<Masina> lista2; /* o lista de masini */

15

Rutinele corespunztoare pentru liste ar ntoarce automat tipurile corecte de date. Compilatorul ar putea s
verifice automat corectitudinea.

1.5.4 Strategii i reprezentare


Exemplul cu privire la liste implic operaii pentru traversarea listei. n mod normal, pentru acest scop este
definit o variabil numit cursor care indic elementul curent. Aceasta implic o strategie de traversare
care definete ordinea n care elementele structurii de date urmeaz s fie vizitate.
Pentru o structur de date simpl cum este cea de list simplu nlnuit putem concepe doar o singur
strategie de traversare. Plecnd de la cel mai din stnga element se viziteaz succesiv vecinii din dreapta
pn cnd se ajunge la ultimul element. Totui, structuri de date mai complexe, cum sunt de exemplu
arborii, pot fi traversate utiliznd mai multe strategii. Lucrurile pot fi chiar mai complicate, uneori strategiile
de traversare depinznd de contextul particular n care o structur de date este utilizat. n consecin, are
sens s se separe reprezentarea efectiv sau forma structurii de date de strategia sa de traversare. Vom
examina acest fapt mai n detaliu n capitolul 6, seciunea 6.2.5.
Ceea ce am artat cu privire la strategia de traversare este valabil i pentru alte feluri de strategii. De
exemplu, inseria poate fi fcut astfel nct s existe sau nu o ordonare a elementelor.

1.6 Programare orientat pe obiecte


Programarea orientat pe obiecte rezolv unele din problemele menionate. Spre deosebire de alte tehnici,
avem acum o reea de obiecte care interacioneaz, fiecare avnd starea sa proprie. (Figura 1.6)

program
obiect
obiect11
date
date

obiect
obiect44
date
date
obiect
obiect33
date
date

obiect
obiect22
date
date

16

Figura 1.6
S considerm din nou exemplul privitor la liste. Problema pus de programarea modular este c trebuie s
se creeze i s se distrug n mod explicit reprezentantele de liste. Apoi se utilizeaz procedurile modulului
pentru modificarea fiecreia dintre reprezentante.
Spre deosebire de aceasta, n programarea orientat pe obiecte putem avea smultan orict de multe obiecte
list este necesar. n loc s se apeleze o procedur creia s-i fie furnizat reprezentanta corect a unei liste,
se va trimite un mesaj respectivului obiect list. n linii mari, se poate spune c fiecare obiect
implementeaz propriul su modul care s permit, de exemplu, ca mai multe liste s coexiste.
Fiecare obiect trebuie s se creeze i s se distrug n mod automat. n consecin, nu mai este nevoie s se
apeleze n mod explicit o procedur de creare i terminare.
S-ar putea pune ntrebarea: "i ce-i cu asta? Nu este aceasta doar o tehnic modular de programare mai
sofisticat?". Aa pare s fie, dac aceasta ar fi totul n legtur cu orientarea pe obiecte. Din fericire, nu
este. ncepnd cu capitolele urmtoare vor fi introduse noi caracteristici i concepte ale orientrii pe obiecte,
care fac ca programarea orientat pe obiecte s fie cu adevrat o nou tehnic de programare, care s
permit creterea eficienei activitilor de proiectare i programare a aplicaiilor.

1.7 Exerciii
17

1. Exemplul privitor la liste include tipul special ANY pentru a putea permite unei liste s conin date de
orice tip. Presupunei c dorii s scriei un modul pentru o list specializat de ntregi care ofer verificarea
tipului. Tot ceea ce avei este definiia interfeei modulului Lista-Simplu-Inlantuita-2.
(a) Cum arat definiia interfeei pentru un modul Intreg-Lista ?
(b) Discutai problemele care apar prin utilizarea tipului ANY pentru elementele listei n modulul ListaSimplu-Inlantuita-2.
(c) Care sunt soluiile posibile ale acestor probleme?
2. Care sunt principalele diferene conceptuale ntre programarea orientat pe obiecte i alte tehnici de
programare?

2 Tipuri abstracte de date


Unii autori prezint proiectarea i programarea orientat pe obiecte ca fiind proiectarea i programarea de
tipuri abstracte de date i a relaiilor dintre ele. n acest capitol prezentm acest concept fundamental i
investigm mai detaliat conceptele utilizate n legtur cu exemplul privitor la liste din capitolul precedent.

2.1 Tratarea problemelor


Prima activitate pe care trebuie s o desfoare o persoan atunci cnd scrie un program este enunarea
precis a problemei pe care urmeaz s o rezolve. Cel mai frecvent activitatea de programare are ca scop
rezolvarea problemelor din "viaa real". Aceste probleme apar de obicei cu aspect nebulos i primul lucru
care trebuie fcut este nelegerea problemei pentru a se separa elementele necesare de cele inutile.
Rezultatul acestui efort este o prezentare abstract sau un model al problemei. Acest proces de modelare se
numete abstractizare i este ilustrat n Figura 2.1.

Problem
real

Abstractizare
18

Model
Model

Figura 2.1
Faptul c modelul reprezint o prezentare abstract a problemei implic definirea unor proprieti ale
problemei. Aceste proprieti includ date i operaii.
S considerm, de exemplu, c administraia unei instituii dorete un program pentru gestiunea angajailor
instituiei. Apar o serie de ntrebri: ce informaii sunt necesare administraiei? Ce operaii trebuie efectuate?
Angajaii sunt persoane reale care pot fi caracterizate de multe proprieti, ca de exemplu:
nume,
nlime,
data naterii,
sex,
numr de identificare,
numrul camerei unde lucreaz,
culoarea prului,
preocupri extraprofesionale, etc.
Cu certitudine, nu toate aceste proprieti sunt necesare pentru rezolvarea problemei administraiei. n
consecin, trebuie creat un model de angajat pentru aceast problem. Acest model conine numai
proprietile care sunt necesare pentru a satisface cerinele administraiei, de exemplu: nume, data naterii
sex, numr de identificare. Aceste proprieti sunt datele modelului de angajat. Astfel, o persoan real va fi
descris cu ajutorul unui "angajat abstract". Desigur, nu este suficient numai descrierea. Trebuie definite
anumite operaii cu ajutorul crora administraia s poat lucra cu "angajaii abstraci". De exemplu, trebuie
s existe o operaie care s permit crearea unui nou "angajat abstract" atunci cnd o nou persoan este
angajat de instituie. n consecin, trebuie identificate operaiile care s poat fi efectuate cu un angajat
abstract. De asemenea, putem decide ca accesul la datele angajailor s fie fcut numai prin anumite operaii
19

asociate. Aceasta ne permite s ne asigurm c datele sunt n permanen ntr-o stare bun. De exemplu,
putem s verificm dac o anumit dat este corect.
n consecin, abstractizarea este procesul de structurare a unei probleme nebuloase n entiti bine
precizate prin definirea datelor i operaiilor. Prin urmare, aceste entiti combin datele i operaiile, care
nu sunt separate unele de celelalte.

2.2 Proprieti ale tipurilor abstracte de date


Exemplul din seciunea precedent arat c prin abstratizare se creaz o entitate bine precizat care s poat
fi prelucrat n mod specific. O astfel de entitate definete structura de date a elementelor unei anumite
mulimi. De exemplu, fiecare angajat are datele: nume, dat de natere, sex, numr de identificare.
Structura de date poate fi accesat numai cu operaii definite n mod expres. Acest set de operaii este numit
interfa i este exportat de entitate. O entitate cu proprietile descrise mai sus este numit tip abstract de
date (TAD). Figura 2.2 prezinta un TAD care const dintr-o stuctur abstract de date i operaii. Numai
operaiile sunt vizibile din exterior i constituie interfaa.

structur
abstract
de date
tip abstract
de date

tip abstract de date


operaii

interfa

Figura 2.2
Dup ce se definete un TAD, se pot crea instanieri ale sale prin acordarea de valori datelor din structur.
De exemplu, atunci cnd un nou angajat este "creat", structura de date este ncrcat cu valorile efective: se
obine astfel o instaniere a unui angajat abstract. Se pot crea attea instanieri ale unui angajat abstract cte
sunt necesare pentru a descrie fiecare persoan real angajat. Caracteristicile unei persoane reale vor fi
tratate astfel ntr-un mod formalizat.

20

Definiie (Tip abstract de date). Un tip abstract de date (TAD) este caracterizat de urmtoarele
proprieti:
1. Export un tip.
2. Export un set de operaii. Acest set este numit interfa.
3. Operaiile din interfa reprezint unicul mecanism de acces la stuctura de date a tipului.
4. Domeniul de aplicabilitate (termen prin care se desemneaz att domeniul valorilor tipului, ct i
domeniile de definiie ale operaiilor i rezultatele operaiilor) este definit prin axiome i precondiii.
Cu prima proprietate este posibil s se creeze mai mult de o instaniere a unui TAD, aa cum s-a vzut n
exemplul de mai sus, prin declararea unei instanieri a tipului. Reamintim i exemplul anterior referitor la
liste. n prima versiune am implementat o list ca un modul i eram capabili s utilizm numai o singur
list la un moment dat. A doua versiune introduce "reprezentanta" ca o referin la un "obiect list".
Conform celor artate acum, reprezentanta, mpreun cu operaiile definite n modulul listei definete un
TAD Lista.
1. Cnd utilizm reprezentanta definim variabila corespunztoare ca fiind de tipul Lista.
2. Interfaa ctre instanierile tipului Lista este asigurat de ctre fiierul de definiie a interfeei.
3. Deoarece definiia interfeei nu include reprezentarea efectiv a reprezentantei, ea nu poate fi modificat
direct.
4. Domeniul de aplicabilitate este definit de semnificaia (semantica) operaiilor introduse. Axiomele i
precondiiile includ enunuri ca:
"O list vid este o list"
"Fie l=(d1, d2, d3,..., dN) o list. Atunci l.append(dM) are ca rezultat l=(d1, d2, d3,..., dN, dM)."
"Primul element al unei liste poate fi ters numai dac lista nu este vid."
Proprietile tipului Lista sunt rezultatul felului n care noi nelegem dorim i putem s utilizm listele.
Este responsabilitatea noastr de a utiliza instanierile tipului n conformitate cu regulile enunate.

Importana incapsulrii structurilor de date


Principiul de a ascunde structura de date utilizat i de a furniza numai o interfa bine definit este
cunoscut sub numele de incapsulare. De ce este att de important ca structura de date s fie incapsulat?
Pentru a rspunde la aceast ntrebare s considerm urmtorul exemplu matematic n care dorim s definim
un TAD pentru numerele complexe.
n ceea ce urmeaz este suficient s tim c numerele complexe constau din dou pri: partea real i
partea imaginar. Ambele pri sunt reprezentate prin numere reale. Pentru numerele complexe sunt
21

definite mai multe operatii: adunarea, scderea, mulirea, mprirea sunt cteva dintre ele. Axiomele i
precondiiile sunt definite prin definiia numrului complex. De exemplu, exist element neutru pentru
adunare.
Pentru a reprenzenta un numr complex este necesar s se defineasc structura de date care s fie utilizat
de TAD-ul su. Se pot concepe cel puin dou posibiliti de a face acest lucru:
Ambele pri sunt memorate ntr-un tablou cu dou elemente unde primul element indic partea reala si al
doilea element indic partea imaginar a numrului complex c.Dac x reprezint partea real i y partea
imaginar, putem s le accesm prin indicarea de indici ai tabloului: x=c[0] i y=c[1].
Ambele pri sunt memorate ntr-o structur cu dou componente, reprezentate prin nume. Dac numele
componentei reprezentnd partea real este r i cel al prii imaginare este i atunci x i y se obin ca x=c.r i
y=c.i.
Punctul 3 al definiiei TAD spune c pentru orice acces la structura de date trebuie s fie definit un operator.
Exemplul de mai sus pare s contazic aceast cerin. Este acest lucru adevrat?
S considerm din nou cele dou posibiliti de a reprezenta numere complexe i s ne ocupm de partea
real.
n prima versiune x primete valoarea c[0]. n cea de a doua versiune x primete valoarea c.r. n ambele
cazuri x primete ca valoare "ceva". Acest "ceva" difer de structura efectiv de date utilizat. n ambele
cazuri operaia efectiv "primete valoarea" are acelai neles, i anume de a declara c x este egal cu partea
real a numrului complex c: cele dou cazuri nglobeaz aceeai semantic.
Dac ne gndim la operaii mai complexe impactul decuplrii structurii de date de operaii devine i mai
clar. De exemplu, adunarea a dou numere complexe necesit s se efectueze o adunare pentru fiecare parte.
Prin urmare este necesar s se acceseze valoarea fiecrei pri, care este diferit pentru fiecare versiune.
Dac se realizeaz operaia "adunare" se pot incapsula aceste detalii care rmn separate de utilizarea
operaiei i invizibile pentru utilizator. n contextul unei aplicaii, pur i simplu "se adun dou numere
complexe", fr a se ine seama de felul n care aceast operaie este definit efectiv.
O dat creat un TAD pentru numere complexe, fie acesta Complex, el poate fi utilizat n acelai mod ca i
alte tipuri de date, bine cunoscute cum ar fi intregii. n rezumat: separarea dintre structurile de date si
operaii i constrngerea ca structura de date s fie accesat numai printr-o interfa bine definit ne permite
s alegem structurile de date cele mai potrivite n contextul unei aplicaii.

2.3 Tipuri abstracte de date generice


22

TAD-urile sunt utilizate pentru a se defini un nou tip, din care pot fi create instanieri. Aa cum s-a artat n
exemplul cu listele, uneori aceste instanieri pot opera i ele cu diferite tipuri de date. De exemplu, putem s
ne gndim la o list de persoane sau la o list de maini sau chiar la o list de liste. Definiia semantic a
listei este mereu aceeai. Numai tipul datelor elementare se schimb dup tipul datelor cu care lista
opereaz.
Aceast informaie adiional ar putea fi specificat ca un parametru generic care este specificat n
momentul crerii instanierii. Astfel, o instaniere a unui TAD generic este de fapt o instaniere a unei
variante particulare a acestui TAD. O list de persoane poate fi deci declarat, presupunndu-se c s-a
definit n prealabil tipul de date Persoana reprezentnd persoanele:
Lista<Persoana> listaDePersoane;
Parantezele unghiulare cuprind aici tipul de date pentru care trebuie creat o variant a TAD-ului generic
Lista. listaDePersoane ofer interfaa ca orice alt list, dar opereaz asupra instanierilor tipului
Persoana.

2.4 Notaii
Deoarece TAD furnizeaz un mod abstract de descriere a proprietilor unei mulimi de entiti, utilizarea
lor este independent de orice limbaj de programare particular. Introducem deci, urmtoarea notaie. Fiecare
descriere a unui TAD const din dou pri:
Date : Aceast parte descrie structura de date utilizat n TAD ntr-un mod neformalizat.
Operaii
Aceast parte descrie operaiile valide pentru acest TAD, deci descrie interfaa sa. Utilizm operaiile
speciale constructor pentru a descrie aciunile care urmeaz s fie executate cnd o entitate a acestui TAD
este creat i destructor pentru a descrie aciunile care urmeaz s fie executate cnd o entitate este
distrus. Pentru fiecare operaie se dau argumente, precondiii i postcondiii.
Prezentm ca exemplu descrierea unui TAD numit Intreg. Fie k o expresie ntreag:
TAD Intreg este
Date
O secven de cifre precedat opional de un semn plus sau minus.
Notm cu N acest numr ntreg cu semn.
Operaii
constructor

23

Creaz un nou ntreg.


destructor
Distruge un ntreg existent.
sum(k)
Creaz un nou ntreg care este suma lui N i k. n consecin, postcondiia acestei operaii este rez=N+k.
Aceasta este o relaie matematic a crei valoare
este "adevrat" pentru valorile lui sum, N i k
dup ce s-a executat sum.
dif(k)
Creaz un nou ntreg, care este diferena dintre N i k.
Deci postcondiia acestei operaii este rez=N-k.
atr(k)
Atribuie lui N valoarea k. Postcondiia acestei operaii
este N=k.
...
Sfrit
Descrierea de mai sus este specificaia pentru TAD Intreg. Se observ c am utilizat cuvinte pentru numele
operaiilor, n locul unor simboluri matematice, pentru a nu se produce confuzii ntre declaraiile de operaii
(care reprezint sintaxa) i postcondiii (care reprezint semantica). Totui pentru a face ct mai uoar
citirea specificaiilor TAD se pot folosi att cuvinte ct i simboluri.
Limbajele de programare reale pot folosi o alt implementare pentru un TAD. De exemplu se poate
implementa operaia add cu operatorul infixat "+", ceea ce conduce la o reprezentare mai intuitiv pentru
adunarea ntregilor.

2.5 Tipuri abstracte de date i orientarea pe obiecte

24

TAD-urile permit crearea de instanieri cu proprieti i comportare bine definite. n proiectarea i


programarea orientate pe obiecte, TAD-urile se numesc clase. Prin urmare, o clas definete proprietile
obiectelor care sunt instanieri ntr-un mediu orientat pe obiecte.
Programarea orientat pe obiecte este "programarea cu TAD-uri". Se combin funcionalitatea a diferite
TAD-uri pentru a rezolva o problem. Prin urmare, instanierile (obiectele) TAD-urilor (claselor) sunt
create, distruse i utilizate n mod dinamic.

2.6 Exerciii
1. TAD Intreg
(a) De ce nu sunt precondiii pentru operaiile sum i dif ? Evident, descrierea TAD-ului Intreg este
incomplet. Adugai metodele inm (nmulire), imp (mprire) i nc una. Descriei impactul lor
specificnd pre- i postcondiii.
2. Proiectai un TAD Fractie care descrie proprietile fraciilor.
(a) Ce structur de date poate fi utilizat?
(b) Cum se prezint interfaa?
(c) Prezentai cteva axiome i precondiii.
3. Descriei cu cuvintele proprii proprietile tipurilor abstracte de date.
4. De ce este necesar s se includ axiome i precondiii n descrierea unui TAD?
5. Descriei cu cuvintele proprii relaiile ntre:
instaniere i tip abstract de date,
tip abstract de date i tipul de date corespunztor,
instanieri ale unui tip abstract de date.

3 Concepte ale orientrii pe obiecte


n continuare vom prezenta conceptele orientate pe obiecte, din care despre unele s-a amintit mai sus, ntrun mod mai detaliat, artnd i denumirile sub care sunt cunoscute n limbajele de programare orientate pe
obiecte cunoscute.

3.1 Implementarea tipurilor abstracte de date


25

Programele orientate pe obiecte permit implemetarea TAD-urilor. n consecin, atunci cnd un TAD este
implementat, avem o reprezentare a sa pe care o putem utiliza.
S considerm din nou TAD Intreg. Limbajele de programare ca Pascal, C, Modula-2 i altele ofer o
implementare pentru el, numit adesea int sau integer. Atunci cnd este creat o variabil de acest tip, se pot
utiliza operaiile care sunt date pentru ea. De exemplu, se pot aduna doi ntregi:
int i, j, k; /* Se definesc trei intregi */
i = 1; /* Atribuie 1 intregului i */
j = 2; /* Atribuie 2 intregului j */
k = i + j; /* Atribuie suma lui i si j lui k */
n fragmentul de cod surs de mai sus vom evidenia relaia cu TAD-ul Intreg. Prima linie definete trei
instanieri i, j i k ale tipului Intreg. n consecin, pentru fiecare instaniere, operaia special constructor
trebuie apelat. n exemplul nostru, acest lucru este fcut implicit de ctre compilator. Compilatorul rezerv
memorie pentru a pstra valoarea unui ntreg i "leag" numele corespunztor cu aceasta.Dac ne referim la
i, ne referim de fapt la acea zon de memorie care a fost "construit" de definiia lui i opional,
compilatoarele pot alege s iniializeze memoria, de exemplu cu 0 (zero). Urmtoarea linie
i=1;
atribuie lui i valoarea 1. Deci putem s descriem aceast linie cu ajutorul notaiei TAD astfel:
Execut operaia atr cu argumentul 1 asupra instanierii lui Intreg i. Aceasta se scrie aa: i.atr(1)
Avem acum o reprezentare pe dou nivele. Primul nivel este nivelul definirii unui TAD, n care se specific
tot ceea se face cu o instaniere a acestuia prin invocarea operaiilor definite. La acest nivel, pre- i
postcondiiile sunt utilizate pentru a descrie ceea ce se efectueaz.
n exemplul urmtor includem aceste condiii n acolade.
{Precondiie: i=n unde n este orice Intreg }
i.atr(1)
{Postcondiie: i=1}
Reamintim c acum discutm despre nivelul TAD, n consecin, condiiile sunt formulate cu mijloace
matematice.
26

Nivelul al doilea este nivelul de implementare, n care este aleas pentru operaie o reprezentare efectiv
ntr-un limbaj de programare.
n limbajul C semnul "=" (egal) implementeaz operaia atr(). n limbajul Pascal a fost urmtoarea
reprezentare: i:=1; n ambele cazuri este implementat operaia atr. S considerm linia
k=i+j;
Evident "+" a fost ales pentru a implemeta operaia sum. Putem citi partea "i+j" ca "adun valoarea lui j la
valoarea lui i", deci, la nivelul TAD se obine:
{Precondiie: Fie i=n1 i j=n2 cu n1, n2 ntregi particulari }
i.sum(j)
{Postcondiie:i=n1 i j=n2 }
Postcondiia asigur c i i j nu i schimb valorile. Reamintim specificaia lui sum. Ea arat c un nou
Intreg este creat, a crui valoare este suma. Prin urmare, trebuie s dm un mecanism pentru a accesa
aceast nou instaniere. Facem aceasta cu operaia atr aplicat instanierii k.
{Precondiie:Fie k=n unde n este un Intreg oarecare}
k.atr(i.sum(j))
{Postcondiie:k=i+j }
Dup cum se vede, unele limbaje de programare aleg o reprezentare care este aproape identic cu
formularea matematic utilizat n pre- i postcondiii. Acest fapt face dificil departajarea ntre cele dou
nivele.

3.2 Clase
O clas este o reprezentare efectiv a unui TAD. Ea conine detalii de implementare pentru structurile de
date i operaiile utilizate. Lucrm cu TAD-ul Intreg i proiectm propria noastr clas pentru el:
class Intreg {
attributes:
int i
methods:
setValue(int n)
27

Intreg addValue(Intreg j)
}
n exemplul de mai sus, ca i n urmtoarele utilizm o notaie care nu aparine unui anumit limbaj de
programare (este un pseudolimbaj). n aceast notaie class{...} reprezint definiia unei clase. ntre acolade
se afl dou seciuni, una fiind attributes i alta methods care definesc implementarea structurilor de date
i ale operaiilor respectivului TAD. Distingem din nou ntre cele dou nivele utiliznd termeni diferii: la
nivelul de implementare vorbim despre "atribute" care sunt elementele structurii de date de la nivelul TAD.
Aceasta se aplic i la "metode" care sunt implementri ale operaiilor TAD. n exemplul nostru, structura
de date const dintr-un singur element: o secven de cifre cu semn. Atributul corespunztor este un ntreg
obinuit al unui limbaj de programare. Definim numai dou metode setValue() i addValue()
reprezentnd cele dou operaii atr i sum respectiv.
Definiie (Clas). O clas este implementarea unui tip abstract de date (TAD). Ea definete atributele i
metodele care implementeaz structurile de date i operaiile din TAD, respectiv.
Instanierile claselor se numesc obiecte. n consecin, clasele definesc proprietile i comportarea unei
mulimi de obiecte.
Conceperea de TAD-uri i declararea, ntr-un limbaj de programare, a claselor ce le sunt asociate formeaz
obiectul proiectrii, iar conceperea algoritmilor pentru realizarea operaiilor i implementarea acestora prin
definirea metodelor n limbajul de programare formeaz obiectul programrii orientate pe obiecte.
O trstur caracteristic a orientrii pe obiecte este ponderea mare pe care o are faza de proiectare n
realizarea unei aplicaii.

3.3 Obiecte
Reamintim exemplul cu angajaii de la seciunea 2.1. Am vorbit acolo despre instanieri ale unor angajai
abstraci. Aceste instanieri sunt exemple efective de angajat abstract deci ele conin valori efective care
reprezint un anume angajat. Numim aceste instanieri obiecte.
Obiectele sunt identificabile n mod unic printr-un nume. Prin urmare, putem avea dou obiecte distincte cu
acelai set de valori. Acest fapt este ntlnit n limbajele de programare "tradiionale" unde putem avea, de
exemplu, doi ntregi i i j cu aceeai valoare "2".

28

Observm utilizarea lui "i" i "j" n ultima propoziie ca nume pentru doi ntregi. Vom numi totalitatea
valorilor la un moment dat ale atributelor ataate unui obiect starea obiectului.
Definiie (Obiect). Un obiect este o instaniere a unei clase. El poate fi identificat n mod unic prin numele
su i n fiecare moment are o stare care reprezint valorile atributelor sale n acel moment.
Starea unui obiect se schimb n funcie de metodele care sunt aplicate asupra sa. Numim aceast posibil
secven de schimbari ale strii comportarea obiectului.
Definiie (Comportare). Comportarea unui obiect este definit de mulimea metodelor care pot fi aplicate
asupra sa.
Am introdus pn acum dou concepte principale ale orientrii pe obiecte: clas si obiect. Programarea
orientat pe obiecte (P00) este deci implementarea tipurilor abstracte de date sau, mai simplu, scrierea de
clase. n momentul execuiei instanierile acestor clase, obiectele, realizeaz scopul programului
schimbndu-i strile. n consecin, se poate gndi la un program n curs de execuie ca la o colecie de
obiecte. Apare ntrebarea: cum reacioneaz aceste obiecte? Apare necesitatea conceptului de mesaj care
este prezentat n continuare.

3.4 Mesaje
Un program aflat n execuie este o colecie de obiecte n care obiectele sunt create, distruse i
interacioneaz ntre ele. Aceast interaciune este bazat pe mesaje care sunt trimise de la un obiect la altul,
prin care obiectul emitor cere obiectului receptor s aplice o metod asupra sa. Pentru a nelege aceast
comunicare, vom considera din nou clasa Intreg prezentat mai sus. n pseudo-limbajul de programare pe
care l utilizm putem s crem noi obiecte i s aplicm metode asupra lor. De exemplu, putem folosi
Intreg i; /* Defineste un nou obiect intreg */
i.setValue(1); /* Ii atribuie valoarea 1 */

29

pentru a stabili faptul c obiectul i i atribuie valoarea 1. Aceasta este mesajul "Aplic metoda setValue cu
argumentul 1 asupra ta " trimis obiectului i. Am notat trimiterea unui mesaj cu ".". Aceast notaie este
utilizat n C++; alte limbaje orientate pe obiecte ar putea utiliza alte notaii, de exemplu ">".

Trimiterea unui mesaj care cere unui obiect s aplice o metod este similar cu apelul unei proceduri n
limbajele"tradiionale" de programare. Totui, n orientarea pe obiecte se au n vedere obiecte autonome
care comunic unele cu altele schimbnd mesaje. Obiectele reacioneaz atunci cnd primesc mesaje
aplicnd metode asupra lor nile. Ele pot de asemenea s refuze executarea unei metode, de exemplu dac
obiectul apelant nu este autorizat s execute metoda cerut.
n exemplul nostru, mesajul i metoda care trebuie aplicat atunci cnd mesajul este primit au acelai nume.
Am trimis "setValue cu argumentul 1" obiectului i care aplic "setValue(1)". n orientarea pe obiecte
aplicarea sau executarea unei metode se numete invocare.
Definiie (Mesaj). Un mesaj este o cerere ctre un obiect de a invoca una din metodele sale.
Un mesaj conine
numele metodei i
argumentele metodei.
n consecin, invocarea unei metode este o reacie cauzat de recepionarea unui mesaj.
Definiie (Metod). O metod este asociat cu o clas. Un obiect invoc o metod ca o reacie la
recepionarea unui mesaj. Program

3.5 Rezumat
obiect2 care interacioneaz este un principiu fundamental n
obiect3 ca o colecie de obiecte
Considerarea unui program
programarea orientat pe obiecte. Obiectele din aceast colecie reacioneaz la primirea unor mesaje,
schimbndu-i starea n funcie de invocarea unor metode care poate, la rndul ei, s cauzeze trimiterea altor
mesaje ctre alte obiecte. Acest fapt este ilustrat n Figura 3.1.
obiect4

obiect2

30

Figura 3.1
n aceast figur programul const din numai patru obiecte. Aceste obiecte i trimit mesaje unul altuia,
dup cum indic sgeile. Se observ c obiectul al treilea i trimite lui nsui un mesaj.
Cum ne poate ajuta aceast concepie s dezvoltm software? Pentru a rspunde la aceast ntrebare vom
revedea felul cum s-a dezvoltat software cu limbajele procedurale. Primul pas const n a diviza problema n
pri mai mici care s poat fi tratate cu uurin separat. Aceste pri erau concepute ca orientate spre
proceduri, care, n rezolvarea problemei, aveau un rol esenial, n timp ce datele ocupau o poziie secundar.
S considerm, de exemplu, felul n care un caracter apare pe ecranul unui calculator atunci cnd este
apsat o tast. ntr-un mediu procedural urmeaz s fie scrii urmtorii pai necesari pentru a trimite un
caracter pe ecran:
1. ateapt pn la apsarea unei taste
2. citete valoarea tastei
3. scrie valoarea tastei la poziia curent a cursorului.
Nu se pot distinge obiectele ca entiti cu proprieti i comportare bine definite. ntru-un mediu orientat pe
obiecte se pot distinge obiectele tast i ecran aflate n interaciune. Atunci cnd o tast primete mesajul c
trebuie s i schimbe starea i s fie apsat, obiectul su corespunztor trimite un mesaj ctre obiectul
ecran s afieze valoarea asociat tastei.

31

3.6 Exerciii
1. Clas.
(a) Prin ce se deosebee o clas de un TAD?
(b) Proiectai o clas pentru TAD-ul Complex. Ce reprezentani alegei pentru operaiile din TAD?
Justificai alegerea.
2. Obiecte in interaciune.
Alegei din viaa dumneavoastr cotidian o activitate care nu are prea muli pai (de exemplu: privitul la
TV, gtirea unei mncri, etc). Descriei aceast activitate n form procedural i n form orientat pe
obiecte. ncercai s vedei lumea ca fiind alctuit din obiecte. Ce dificulti ntmpinai?
3. Mesaje.
(a) De ce vorbim despre "mesaje" n loc de "apelri de proceduri" ?
(b) Artai cteva mesaje care au sens n mediul Internet (Trebuie deci s identificai obiectele).
(c) De ce termenul "mesaje" este mai potrivit n contextul ultimului exerciiu dect termenul "apelare de
procedur"?

4 Alte concepte orientate pe obiecte


Mai sus s-au prezentat conceptele fundamentale ale POO. n continuare se vor prezenta mai multe detalii.

4.1 Relaii
n exerciiile 2.6.5 ai investigat deja relaiile dintre tipurile abstracte de date i instanieri i le-ai descris cu
propriile cuvinte. Aici vor fi tratate mai detaliat.

4.1.1 Relaia "un fel de"


S presupunem c trebuie s scriem un program pentru a desena. Acest program va permite desenarea a
diferite obiecte: puncte, cercuri, dreptunghiuri, triunghiuri, etc. Pentru fiecare obiect dm o definiie de
clas. De exemplu, clasa Punct definete un punct prin coordonatele sale:
class Punct {
attributes:
int x, y
32

methods:
setX(int nouX)
getX()
setY(int nouY)
getY()
}
Continum s definim clasele programului nostru de desen cu o clas care descrie cercuri. Un cerc este
definit prin centru i raz:
class Cerc {
attributes:
int x, y,
raza
methods:
setX(int nouX)
getX()
setY(int nouY)
getY()
setRaza(int nouaRaza)
getRaza()
}
Comparnd cele dou definiii de clase observm urmtoarele:
Ambele clase au dou date membre x i y. n clasa Punct aceste elemente descriu poziia punctului, iar n
clasa Cerc ele descriu centrul cercului. Astfel, x i y au acelai nteles: n ambele clase descriu poziia
obiectelor asociate prin definirea unui punct.
Ambele clase ofer acelai set de metode pentru a obine i a atribui valoarea celor dou date membre x i
y.
Clasa Cerc "adaug" o nou dat membr raza i metode corespunztoare de acces.
33

Cunoscnd proprietile clasei Punct putem descrie cercul ca un punct plus o raz i metodele de a o
accesa. Deci, un cerc este "un fel de" punct. Totui, un cerc este ceva mai "specializat". Ilustrm acest fapt
n Figura 4.1.

Cerc

un fel de
Punct

Figura 4.1
n aceasta i n urmtoarele figuri clasele sunt reprezentate prin dreptunghiuri. Numele lor ncepe totdeauna
cu majuscule. Sgeata indic direcia unei relaii, deci se va citi "Cerc este un fel de Punct".

4.1.2 Relatia "este un/o"


Relaia precedent este utilizat la nivelul clasei pentru a descrie relaiile dintre dou clase similare. Dac
sunt create obiecte a dou astfel de clase, ne referim la relaia lor ca la o relaie "este un/o". Figura 4.2
ilustreaz aceast relaie. n aceast figur i n urmtoarele, obiectele sunt reprezentate ca dreptunghiuri cu
colurile rotunjite. Numele lor conin numai litere mici.

cerc

este un

punct

Figura 4.2

4.1.3 Relaia "parte din"


Deseori avem nevoie s putem construi obiecte prin combinarea altor obiecte. Cunoatem aceasta deja de la
programarea procedural, unde se construiesc structuri sau negistrri punnd mpreun date de mai multe
tipuri. S revenim la programul nostru de desen. S pesupunem c deja au fost create mai multe clase pentru
diferite figuri. Dac dorim s avem o figur special, pe care o vom numi logo, format dintr-un cerc i un
triunghi (presupunnd c avem deja definit o clas Triunghi) atunci aceast figur poate fi reprezentat de
o nou clas Logo care const din dou pri, Cerc, i Triunghi. Spunem deci despre fiecare din aceste dou
clase c este "parte din" noua clas:

34

class Logo {
attributes:
Cerc cerc
Triunghi triunghi
methods:
set(Punct loc)
}
Ilustrm acest fapt n Figura 4.3.

parte din
Cerc

parte din

Logo

Triunghi

Figura 4.3

4.1.4 Relaia "are un/o"


Aceast relaie este inversa relaiei "parte a". Putem deci s adugm aceast relaie la figura care ilustreaz
relaia "parte a" prin adugarea unor sgei cu sensul schimbat (Figura 4.4).

parte din

parte din
Cerc

are un

Logo

are un

Triunghi

Figura 4.4

4.2 Motenire
Motenirea ne permite s utilizm relaiile "un fel de" i "este un/o". Aa cum se arat aici, clasele care sunt
"un fel de" alt clas au proprietile comune cu acesta. Exemplul nostru privitor la punct i cerc poate fi
rescris specificnd c un cerc motenete de la (n englez inherits from ) punct:

35

class Cerc inherits from Punct {


attributes:
int raza
methods:
setRaza(int nouaRaza)
getRaza()
}
Clasa Cerc motenete toate datele membre i metodele de la Punct. Nu este nevoie ca acestea s fie definite
de dou ori; se utilizeaz numai date i metode deja definite i bine cunoscute. La nivelul obiectelor putem
s utilizm un cerc aa cum am utiliza un punct, deoarece un cerc "este un" punct. De exemplu, putem
defini un obiect cerc i s dm valori coordonatelor centrului su
Cerc uncerc
uncerc.setX(1) /* Mostenit de la Punct */
uncerc.setY(2)
uncerc.setRaza(3) /* Adaugat de Cerc */
"Este un" implic de asemenea c se poate utiliza un cerc oriunde este utilizat un punct. De exemplu, se
poate scrie o funcie sau o metod, de exemplu move(), care s mite un punct n direcia x :
move(Punct unpunct, int deltax) {
unpunct.setX(unpunct.getX() + deltax)
}
Deoarece cercul motenete de la punct, putem utiliza aceast funcie avnd ca argument un cerc pentru a
muta centrul su, deci ntregul cerc:
Cerc uncerc
...
move(uncerc, 10) /* deplaseaza cercul prin */
36

/* deplasarea centrului sau */


S formalizm termenul "motenire":
Definiie (Motenire). Motenirea este mecanismul care permite unei clase A s primeasc proprieti ale
unei clase B. Spunem "A motenete de la B". Obiectele clasei A au astfel acces la atributele i metodele
clasei B fr a fi nevoie ca ele s fie redefinite.
Urmtoarea definiie introduce doi termeni cu care ne putem referi la clasele care particip la relaia de
motenire.
Definiie (Supraclas/Subclas). Dac o clas A motenete de la o clas B, atunci B este numit
supraclas a clasei A. A se numete subclas a clasei B.
Obiectele unei subclase pot fi utilizate acolo unde pot fi utilizate i obiectele supraclasei corespunztoare.
Aceasta se datorete faptului c obiectele subclasei au o comportare comun cu obiectele supraclasei. n
literatur se ntlnesc i ali termeni pentru "supraclas" i "subclas". Supraclasele sunt numite i clase
printe (n englez parent classes ). Subclasele se numesc i clase copil (n englez child classes ) sau clase
derivate (n englez derived classes ). Bineneles, se poate moteni i de la o subclas fcndu-se din
aceasta supraclas unei noi subclase. Aceasta conduce la o ierarhie a relaiei supraclas/subclas. Dac se
deseneaz aceast ierarhie, se obine un graf al motenirii. Un mod obinuit de a desena astfel de scheme
este utilizarea sgeilor pentru a indica motenirea ntre dou clase sau obiecte, sgeata pornind de la
subclas la supraclas. A se vedea Figura 4.5.

37

Punct

motenete de la

Cerc
Figura 4.5
n literatur se ntlnesc i reprezentri grafice n care sgeile au cellalt sens, dup cum diveri autori
nteleg respectiva relaie. n continuarea prezentei lucrri sgeile reprezentnd motenirea nu vor mai fi
etichetate.

4.3 Motenire multipl


Un mecanism important orientat pe obiecte este motenirea multipl. Motenire multipl nu nseamn c
mai multe subclase au n comun aceeai supraclas. De asemenea, nu nseamn c o subclas motenete de
la o clas care este la rndul su o subclas a altei clase. Prin motenire multipl se nelege faptul c o
subclas are mai mult dect o singur supraclas. Aceasta permite subclasei s moteneasc proprietile a
mai mult dect o supraclas i s "combine" proprietaile supraclaselor sale. S considerm de exemplu din
nou programul de desenat. S presupunem c avem o clas Text care permite manipularea convenabil a
textelor. De exemplu, putem avea o metod de a aduga text. n programul nostru am putea s ne propunem
s utilizm aceast clas pentru a aduga text la obiectele care reprezint desene. Ar fi convenabil de
asemenea s utlizm rutine deja existente, cum este move(), pentru a deplasa textul adugat. n consecin,
are sens ca textul desenabil s aib un punct care definete poziia sa n interiorul zonei desenate. Prin
urmare, se deriveaza o nou clas TextDesenabil care motenete proprieti de la Punct i Text, aa cum se
arat n Figura 4.6.

38

Text

Punct

TextDesenabil

Figura 4.6
n pseudo-limbajul nostru scriem aceasta separnd supraclasele aceleiai subclase prin virgule:
class TextDesenabil inherits from Punct, Text {
attributes:
/* Toate mostenite de la supraclase */
methods:
/* Toate mostenite de la supraclase */
}
Putem utiliza obiectele clasei TextDesenabil att ca puncte, ct i ca texte. Deoarece un TextDesenabil "este
un" Punct, putem s l deplasm
TextDesenabil textd
...
textd.move(10)
...
Deoarece "este un" Text, putem s-i adugm alt text:
39

textd.append("Vulpea cea roscata...")


Acum putem defini motenirea multipl:
Definiie (Motenire multipl). Faptul c o clas A motenete de la mai mult de o clas, adic A
motenete de la clasele B1, B2,..., Bn, cu n>1 se numete motenire multipl. Acest fapt poate introduce
conflicte de nume n A dac cel puin dou din supraclasele sale definesc propieti cu acelai nume.
Definiia de mai sus introduce conflictele de nume care au loc dac mai mult o supraclas a unei subclase
utilizeaz acelai nume pentru atribute sau metode. De exemplu, s presupunem c avem o clas Text care
definete o metod setX() care atribuie ca valoare textului o secven de caractere "X". Se pune ntrebarea:
ce va moteni TextDesenabil ? Versiunea din Punct, cea din Text, sau niciuna dintre ele?
Aceste conflicte pot fi soluionate in cel puin dou moduri:
Supraclasele se dau ntr-o anumit ordine i aceasta definete care proprietate va fi accesibil. Celelalte
vor fi "ascunse".
Subclasa trebuie s rezolve conflictul definind explicit cum vor fi utilizate proprietile cu acelai nume
din supraclasele sale.
Prima soluie nu este prea convenabil deoarece ea introduce consecine implicite n legtur cu ordinea n
care clasele motenesc una de la alta. Pentru cazul al doilea, subclasele trebuie s defineasc n mod explicit
proprietile care sunt implicate ntr-un conflict de nume.
Un tip special de conflict de nume este introdus dac o clas D motenete multiplu de la supraclasele B i
C care sunt i ele derivate dintr-o supraclas A. Aceasta conduce la graful de motenire artat n Figura 4.7.

40

Figura 4.7
Se pune ntrebarea: ce proprieti motenete efectiv clasa D de la supraclasele sale B i C ? Unele din
limbajele de programare existente rezolv acest graf de motenire derivnd D cu:
proprietile lui A plus
proprietile lui B i C fr proprietile pe care aceste clase le-au motenit de la A.
n consecin, D nu poate s introduc conflicte de nume cu nume din clasa A. Totui, dac B i C au
proprieti cu acelai nume, D ajunge ntr-un conflict de nume.
O alt posibil soluie este ca D s moteneasc din ambele ramuri de motenire. n aceast soluie, D
posed dou copii ale proprietilor din A : una este motenit de B i cealalt de C.
Dei motenirea multipl este un mecanism puternic orientat pe obiecte, problemele introduse de
conflictele de nume au determinat pe unii autori s l "condamne". Deoarece rezultatele motenirii multiple
pot fi ntotdeauna obinute utiliznd motenirea (simpl), unele limbaje de programare orientate pe obiecte

41

nu permit utilizarea sa. Totui, utilizat cu atenie, n anumite condiii, motenirea multipl reprezint un
instrument elegant i eficient de proiectare orientat pe obiecte.

4.4 Clase abstracte


Utiliznd motenirea putem determina o subclas s ofere aceleai proprieti ca i supraclasele sale. n
consecin, obiectele unei subclase se comport ca i obiectele supraclaselor sale.
Uneori are sens ca numai s se descrie proprietile unei mulimi de obiecte fr s li se cunoasc dinainte
comportarea efectiv. n exemplul nostru cu programul de desenare, fiecare obiect d o metod de a se
desena pe sine. Totui, paii necesari pentru a desena un obiect depind de forma sa. De exemplu, rutina de
desenare a unui cerc difer de rutina de desenare a unui dreptunghi.
S denumim metoda de desenare print(). Pentru a fora fiecare obiect desenabil s includ o astfel de
metod, definim o clas ObiectDesenabil de la care orice alt clas din exemplul nostru motenete
proprietile generale ale obiectelor desenabile:
abstract class ObiectDesenabil {
attributes:
methods:
print()
}
Aici introducem noul cuvnt cheie abstract. El este utilizat pentru a exprima faptul c proprietile trebuie
s fie "redefinite" de clasele derivate pentru a realiza funcionalitatea dorit. Astfel, din punctul de vedere al
claselor abstracte, proprietile sunt numai specficate i nu complet definite. Definiia complet, incluznd
semantica proprietilor, trebuie s fie dat de clasele derivate.
n continuare, fiecare clas din programul nostru de desen trebuie s moteneasc proptieti de la clasa
general pentru obiectele desenabile. Astfel clasa Punct trebuie schimbat i devine:
class Punct inherits from ObiectDesenabil {
attributes:
int x, y

42

methods:
setX(int nouX)
getX()
setY(int nouY)
getY()
print() /* Redefinire pentru Punct */
}
Putem acum s form fiecare obiect desenabil s aib o metod print care s dea funcionaliatea desenrii
obiectului n interiorul zonei de desenare. Supraclasa tuturor obiectelor desenabile, clasa ObiectDesenabil,
nu ofer funcionaliatea pentru desenare. Aceast clas nu este destinat pentru a se crea obiecte din ea. Ea
specific mai degrab proprieti care trebuie definite de ctre fiecare clas derivat. Numim acest tip
special de clas, clas abstract.
Definiie (Clas abstract). O clas A se numete clas abstract dac ea este utilizat numai ca
supraclas pentru alte clase. Clasa A doar specific proprieti. Ea nu este utilizat pentru a se crea
obiecte. Clasele derivate trebuie s defineasc proprietile clasei A.
Clasele abstracte ne permit s structurm graful de motenire. Totui nu dorim s crem obiecte din ele;
dorim numai s exprimm caracteristicile comune ale unei mulimi de clase.

4.5 Exerciii
1. Motenire.
S considerm programul de desenare.
(a) Definii clasa Dreptunghi prin motenire de la clasa Punct. Punctul va indica colul din stnga sus al
dreptunghiului. Care sunt atributele acestei clase? Ce metode adiionale introducei?
(b) Toate exemplele de mai sus sunt bazate pe viziunea bidimensional. Dorim acum s introducem obiecte
tridimensionale cum ar fi: sfere, cuburi, paralelipipede dreptunghice, etc. Proiectai clasa Sfera utiliznd o
clas Punct-3D ale crei obiecte sun punctele n spaiul tridimensional. Specificai rolul punctului n sfer.
Ce relaie utilizai ntre clasele Sfera i Punct-3D?
(c) Ce funcionalitate d move() pentru obiectele tridimensionale? Dai o exprimare ct mai precis.

43

(d) Desenai graful de motenire care s includa urmatoarele clase: ObiectDesenabil, Punct, Cerc,
Dreptunghi, Punct-3D i Sfera.
(e) Privii graful de motenire din Figura 4.8.

Punct

Cerc

Sfer

Figura 4.8
O definiie corespunztoare ar putea fi:
class Sfera inherits from Cerc {
attributes:
int z /* Adauga a treia dimensiune */
methods:
setZ(int nouZ)
getZ()
}
Motivai avantajele/dezavantajele acestei alternative.
2. Motenire multipl. Comparai graful de motenire din Figura 4.9. Aici noi artm c B i C au fiecare o
copie a clasei A.

44

Figura 4.9
Ce conflicte de nume pot s apar? Prezentai cazurile prin exemple simple de clase.

5 Din nou concepte ale orientrii pe obiecte


Continum prezentarea conceptelor orientate pe obiecte cu o scurt introducere privind comparaia dintre
legarea static i dinamic. Introducem polimorfismul ca un mecanism care permite obiectelor s arate ceea
ce urmeaz s se fac n momentul execuiei. Mai nti, dm o scurt prezentare a tipurilor generice.

5.1 Tipuri generice


Cunoatem deja tipurile generice din capitolul 2, unde am discutat despre tipurile abstracte de date
generice. Atunci cnd definim o clas, definim de fapt un tip definit de utilizator. Unele din aceste tipuri pot

45

opera cu alte tipuri. De exemplu, pot fi liste de persoane, liste de maini, liste de numere complexe, sau
chiar liste de liste.
Atunci cnd scriem definiia unei clase, trebuie s putem preciza dac aceast clas definete un tip generic.
Totui, nu tim cu care tipuri va fi utilizat clasa. n consecin, trebuie s putem defini clasa cu ajutorul
unui "lociitor" la care ne referim ca i cnd ar fi tipul asupra cruia opereaz clasa. Astfel, definiia clasei
ne este dat ca un model sau ablon (traducerea termenului englez template) al unei clase efective. Definiia
clasei este creat de fapt atunci cnd declarm un obiect particular. Vom ilustra aceasta cu urmtorul
exemplu. S presupunem c dorim s definim o clas de liste de persoane, maini, sau de alt tip.
template class Lista for T {
attributes:
... /* Structura de date necesara pentru */
/* implementarea listei */
methods:
append(T element)
T getFirst()
T getNext()
bool more()
}
Modelul de clas Lista de mai sus arat ca orice alt definiie de clas. Totui, prima linie declar Lista ca
fiind un model pentru diferite tipuri. Identificatorul T este utilizat ca lociitor pentru un tip real. De exemplu,
append() are un element ca argument. Tipul acestui element trebuie s fie tipul de date cu care o list
efectiv de obiecte este creat. De exemplu, putem s declarm un obiect list de persoane dac exist o
definiie a tipului Persoana:
Lista for Persoana persoanaLista
Persoana oPersoana,
altaPersoana
persoanaLista.append(altaPersoana)
persoanaLista.append(oPersoana)
46

Prima linie declar persoanaLista ca fiind o list de persoane. n acest moment, compilatorul utilizeaz
definiia modelului, nlocuiete fiecare apariie a lui T cu Persoana i creaz o definiie efectiv de clas
pentru ea. Aceasta conduce la o definiie de clas efectiv similar cu urmtoarea:
class Lista {
attributes:
... /* Structura de date necesara pentru */
/* implementarea listei */
methods:
append(Persoana element)
Persoana getFirst()
Persoana getNext()
bool more()
}
Aceasta nu este chiar ceea ce generaz compilatorul. Compilatorul trebuie s asigure c putem crea mai
multe liste pentru diferite tipuri n orice moment. De exemplu, dac avem nevoie de o alt list, de exemplu
pentru maini, putem scrie:
Lista for Persoana persoanaLista
Lista for Masina masinaLista
...
n ambele cazuri compilatorul genereaz o definiie efectiv de clas. Cele dou definiii nu se afl n
conflict deoarece compilatorul genereaz nume diferite. Totui, deoarece acest lucru nu este vizibil pentru
noi, nu vom prezenta aici mai multe detalii. n orice caz, dac declarm o alt list de persoane,
compilatorul poate s stabileasc dac exist deja o definiie efectiv de clas i s o utilizeze sau, dac este
necesar, s o creeze. Astfel,
Lista for Persoana oLista
47

Lista for Persoana altaLista


va crea definiia efectiv de clas pentru oLista i o va reutiliza pentru altaLista. Prin urmare, ambele sunt
de acelai tip. Rezumm aceasta n urmtoarea:
Definiie (Model de clas). Dac o clas A este parametrizat cu un tip de clase B, A se numete model de
clas. Atunci cnd un obiect din A este creat, B este nlocuit cu un tip efectiv. Aceasta permite definiia unei
clase efective bazat pe modelul specificat pentru A si tipul efectiv.
Putem defini modele de clase cu mai mult dect um parametru. De exemplu cataloagele sunt colecii de
obiecte n care fiecare obiect este referit printr-o cheie. Bineneles, un catalog ar trebui s fie capabil s
conin orice tip de obiect. De asemenea, sunt diferite posibiliti pentru chei. De exemplu, ele pot fi iruri
de numere. Prin urmare vom defini un model de clas Catalog care este bazat pe doi parametri-tipuri, unul
pentru chei i unul pentru obiectele coninute.

5.2 Legare static i dinamic


n limbajele de programare cu reguli stricte de stabilire i de utilizare a tipurilor, tipurile trebuie declarate
naintea utilizrii lor. Aceasta implic definirea variabilei, care produce rezervarea spaiului de ctre
compilator. De exemplu, n Pascal o expresie ca
var i : integer;
declar variabila i ca fiind de tipul integer. n plus, ea definete un spaiu de memorie suficient pentru a
pstra o variabil de tipul respectiv.
Cu declaraia se leag numele i de tipul integer. Aceast legtura este adevrat n conformitate cu scopul n
care i este creat. Aceasta permite compilatorului s verifice n timpul compilrii compatibilitatea tipurilor.
De exemplu, urmtoarea atribuire va produce o eroare de incompatibilitate a tipurilor atunci cnd este
compilat:
var i : integer;
...
i := 'sir';
48

Numim acest tip particular de legtur "static" deoarece este fixat n momentul compilrii.
Definiie (Legare static). Dac tipul T al unei variabile este asociat n mod explicit cu numele su N prin
declaraie, spunem c N este legat static de T. Procesul de asociere se numeste legare static.
Exist limbaje de programare care nu utilizeaz variabile cu un tip asociat n mod explicit. De exemplu,
anumite limbaje permit s fie introduse variabile atunci cnd sunt utilizate:
...

/* Nicio aparitie a lui i */

i := 123 /* Crearea lui i ca intreg */


Tipul lui i este cunoscut atunci cnd variabila primete o valoare. n acest caz i este de tipul integer
deoarece i s-a atribuit un numr ntreg. Astfel, deoarece coninutul lui i este un numr ntreg, tipul lui i este
integer.
Definiie (Legare dinamic). Dac tipul T al unei variabile cu numele N este implicit asociat prin
coninutul su, spunem c N este legat dinamic de T. Procesul de asociere se numete legare dinamic.
Cele dou definiii difer prin momentul cnd tipul este legat de variabil. S considerm urmtorul
exemplu (scris n limbajul de programare JavaScript, care permite numai legarea dinamic a variabilelor):
if (conditie() == TRUE)
n = 123;
else
n = 'abc';
Tipul lui n dup instruciunea if depinde de evaluarea lui conditie(). Dac aceasta este TRUE, n este de tip
ntreg, iar n cellalt caz este de tip ir de caractere.

5.3 Polimorfism
49

Polimorfismul (termen de origine greac poli=mai multe morphe=form) permite unei entiti (de exemplu:
variabil, funcie sau obiect) s aib diferite reprezentri (adic sub acelai nume se prezint diferite
coninuturi). Aici vom pune n eviden mai multe tipuri de polimorfism.
Primul tip este similar cu conceptul de legare dinamic. n acest caz, tipul unei variable depinde de
coninutul su ntr-un anumit moment:
v := 123 /* v este intreg */
... /* utilizeaza v ca intreg */
v := 'abc' /* v "comuta" la un sir de caractere */
... /* utilizeaza v ca sir de caractere */
Definiie (Polimorfism (1)). Conceptul de legare dinamic permite unei variabile s primeasc diferite
tipuri n funcie de coninutul su ntr-un anume moment. Aceast particularitate a unei variabile este
numit polimorfism.
Un alt tip de polimorfism poate s fie definit pentru funcii. De exemplu s presupunem c dorim s definim
o funcie isNull() care ntoarce rezultatul TRUE dac argumentul su este 0(zero) i FALSE altfel. Pentru
numere ntregi este uor:
boolean isNull(int i) {
if (i == 0) then
return TRUE
else
return FALSE
endif
}
Totui, dac dorim s facem verificarea pentru numere reale, va trebui s utilizm alt comparaie datorit
problemelor de precizie:
boolean isNull(real r) {
if (r < 0.01 and r > -0.99) then
return TRUE
50

else
return FALSE
endif
}
n ambele cazuri dorim ca funcia s aib numele isNull. n limbajele de programare fr polimorfism
pentru funcii nu putem s declarm aceste dou funcii deoarece numele isNull ar fi definit de dou ori.
Fr polimorfism pentru funcii, dubla definire a numelor ar duce la ambiguitate. Totui, dac limbajul ar
ine cont de argumentele funciilor, ar fi posibil. Astfel funciile (sau metodele) sunt unic definite prin:
numele funciei (sau metodei) i
tipurile din lista de argumente.
Deoarece listele de argumente ale celor doua funcii isNull difer, compilatorul poate s construiasc corect
apelul de funcie utiliznd tipurile efective ale argumentelor:
var i : integer
var r : real
i = 0
r = 0.0
...
if (isNull(i)) then... /* Utilizeaza isNull(int) */
...
if (isNull(r)) then... /* Utilizeaza isNull(real) */
Definiie (Polimorfism (2)). Dac o funcie (sau metod) este definit prin combinarea dintre

numele su i
lista tipurilor argumentelor sale
spunem c avem polimorfism.

51

Acest tip de polimorfism ne permite s reutilizm acelai nume pentru funcii (sau metode) dac listele de
argumente sunt diferite. Uneori acest tip de polimorfism este numit suprancrcare, supraacoperire sau
supradefinire.
Ultimul tip de polimorfism permite unui obiect s-i aleag metodele corecte. S considerm din nou
funcia move(), care are un obiect al clasei Punct ca argument. Am utilizat aceast funcie ca orice obiect
al claselor derivate, deoarece are loc relaia "este un/o".
S considerm o funcie display() care s fie utilizat pentru a afia obiecte desenabile. Declaraia acestei
funcii ar putea fi:
display(ObiectDesenabil o) {
...
o.print()
...
}
Dorim s utilizm aceast funcie cu obiecte ale claselor derivate din ObiectDesenabil:
Cerc uncerc
Punct unpunct
Dreptunghi undreptunghi
display(unpunct) /* Trebuie invocata unpunct.print() */
display(uncerc) /* Trebuie invocata uncerc.print() */
display(undreptunghi)/* Trebuie invocata undreptunghi.print() */
Metoda care va fi utilizat efectiv va fi definit de obiectul o al funciei display(). Deoarece acest lucru
este cam complicat, dm un exemplu mai abstract, dar mai simplu:
class Baza {
attributes:
methods:
52

virtual foo()
bar()
}
class Derivata inherits from Baza {
attributes:
methods:
virtual foo()
bar()
}
demo(Baza o) {
o.foo()
o.bar()
}
Baza obaza
Derivata oderivata
demo(obaza)
demo(oderivata)
n acest exemplu definim dou clase: Baza i Derivata. Fiecare clas definete dou metode: foo() i
bar(). Prima metod este definit ca virtual (prin cuvntul cheie virtual). Aceasta nseamn c dac
aceast metod este invocat, definiia sa va fi evaluat prin coninutul obiectului.
Definim apoi o funcie demo() care are un obiect din Baza ca argument. Prin urmare, putem s utilizm
aceast funcie cu obiecte ale clasei Derivata ca i cum ar fi valabil relaia "este un/o". Apelm aceast
funcie cu un obiect din Baza i cu un obiect din clasa Derivata, respectiv s presupunem c funciile foo()
i bar() sunt definite astfel nct singurul lucru pe care l fac este s-i scrie numele i clasa n care sunt
definite. Atunci ieirea este:

53

foo() din Baza apelata.


bar() din Baza apelata.
foo() din Derivata apelata.
bar() din Baza apelata.
De ce? S vedem ce se execut. Prima apelare a funciei demo() utilizeaz un obiect din Baza. Astfel,
argumentul funciei este "ncrcat " cu un obiect al clasei Baza. Atunci cnd se invoc metoda foo()
funcionalitatea ei real bazata pe coninutul curent al obiectului corespunztor o. De aceast dat, el este un
obiect din Baza. n consecin, este apelat metoda foo() aa cum este definit n clasa Baza. Apelarea
metodei bar() nu este afectat de coninutul obiectului. Ea nu este declarat ca virtual. n consecin,
metoda bar() este apelat n domeniul clasei Baza. A doua apelare a funciei demo() are un obiect din
Derivata ca argument. Astfel, argumentul o este ncrcat cu un obiect din Derivata. Totui, o nsui nu
reprezint dect partea Baza a obiectului furnizat oderivata.
n sfrit, apelarea metodei foo() este evaluat prin examinarea coninutului lui o, deci este apelat n
interiorul domeniului clasei Derivata. Pe de alt parte, bar() este evaluat tot n domeniul clasei Baza.
Definiie (Polimorfism (3)). Obiectele supraclaselor pot s fie ncrcate cu obiecte ale subclaselor lor.
Operatorii i metodele subclaselor pot s fie definii pentru a fi evaluai n dou contexte:
1. Pe baza tipului declarat al obiectului (numit i tip static), ceea ce conduce la o evaluare n interiorul
domeniului supraclasei.
2. Pe baza tipului coninutului obiectului (numit i tip dinamic), ceea ce conduce la o evaluare n interiorul
domeniului subclasei care le conine.
Al doilea tip este numit polimorfism.

6 Implementarea conceptelor orientate pe obiecte


n limbaje de programare
n acest capitol prezentm felul cum sunt tratate chestiunile privind programarea orientat pe obiecte n
dou limbaje de programare care, datorit calitilor lor, sunt printre cele mai cunoscute i utilizate:
TurboPascal, pentru care vom face o scurt prezentare, i C++, pentru care prezentarea va fi mult mai
ampl.

54

6.1 Programare orientat pe obiecte n TurboPascal


Se presupune cunoscut limbajul TurboPascal.
n TurboPascal corespondentul clasei este tipul declarat de utilizator object, a crui declarare are sintaxa
asemntoare cu a tipului record. Componentele unui tip object pot fi att date (reprezentnd atribute), ct
i proceduri sau funcii (reprezentnd metode). Declararea unui tip object este permis numai n partea de
declarare a modulului (deci nu n corpul funciilor i al procedurilor). Definiiile metodelor asociate trebuie
plasate n acelai domeniu dup declararea tipului object. Referirea la componentele unei variabile avnd
un tip object se face cu sintaxa nume_variabil.nume_componenta[(lista_de_ argumente_efective)].
Exemplul de mai jos prezint un program n care se definete i se utilizeaz un tip object pentru numere
complexe.
type complex=object
{date}
re, im:real;
{declarari de metode}
procedure comp(a, b:real);
procedure atr(v:complex);
function sum_re(v:complex):real;
function sum_im(v:complex):real;
procedure sum(v:complex; var s:complex);
procedure citeste;
procedure scrie;
end;
{implementarea metodelor}
procedure complex.comp(a, b:real);
begin
re:=a;
im:=b
end;
55

procedure complex.atr(v:complex);
begin
comp(v.re, v.im)
end;
function complex.sum_re(v:complex):real;
begin
sum_re:=re+v.re
end;
function complex.sum_im(v:complex):real;
begin
sum_im:=im+v.im
end;
procedure complex.sum(v:complex; var s:complex);
begin
s.re:=re+v.re;
s.im:=im+v.im
end;
procedure complex.citeste;
begin
read(re, im); readln
end;
procedure complex.scrie;
begin
write('re=', re, ' im=', im); writeln
end;

56

{program principal}
var c1, c2:complex;
begin
c1.citeste;
c2.atr(c1);
c2.sum(c1, c1);
c1.scrie;
c2.scrie;
readln
end.
Dm mai jos o scurt prezentare a celorlalte elemente de programare orientat pe obiecte pe care
TurboPascal le ofer.
Posibilitatea ca un tip object s moteneasc un alt tip object, declaraia ncepnd cu o specificare a
tipurilor cu sintaxa:
tip_obiect object tip_obiect_mostenit
urmat de declararea datelor i metodelor.
Posibilitatea de a se suprancrca metodele motenite prin definirea n obiectul motenitor unor metode
cu acelai nume, dar cu un corp diferit i, eventual, cu alt list de argumente.
Posibilitatea ca unele din metodele unui tip obiect, s fie declarate virtuale prin adugarea cuvntului
cheie virtual la declaraia metodei, ca de exemplu:
procedure nume(argumente); virtual;
ceea ce oblig la declararea ca virtuale a metodelor cu acelai nume ale tipurilor object motenitoare.
Efectul acestor declararaii este rezolvarea tuturor apelurilor procedurilor virtuale n momentul execuiei i
nu al compilrii, adic rezolvarea apelurilor este dinamic. Acest mecanism de apel trebuie s fie pregtit n
timpul execuiei prin apelarea nainte de apelarea oricrei metode virtuale a unei metode declarat cu
cuvntul cheie constructor (n loc de procedure sau function).

57

Metodele care nu sunt virtuale se numesc statice. Este interzis suprancrcarea metodelor virtuale cu
metode statice.
Pentru mai multe informaii asupra limbajului TurboPascal, ca i asupra programrii orientate pe obiecte n
acest limbaj, recomandm s se consulte [1].
Din scurta prezentare de mai sus se observ absena implementrii unor concepte importante ale orientrii
pe obiecte cum sunt: clase abstracte, modele de clase, motenire multipl. Principiul incapsulrii datelor i
metodelor este de asemenea incomplet implementat, lipsind elmentele care s permit limitarea i
diferenierea accesului la componentele unui tip object.
Aceste lipsuri se explic prin faptul c limbajul TurboPascal a fost realizat de firma Borland prin extensia
limbajului standard Pascal, care este un limbaj procedural tipic, astfel nct s fie nglobate i celelalte
tehnici de programare: programarea modular i programarea orientat pe obiecte. Conceptele orientrii pe
obiecte s-au implementat ns incomplet, probabil pentru c limbajul de baz standard Pascal are
caracteristici care fceau foarte dificil o abordare mai ampl: ar fi trebuit creat un limbaj cu totul nou, mult
ndeprtat de limbajul de baz.
Situaia este substanial diferit n cazul limbajului C++, cu toate c i acest limbaj a fost realizat prin
extensia "limbajului de baz" C. Flexibilitatea deosebit a limbajului C a permis ca adugarea facilitilor de
programare orientat pe obiecte, care este principala raiune a crerii limbajului C++, s se fac ntr-un mod
mult mai complet i eficient, fr s se piard posibilitatea ca limbajul s fie utilizat doar ca un limbaj
procedural obinuit.

6.2 C++ - limbaj creat pentru programarea orientat pe


obiecte
Aceast seciune este o introducerie n C++. Mai nti facem o scurt prezentare a limbajului de programare
C, apoi artm cum C++ extinde limbajul C prin lrgirea sistemului de tipuri, prin alte cteva faciliti i,
cel mai important, prin concepte orientate pe obiecte. Prezentm, de asemenea, nainte de extensiile aduse
de C++, i incompatibilitile ntre C++ i C.

6.2.1 Limbajul de programare C


Dezvoltat spre sfritul anilor '70, C a obinut un succes uria datorit dezvoltrii sistemului de operare
UNIX, care a fost aproape n ntregime scris n acest limbaj. Spre deosebire de alte limbaje de nivel nalt, C
a fost scris de programatori pentru programatori. Astfel, el permite uneori formulri care, n alte limbaje,

58

cum ar fi Pascal, sunt interzise din cauza influenei lor nefaste asupra stilului de programare. Oricum, dac
este utlizat cu o anumit rigoare, C este un limbaj la fel de bun ca i multe alte limbaje de nivel nalt.
Comentariile n C sunt cuprinse ntre /*...*/.

6.2.1.1 Tipuri de date


Tabelul 6.1 descrie tipurile de date ale limbajului C n implementarea Turbo C++ i Borland C++.
Lungimea specificat este msurat n octei. Domeniul este o consecin a lungimii. Se pot obine
informaii asupra lungimii unui tip cu operatorul sizeof.
Tabelul 6.1

Tip
<signed> char
double

Descriere

Lungime

Domeniu/Precizie

Octet cu semn

128..127

Numr n dubl
precizie

ca (1, 7X10308..1, 7X10308)


precizie 15 cifre

<signed> <short>int |
<signed> short
float

ntreg cu semn

Numr n virgul
mobil

215.. 215 1
ca (3, 4X1038..3, 4X1038)
precizie 7 cifre

<signed> long <int>

ntreg lung cu
semn

231.. 231 1

long double

Numr n cea mai


mare precizie

10

ca (3, 4X104932..3, 4X104932)


precizie 19 cifre

unsigned char

Octet fr semn

0..255

unsigned <short>
<int>
unsigned long <int>

ntreg fr semn

0.. 216 1

ntreg lung fr
semn

0.. 232 1

Implementarea Unix are urmtoarele deosebiri fa de tabelul de mai sus:


-

tipul short <int> are lungimea 2, domeniul 215.. 215 1 i coincide cu <signed> short <int>;

59

tipul unsigned short <int> are lungimea 2 i domeniul 0.. 216 1;

tipul <signed> int are lungimea 4, domeniul 231.. 231 1 i coincide cu <signed> long <int>;

tipul unsigned <int> are lungimea 4, domeniul 0.. 232 1 i coincide cu tipul unsigned long <int>;

exist modificatorul long long i tipul <signed> long long <int> cu lungimea 8 i domeniul 263.. 263
1;

exist tipul unsigned long long <int> cu lungimea 8 i domeniul 0.. 264 1;

nu exist tipul long double.

Variabilele acestor tipuri sunt definite simplu prin specificarea tipului naintea numelui:
int un_int;
float un_float;
long long un_intreg_foarte_lung;
Cu struct se pot combina mai multe tipuri definite. n alte limbaje acest tip structurat este numit record:
struct data_s {
int ziua, luna, an;
} oData;
Definiia de mai sus pentru oData este de asemenea declaraia unei structuri numit data_s. Putem defini
alte variabile de acest tip referindu-ne la structur prin nume:
struct data_s altaData;
Nu este necesar s denumim structurile. Dac le omitem numele, nu putem s le reutilizam. Totui, dac
denumim o structur, putem doar s o declarm, fr a defini o variabil:
struct timp_s {
int ora, minut, secunda;
};

60

Putem s utilizm aceast structur aa cum s-a artat pentru altaData. Acest lucru este foarte asemntor
cu definiiile de tipuri din alte limbaje, unde un tip este declarat naintea definirii unei variabile de acest
tip. Variabilele trebuie definite naintea utilizrii lor. Aceste definiii trebuie s apar naintea oricrei
instruciuni, deci ele formeaz partea superioar a unui bloc de instruciuni.

6.2.1.2 Instruciuni
C conine toate tipurile uzuale de instruciuni. Instruciunile sunt terminate prin ";". Putem s grupm mai
multe instruciuni n blocuri prin plasarea lor ntre acolade. n interiorul fiecrui bloc putem s definim noi
variabile:
{ /* Incepe blocul exterior */
int i; /* Defineste un i */
i = 1; /* Atribuie lui i valoarea 1 */
{ /* Incepe un nou bloc */
int i; /* Defineste un nou i */
i = 2; /* care primeste valoarea 2 */
} /* Inchide blocul */
/* Aici i este 1 din nou din blocul exterior */
}
Tabelul 6.2 listeaz toate instruciunile
Tabelul 6.2

Instruciune

Descriere

Instruciunea de efect nul.

expr;

Instruciunea expresie. Efectueaz expresia

break;

ncetez execuia instruciunii repetitive sau


sau switch.

continue;

Utilizat numai n instruciuni repetitive.


Produce trecerea la ciclul urmtor.

expr.

61

do

Execut instr ct timp expr are


valoarea diferit de 0 (orice valaore nenul
este interpretat ca valoare de adevr
TRUE).

instr
while(expr)
for ([expr1]; [expr2]; [expr3];)
instr

Prescurtare pentru o instruciune while n


care expr1 este iniializare, expr2 este
condiie, iar expr3 se efectueaz dup
fiecare
ciclu.
Lipsa
expr3 se
interpreteaz ca valoare de adevr TRUE.

goto eticheta;

Transfer la poziia din aceeai definiie de


funcie indicat de eticheta. Destinaia
este eticheta urmat de ":".

if (expr) instr1 [else instr2];

Instruciune condiional. Dac valoarea de


adevr a expr este TRUE se execut
instr1, altfel se execut instr2
(dac exist).

return [expr];

Revenire din funcie. Dac funcia nu


ntoarce valoare, expr nu este obligatorie
i nu se utilizeaz, altfel este obligatorie i
valoarea sa este valoarea ntoars.

switch (expr) {

Valoarea expr este comparat cu vlorile


const-expr i (pentru i de la 1 la n)
i se execut secvena de instruciuni
lista_
de_
instr
i,
continundu-se cu executarea urmtoarelor
secvene de instruciuni, pn la execuia
unei instruciuni break. Dac nicio
clauz case nu corespunde, se execut
secvena lista_ de_ instr de la
default (dac exist).

case const-expr 1: lista_de_instr1

case const-expr: lista_de_instr


[default: lista_de_instr]
}
while (expr) instr

Execut instr ct timp expr are


valoarea de adevr TRUE.

Instruciunea for este singura care difer esenial de instruciunile for cunoscute n alte limbaje. O form
special are i instruciunea switch, care execut lista de instruciuni ncepnd cu "eticheta" case
corespunztoare valorii expresiei testate pn la execuia unei instruciuni break. De asemenea, posibilitatea
de a se termina execuia instruciunilor repetitive cu instruciunea break i posibilitatea de a se ntrerupe
ciclul curent cu instruciunea continue sunt elemente care pot contribui la construirea unor programe

62

eficiente. Toate celelalte instruciuni difer de cele din alte limbaje mai mult sau mai puin prin sintax. Mai
jos artm dou blocuri care au absolut aceeai funcionalitate. Unul utilizeaz bucla while, iar cellalt
instruciunea for :
{
int ix, suma;
suma = 0;
ix = 0; /* initializare */
while (ix < 10) { /* conditie */
suma = suma + 1;
ix = ix + 1; /* pas */
}
}
{
int ix, suma;
suma = 0;
for (ix = 0; ix < 10; ix = ix + 1)
suma = suma + 1;
}
Cele de mai sus pot fi bine nelese dac se ine seama de nc o proprietate caracteristic a limbajului C:
instruciunea de atribuire este n acelai timp o expresie.

6.2.1.3 Operatori i expresii


n C aproape orice este o expresie. De exemplu, instruciunea de atribuire "=" ntoarce valoarea operandului
din dreapta. Ca un "efect colateral" atribuie valoarea operatorului din stnga. Astfel,
ix = 12;
atribuie lui ix valoarea 12 (presupunnd c ix are un tip compatibil). Deoarece atribuirea este de asemenea o
expresie, putem combina mai multe atribuiri; de exemplu:

63

kx = jx = ix = 12;
S-a efectuat ceea ce, n stilul comun celor mai multe limbaje de programare s-ar fi efectuat prin
ix = 12;
jx = 12;
kx = 12;
n C valoarea 0 (zero) reprezint valoarea de adevr FALSE. Orice alt valoare reprezint TRUE. De
exemplu, funcia standard strcmp(), cu dou iruri ca argumente, ntoarce -1 dac primul este mai mic
dect al doilea, 0 dac sunt egale i 1 dac primul este mai mare dect al doilea. Pentru a testa dac dou
iruri str1 i str2 sunt egale, se utilizeaz adesea instruciunea condiional:
if (!strcmp(str1, str2)) {
/* str1 este egal cu str2 */
}
else {
/* str1 nu este egal cu str2 */
}
Semnul de exclamare reprezint operatorul boolean NOT (negatie). Astfel, expresia este evaluat TRUE
numai dac strcmp() ntoarce 0. Expresiile se construiesc prin combinarea termenilor (sau operanzilor) i a
operatorilor. Termenii pot fi constante, variabile, sau expresii. Operatorii limbajului C sunt cei cunoscui din
alte limbaje, la care se adaug civa care reprezint prescurtri ale unor combinaii ale altor operatori.
Tabelul 6.3 listeaz toi operatorii disponibili. Coloana a doua arat prioritatea lor, numerele mai mici
indicnd o prioritate mai nalt i numerele egale, aceeai prioritate. Coloana a treia conine aritatea (adic
numrul de operanzi). Aritatea primului operator (apelul de funcie) este varabil, depinznd de felul n care
este declarat primul operand (funcie sau pointer la funcie) printr-un prototip. Ultima coloan listeaz
ordinea de evaluare.
Tabelul 6.3
64

Preceden
1
1
1
1
2
2
2
2
2
2
2
2
2
3
3
3
4
4
5
5
6
6
6
6
7
7
8
9

Operator

Descriere

()
[]

Operatorul apelare de funcie


Operatorul de referire la element de
tablou prin indice
->
Selecie indirect
.
Selecie direct
!
Negaie (NOT) logic
~
Negaie (NOT) la nivel de bit
+
Plus unar
Minus unar
++
Preincrementare sau postincrementare
-Predecrementare sau postdecrementare
&
Adres
*
Indirectare (dereferire)
sizeof (lungimea operandului n octei)
*
nmulire
/
mprire
Restul mpririi ntregi (modulo)
%
+
Adunare
Scdere
<<
Deplasare la stnga pe bii
>>
Deplasare la dreapta pe bii
<
Operatorul "mai mic"
<=
Operatorul "mai mic sau egal"
>
Operatorul "mai mare"
>=
Operatorul "mai mare sau egal"
==
Operatorul "egal"
!=
Operatorul "diferit"
&
AND (I; conjuncie) pe bii
^
XOR (SAU exclusiv) pe bii
65

Aritate

Ordine

variabil
2

din stnga
din stnga

2
2
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2

din stnga
din stnga
din dreapta
din dreapta
din dreapta
din dreapta
din dreapta
din dreapta
din dreapta
din dreapta
din dreapta
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga
din stnga

10
11
12
13

14
14

=
op=

&&
||
?:

OR (SAU; disjuncie) pe bii


AND (I; conjuncie) logic
OR (SAU; disjuncie) logic
Operator condiional (ternar: a ? x :
y ntoarce x dac a are valoarea TRUE
i y altfel)

2
2
2
3

din stnga
din stnga
din stnga
din dreapta

Atribuire simpl
2
Atribuire combinat cu un operator binar 2
op, unde op poate fi unul din operatorii

din dreapta
din dreapta

* / % + - & ^ | << >>

15

Operatorul "virgul" (evaluare succesiv)

din stnga

Probabil c cele mai multe din aceste operaii v sunt deja cunoscute. S observm mai nti c operatorii
booleeni &, |, ^ au prioritate inferioar operatorilor == i !=$. n consecin, dac dorim s scriem expresii
care conin aceti operatori, trebuie, aa ca n exemplul de mai jos, n care se face verificarea unei
succesiuni de bii cu ajutorul unei "mti":
if ((sir_de_biti & MASCA) == MASCA) {
...
}
s se nchid operaiile booleene ntre paranteze. Operatorii de incrementare/ decrementare ++ i -- pot fi
explicai prin urmtorul exemplu. Dac avem urmtoarea secven de instruciuni
a = a + 1;
b = a;
putem utiliza operatorul de preincrementare b = ++a;
De asemenea, dac avem instruciunile n ordinea invers

66

b = a;
a = a + 1;
putem utiliza operatorul de postincrementare
b = a++;
Astfel operatorul de preincrementare mai nti incrementeaz variabila asociat, apoi ntoarce noua valoare,
n timp ce operatorul de postincrementare ntoarce mai nti valoarea, apoi incrementeraz variabila.
Aceleai reguli se aplic operatorilor de pre- i postdecrementare --.
Apelrile de funcii, atribuirile imbricate i operatorii de incrementare/ decrementare produc efecte
colaterale n momentul aplicrii. Aceasta poate introduce dependene de compilator deoarece ordinea de
evaluare n anumite situaii este dependent de compilator. S considerm urmtorul exemplu care
demonstreaz acest lucru:
a[i] = i++;
Depinde de ordinea n care compilatorul este prevzut s evalueze expresiile din stnga i din dreapta
operatorului de atribuire dac vechea sau noua valoare a lui i este utilizat ca indice n tabloul a.
Operatorul condiional ?: este o prescurtare pentru o instruciune if utilizat n mod obinuit. De exemplu,
pentru a atribui variabilei max cea mai mare dintre valorile variabilelor a, b, se poate utiliza:
if (a > b)
max = a;
else
max = b;
Aceasta se poate scrie mai scurt:
max = (a > b) ? a : b;

67

Urmtorul operator neobinuit este cel de atribuire combinat cu alt operator. Utilizm adesea atribuiri de
urmtoarea form:
expr1 = (expr1) op (expr2)
de exemplu
i = i * (j + 1);
n aceste atribuiri valoarea din stnga apare i n partea dreapt, imediat lng operatorul de atribuire. C
permite ca aceste tipuri de atribuire s fie prescurtate ca:
i*= j + 1;
Putem face asta cu aproape toi operatorii binari. De reinut c operatorul de mai sus implementeaz forma
lung, dei "j+1" nu este n paranteze. Urmtorul operator neobinuit este operatorul ", ". El poate fi
explicat cel mai bine printr-un exemplu:
i = 0;
j = (i += 1, i += 2, i + 3);
Acest operator i evalueaz argumentele de la stnga la dreapta i ntoarce valoare expresiei din dreapta.
Astfel, n exemplul de mai sus operatorul evaluez mai nti "i+=1" care, ca un efct colateral,
incrementeaz valoarea lui i; apoi este evaluat expresia "i+=2" care adaug 2 la 3, deci i primete valoarea
3. A treia expresie este evaluat i valoarea sa este ntoars ca rezultat al operatorului. Astfel, j primete
valoarea b.
Operatorul virgul introduce o anumit "capcan" atunci cnd se utilizeaz tablouri n-dimensionale cu n>1.
O eroare frecvent este utilizarea unei liste de indici separai prin virgule:
int matrice[10][5]; // matrice bidimensionala
int i;

68

...
i = matrice[1, 2]; // NU FUNCTIONEAZA!!
i = matrice[1][2]; // BINE
n realitate, la prima atribuire, lista de indici separai prin virgule este interpretat ca operator virgul, ceea
ce conduce la o atribuire a adresei celei de a treia secvene de cinci elemente a matricii! Limbajul C ignor
valorile neutilizate ale expresiilor. De exemplu, se poate scrie secvena:
ix = 1;
4711;
jx = 2;
dar linia a doua, dei ntoarece o valoare (4711), nu are nici un efect. Trecnd peste aceste lucruri oarecum
stranii, vom lua n considerare lucrurile cu adevrat utile. n continuare vom discuta despre funcii.

6.2.1.4 Funcii
Deoarece C este un limbaj procedural, el permite definirea funciilor. Procedurile sunt simulate prin funcii
care nu ntorc valori, mai exact, ntorc un tip special numit void. Funciile sunt declarate ca i variabile, dar
trebuie s aib argumente cuprinse ntre paranteze (chiar dac nu exist argumente, parantezele trebuie
specificate):
int sum(int to);/* Declaratia functiei sum cu un argument */
int bar(); /* Declaratia functiei bar fara argumente */
void foo(int ix, int jx)/* Declaratia functiei foo cu doua
argumente */
Pentru a defini efectiv o funcie, se adaug corpul su: C permite numai transmiterea argumentelor prin
valori. n consecin nu se poate schimba valoarea unui argument al unei funcii. Dac dorii s transmitei
un argument prin referin, trebuie s transmitei ca valoare a argumentului corespunztor al funciei, adresa
variabilei a crei valoare dorii s fie schimbat, deci trebuie s utilizai pointeri.

6.2.1.5 Pointeri i tablouri

69

Una din cele mai obinuite probleme ale programrii in C (i uneori n C++) este nelegerea pointerilor i
tablourilor. n C (C++) aceste concepte sunt foarte nrudite, cu unele mici (dar eseniale) diferene.
Se declar un pointer punnd un asterisc ntre tipul de date i numele variabilei sau a funciei:
char *strp; /* strp este 'pointer la char' */
Accesm coninutul unui pointer utiliznd din nou un asterisc:
*strp = 'a'; /* Un singur caracter */
Ca i n alte limbaje, trebuie s furnizm un anumit spaiu pentru valoarea la care trimite un pointer. Un
pointer la caractere poate fi utilizat pentru a indica un ir de caractere. irurile de caractere n C se termin
prin caracterul special NULL (0 sau constanta de tip char '0'). Astfel, putem avea iruri de orice lungime.
irurile sunt nchise ntre ghilimele:
strp = "salut";
n acest caz compilatorul adaug automat caracterul NULL la sfrit. Astfel, strp trimite la un ir de 6
caractere: primul este 's', al doilea 'a', etc. Putem accesa caracterele cu un indice n strp:
strp[0] /* s */
strp[1] /* a */
strp[2] /* l */
strp[3] /* u */
strp[4] /* t */
strp[5] /* \0 */
Primul caracter este egal i cu *strp, ceea ce poate fi scris i ca *(strp+0). Aceasta reprezint ceea ce se
numeste aritmetica pointerilor i este una din cele mai puternice caracteristici ale limbajului C. Astfel, avem
urmatoarele egaliti:
*strp == *(strp + 0) == strp[0]
70

*(strp + 1) == strp[1]
*(strp + 2) == strp[2]
...
De reinut c aceste egaliti sunt adevarate pentru orice tip de date. Adunarea nu este orientat spre octei,
ci este orientat spre lungimea respectivului tip de pointer!
Pointerul strp poate s trimit i la alte locaii de memorie. Destinaia sa este schimbtoare. Spre deosebire
de aceasta, tablourile sunt pointeri fici. Ei trimit spre o zona predefinit de memorie care este specificat
ntre paranteze drepte:
char str[6];
Putem s considerm str ca o constant pointer care trimite la o zon cu 6 caractere. Nu este permis s fie
utilizat astfel:
str = "salut"; /* EROARE */
deoarece aceasta ar nsemna schimbarea pointerului pentru a trimite la 's'. Trebuie s copiem irul n zona
de memorie pentru date. Putem utiliza o funcie din biblioteca C standard, numit strcpy().
strcpy(str, "salut"); /* E bine */
De reinut c, exceptnd atribuirea, se poate folosi str oriunde poate fi folosit un pointer de caracter,
deoarece el este un pointer (fix).

6.2.1.6 Pointeri la funcii


O caracteristic a limbajului C este posibilitatea de a se defini variabile pointer la funcie. O astfel de
variabil poate primi ca valoare adresa la care este plasat n memorie n timpul execuiei programului codul
unei funcii care are un anumit prototip (constnd, dup cum se tie, din tipul rezultatului i tipurile
argumentelor funciei) specificat prin declaraia de variabil. Sintaxa unei astfel de declaraii este:
tip

(*nume) (lista_de_tipuri)
71

unde tip este un tip oarecare sau void, reprezentnd tipul de rezultat ntors funciile a cror adres poate fi
valoare a pointerului declarat, nume identificatorul care desemneaz pointerul, iar lista_de_tipuri
este vid sau conine un singur tip sau este o succesiune de tipuri separate de virgule, aa cum se utilizeaz
ntr-o declaraie (prototip) de funcie.
Iat cteva exemple de astfel de declaraii:
int (*pf-int1)(float, int); // este intors tipul int
void (*pointf)(int, int, *char); // nu exista rezultat
double (*pf-d)(int); // este intors tipul double
char *(*pf-int1)(); // este intors pointer la char
Daca se declar o funcie, de exemplu
int

f-int(float, int);

un pointer declarat conform cu prototipul acestei funcii poate primi ca valoare, cu ajutorul operatorului de
atribuire, adresa din memorie a funciei, astfel:
pf-int1=f-int;
Se observ c numele unei funcii este un pointer constant care are ca valoare adresa funciei.
Cu un pointer la funcie se poate construi, utilizndu-se operatorul de apel de funcie, o expresie al crei
efect este apelarea funciei a crei adres a fost n prealabil atribuit ca valoare pointerului. Astfel, expresia
pf-int1(1.5, -2)
poate fi utilizat, dup executarea atribuirii de mai sus, n locul expresiei
f-int(float, int).
Pointerii la funcie permit elaborarea unor programe deosebit de flexibile, ei permind ca un program s
stabileasc n mod dinamic (adic n timpul execuei) dac va fi apelat o funcie sau alta ntr-un anumit
72

punct al su. De asemenea, ei lrgesc considerabil posibilitile de programare deoarece pot fi utilizai ca
argumente ale unor funcii sau ca elemente ale unor structuri.
Se pot defini tablouri de pointeri la funcii i poiteri la pointeri la funcii, aa cum ilustrm prin exemplele
de mai jos:
int (*tab-pf[5]) (char *, int); // tablou cu 5 elemente pointer la
// functii
float (**ppf) (double, int, int); // pointer la pointer la functie
ceea ce poate aduga elemente importante de flexibilitate programelor.

6.2.1.7 Un prim program


Introducem aici primul program, destul de des folosit: un program care afieaz "Salut, lume!" pe ecran:
#include <stdio.h>
/* Variabilele globale trebuie sa fie aici */
/* Definitiile de functii trebuie sa fie aici */
int main() {
puts("Salut, lume!");
return 0;
} /* main */
Prima linie arat oarecum straniu. Explicarea ei necesit anumite informaii despre cum sunt tratate
programele C (i C++) de ctre compilator. n linii mari pasul de compilare este mprit n doi pai. Primul
pas este numit "preprocesare" i este utilizat pentru pregtirea unui cod C pur. Acest pas trateaz prima linie
ca pe o comanda de a include un fiier numit stdio.h n surs. Parantezele unghiulare indic faptul c fiierul
urmeaz s fie cutat pe calea standard de cutare configurat pentru calculator. Fiierul nsui furnizeaz
anumite declaraii sau definiii pentru intrrile/ieirile standard. De exemplu, declar o funcie numit
put(). n pasul de preprocesare sunt, de asemenea, terse comentariile.

73

n cel de al doilea pas, codul C generat n primul pas este compilat i se creaz cod executabil. Fiecare cod
executabil trebuie s conin o funcie numit main(). Aceast funcie este apelat atunci cnd programul
este pornit (adic lansat n execuie). Acest funcie ntoarce un ntreg care reprezint starea de ieire a
programului.
Funcia main() poate s aib argumente care reprezint argumentele din linia de comand. i artm aici,
dar nu vom explica felul cum pot fi utilizai. Pentru astfel de explicaii se pot consulta [2], [3]:
#include <stdio.h>
int main(int argc, char *argv[]) {
int ix;
for (ix = 0; ix < argc; ix++)
printf("Argumentul meu nr. %d este %s\n", ix, argv[ix]);
return 0;
} /* main */
Primul argument, argc ntoarce numrul de argumente din linia de comand. Argumentul al doilea, argv
este un tablou de iruri de caractere (reamintim c irurile sunt reprezentate prin pointeri la caractere. Astfel,
argv este un tablou de pointeri la caractere).

6.2.2 De la C la C++
Aceast seciune prezint mai nti unele incompatibiliti ntre limbajele C i C++, apoi extensiile
limbajului C care au fost introduse de C++. De asemenea sunt artate conceptele orientate pe obiecte i
implementarea lor.

6.2.2.1 Incompatibiliti ntre limbajele C i C++


Dei C++ a fost conceput ca extensie a limbajului C, exist construcii n C care nu sunt admise sau sunt
interpretate diferit n C++. Numim aceste diferene ntre cele dou limbaje incompatibiliti. Prezentm
mai jos pe scurt pe cele mai importante dintre ele.
1. Constantele caracter (caracter cuprins ntre apostrofuri) au n C tipul int, iar n C++ au tipul char i
sunt reprezentate pe un octet. Prin urmare,
sizeof('a')
74

are n C valoarea 2, n implementarea Borland C++, respectiv valoarea 4 n implementarea Unix, iar n C ++
are valoarea 1 n ambele implementri.
2. n C++ un salt cu instruciunea goto nu poate trece peste o declaraie cu iniializare dect dac trece
peste ntregul bloc al declaraiei. De exemplu, programul
/* In C este permis, dar in C++ nu este permis saltul peste o
declaratie cu initializare daca nu se face peste intregul bloc
care o contine.*/
#include <stdio.h>
void main(void)
{ int a;
printf("a="); scanf("%d", &a);
if (a>10) goto et; // eticheta din blocul interior
{ int b=10;
printf("b="); scanf("%d", &b); a+= b;
et: printf("a=%d b=%d\n", a, b);
}
}

este corect n C, dar nu este corect n C ++, deoarece instruciunea goto trece peste declaraia cu
iniializare din blocul interior, fr a trece peste tot acest bloc.
3. C++ admite numai conversia de tip de la un pointer de un tip oarecare la tipul void, nu i conversia n
sens invers, spre deosebire de C. n C++ conversia n sens invers se poate face numai cu operatorul de
conversie explicit (notat (tip) ). Dm cteva exemple:
void* pv;
int* pi;
pv=pi; /*corect in C si in C++*/
pi=pv; /*corect in C, incorect in C++ */
75

pi=(int*)pv; /* corect in C si in C++ */


4. n C++ este oblgatorie existena prototipului sau a definiiei unei funcii naintea apelrii sale i se
accept numai prototipuri de funcii n care se specific lista argumentelor. Prin urmare, inexistena listei
de argumente n prototip este interpretat n C++ ca o declaraie de funcie fr argumente, nefiind
necesar ca lista s fie nlocuit cu cuvntul cheie void. n C se pot specifica argumente efective la
apelarea unei funcii al crei prototip nu specific argumente, fiind necesar, pentru ca lista argumentelor
s fie considerat vid, s se specifice void explicit n prototip.
5. Limbajul C permite ca funcia main() s fie apelat de o alt funcie i chiar de ea nsi, dar n C++
acest lucru este interzis. Dm mai jos dou exemple de programe: unul n care funcia main() este
apelat de alt funcie
/* In C functia main() poate fi apelata de alta functie (apel recursiv
indirect). In C++ acest lucru este interzis. */
#include<stdio.h>
#include<conio.h>
#include<process.h>
#include<stdlib.h>
int a=10;
void main(void);
void f(void) { a--; main();}
void main(void)
{
if (a) {printf("\na=%d ", a); f();} else exit(getch());
}
i unul n care este apelat de ea nsi
/* In C functia main() se poate apela singura.
In C++ acest lucru este interzis. */
#include<stdio.h>
#include<conio.h>
76

#include<process.h>
#include<stdlib.h>
int a=10;
void main(void)
{
if (a) {printf("\na=%d ", a); a--; main();} else exit(getch());
}
Dac sunt compilate cu un compilator de C, compilarea se ncheie cu succes i, la execuie fiecare din
programe va afia
10 9 8 7 6 5 4 3 2 1
i se va opri dup apsarea unei taste. Un compilator de C++ nu va accepta nici unul din aceste programe.
6. Dup cum se va vedea la 6.2.3 (Paii compilrii), programele pot fi alctuite din mai multe fiiere surs.
Un identificator declarat (i, eventual iniializat) ntr-un fiier poate avea aceeai semnificaie n ntregul
program (ceea ce nseamn c va ocupa o anumit zon de memorie n programul executabil) sau va fi
recunoscut doar la nivelul fiierului n care este declarat (deci, dac va aprea o nou declarare a sa n alt
fiier, acestei noi declarri i va corespunde alt zon de memorie n programul executabil). Se spune c
identificatorii din prima categorie sunt cu legtur extern, iar cei din a doua categorie sunt cu
legtur intern. Atribuirea de memorie n funcie de tipul de legtur se face prin aciunea
compilatorului i a editorului de legturi. Pentru variabilele globale care sunt declarate cu modificatorul
const (adic nu i pot schimba valoarea) legtura implicit este n C extern, iar n C++ intern.
Legtura intern pentru o variabil global se declar cu modificatorul static, iar cea extern cu
modificatorul extern. n concluzie, exist anumite echivalene ntre declaraiile de variabile globale cu
modificatorul const, pe care le artm mai jos:
/* C */

/* C++ */

static const int n; const int n;


const int n;

extern const int n;

77

6.2.2.2 Extensii de baz


Aici vom prezenta extensii ale conceptelor deja introduse de C. C++ prezint un nou comentariu care este
introdus de dou slash-uri (//) i care ine pn la sfritul liniei. Putem utiliza amndou stilurile de
comentariu, ca n exemplul de mai jos:
/* comentariul C poate sa includa // si poate sa se
intinda peste mai multe linii. */
// /* Acesta este stilul de comentariu C++ */ pana la sfarsitul liniei
n C variabilele trebuie definite la nceputul unui bloc sau n afara oricrui bloc. C++ ne permite s definim
variabilele oriunde n interiorul unui bloc. Astfel, variabilele i obiectele pot fi definite acolo unde sunt
utilizate.

6.2.2.2.1 Tipuri de date


C++ introduce un nou tip de date numit referin. Putem gndi c variabilele de acest tip sunt
"pseudonime" ale unor variabile sau obiecte "reale". Aa cum un pseudonim nu poate exista fr
corespondentul su real, nu pot fi definite referine singure. Caracterul & (ampersand) este utilizat pentru
definirea referinelor. De exemplu:
int ix; /* ix este variabla "reala" */
int &rx = ix; /* rx este "pseudonim" pentru ix */
ix = 1; /* de asemenea rx == 1 */
rx = 2; /* de asemenea ix == 2 */
Referinele pot fi utilizate ca argumente de funcii i valori de ntoarcere. Aceasta permite s se transmit
argumentele prin referin sau s se ntoarc o modalitate de a accesa variabilele sau obiectele calculate.
Tabelul 6.4 ne ofer o trecere n revist a declaraiilor posibile. Ea nu este complet, prin faptul c nu
conine toate combinaiile posibile. Totui acestea sunt probabil cele mai frecvent utilizate.

78

Tabelul 6.4

Declaraie
tip nume;
tip nume[];
tip nume[n];
tip *nume;
tip *nume[];
tip (*nume[]);
tip (*nume)[];
tip &nume;
tip nume( );
tip *nume( );
tip *(nume( ));
tip (*nume)( );
tip &nume( );

nume este
de tipul tip
tablou (deschis) de tip
tablou cu n elemente de tipul tip
(nume[0], nume[1], , nume[n-1])
pointer la tipul tip
tablou (deschis) de pointeri la tipul tip
tablou (deschis) de pointeri la tipul tip
pointer la tablou (deschis) de tipul tip
referin la tipul tip
funcie care ntoarce tipul tip
funcie care ntoarce pointer la tipul tip
funcie care ntoarce pointer la tipul tip
pointer la funcie care ntoarce tipul tip
funcie care ntoarce referin la tipul tip

Exemplu
int num;
int num[];
int num[5];
int
int
int
int
int
int
int
int
int
int

*num;
*num[];
*(num[]);
(*num)[];
&num;
num();
*num();
*(num());
(*num)();
&num();

n C i C++ se poate utiliza modificatorul const pentru a se declara anumite aspecte ale unei variabile (sau
obiect) ca fiind constante. Urmtorul tabel 6.5 listeaz posibilele combinaii i descrie semnificaia lor.
Tabelul 6.5

Declaraie
const tip nume = valoare;
tip *const nume = valoare;
const tip *nume = valoare;
const tip *const nume = valoare;

nume este
constant de tipul tip
pointer constant la tipul tip
pointer (variabil) la constant de
tipul tip
pointer constant la constant de
tipul tip

Vom investiga unele exemple de variabile declarate cu modificatorul const mpreun cu utilizarea lor. S
considerm urmtoarele declaraii:
int i; // un intreg obisnuit
79

int *ip; // pointer neinitializat la


// intreg
int * const cp = &i; // pointer constant la intreg
const int ci = 7; // intreg constant
const int *cip; // pointer la intreg constant
const int * const cicp = &ci; // pointer constant la constanta
// intreaga
Urmtoarele atribuiri sunt corecte :
i = ci; // atribuie o constanta intreaga unui intreg
*cp = ci; // atribuie o constanta intreaga unei variabile
// care este referita de un pointer constant
cip = &ci; // schimba valoarea unui pointer la constanta
// intreaga
cip = cicp; // unui pointer la constanta intreaga i se
// atribuie valoarea unui pointer constant la un
// intreg constant
Urmtoarele atribuiri sunt incorecte:
ci = 8; // nu se poate schimba valoarea unui intreg
// constant
*cip = 7; // nu se poate schimba un intreg constant referit
// de un pointer
cp = &ci; // nu se poate schimba valoarea unui pointer
// constant
ip = cip; // aceasta ar permite sa se schimbe valoarea
// intregului constant *cip cu *ip
Cnd se utilizeaz referinele, trebuie avute n vedere anumite particulariti. S vedem urmtorul exemplu
de program:

80

#include <stdio.h>
int main() {
const int ci = 1;
const int &cr = ci;
int &r = ci; // creaza un intreg temporar pentru referinta
// cr = 7; // nu se poate atribui valoare unei referinte
// constante
r = 3; // schimba valoarea intregului temporar
printf("ci == %d, r == %d, cr==%d\n", ci, r, cr);
return 0;
}
Dac se compileaz cu GNU C++, compilatorul afieaz urmtorul avertisment:
conversion from 'const int' to 'int &' discards const
Ceea ce se ntmpl de fapt este crearea de ctre compilator a unei variabile ntregi temporare cu valoarea
lui ci cu care este iniializat referina lui r. Prin urmare, cnd se schimb r, valoarea variabilei temporare
este cea care se schimb, i nu ci. Aceast variabil temporar exist att timp ct exist referina r.
Alte compilatoare (de exemplu TURBO C++, BORLAND C++) fac acelai lucru, dar nu afieaz nici un
avertisment.
Referina cr este definit ca read-only (referin constant). Aceasta interzice utilizarea ei n partea stng a
atribuirilor. Dac am terge marcajul comentariu (//) din faa liniei care conine o astfel de atribuire
interzis, compilatorul ar da un mesaj de eroare.

6.2.2.2.2 Funcii
C++ permite suprancrcarea aa cum s-a definit la sectiunea 5.3. De exemplu, putem s definim dou
funcii max(), din care una ntoarce cel mai mare dintre doi ntregi, iar cealalt ntoarce cel mai mare dintre
dou iruri de caractere.
#include <stdio.h>

81

int max(int a, int b) {


if (a > b) return a;
return b;
}
char *max(char *a, char * b) {
if (strcmp(a, b) > 0) return a;
return b;
}
int main() {
printf("max(19, 69) = %d\n", max(19, 69));
printf("max(abc, def) = %s\n", max("abc", "def"));
return 0;
}
Programul din exemplul de mai sus definete aceste dou funcii care difer prin listele lor de argumente,
deci sunt definite dou funcii diferite. Prima apelare a funciei printf() din funcia main() produce o
apelare a primei versiuni a max(), deoarece are ca argumente doi ntregi. La fel, a doua apelare a funciei
printf() produce o apelare a celei de a doua versiuni a max().
Se pot utiliza referine pentru a se obine o funcie cu un pseudonim (alias) al unui argument efectiv de la
apelarea unei funcii. Acest fapt permite s se schimbe valoarea argumentului efectiv i este cunoscut i la
alte limbaje cu transfer prin referin a argumentelor:
void foo(int prinValoare, int &prinReferinta) {
prinValoare = 42;
prinReferinta = 42;
}
void bar() {
int ix, jx;

82

ix = jx = 1;
foo(ix, jx);
/* ix == 1, jx == 42 */
}
Utilizarea
plasarea

referinelor

simbolului

&

la
dup

ntoarcerea
tipul

efectuat cu valoarea ntoars

valorilor

valorii

funciilor

ntoarse.

acest

se

specific

caz

orice

prin

operaie

de funcie va utiliza referina la variabila plasat dup instruciunea

return care a produs oprirea execuiei funciei i ntoarcerea valorii. Aceasta permite ca astfel de apleuri de
funcii s fie utilizate i n membrul stng al unei atribuiri.
Exemplu:
#include <iostream.h>
int a;
int & schimba(int b)
{
a+=b;
cout<<"in schimba a="<<a<<endl;
return a; // se intoarce referinta la variabila globala a schimbata de
// functie
}
int main()
{
schimba(2)+=3; // se schimba valoarea lui a de catre functie si de
// catre atribuire
cout<<"in main a="<<a<<endl;
}
ntoarcerea referinelor la variabile locale (sau temporare, de exemplu rezultatul unor expresii) din blocul
funciei este incorect (semnalat de obicei de compilator cu un avertisment) deoarece n acest caz variabila
local este eliminat imediat dup execuia funciei, deci este eliminat i referina, iar valoarea nu mai este
ntoars n funcia apelant. De notat c, dac nu s-ar fi folosit referin ca valoare de ntoarcere, varabila
local sau temporar respectiv ar fi fost eliminat dup transmiterea rezultatului n funcia apelant.

83

6.2.2.2.3 Alte extensii de baz


Pe lng extensiile de baz prezentate mai sus, C++ ofer i altele, dintre care prezentm mai jos pe cele
mai frecvent folosite, avnd n vedere i utilitatea acestora n scrierea de programe mai eficiente i mai
clare.
Utilizarea identificatorilor declarai const n iniializri i dimensiuni de tablouri
Expresiile care conin identificatori declarai const pot fi folosite n C++ pentru iniializri i dimensionri
de tablouri, fapt interzis n C. Exemple:
const int n=20, m=90;
int a=2*n;
float vect[n+1]; mat[n+2][3*m]
Argumente cu valori implicite
n C++ exist posibilitatea declarrii funciilor cu valori implicite ale argumentelor. La apelarea unei astfel
de funcii se poate omite specificarea argumentelor efective pentru acei argumente formale care au declarate
valori implicite i se transfer automat valorile efective:

#include<iostream.h>
void func (int, int=10);
void main()
{ cout<<"func(5, 99)"
func(5, 99); //apel normal
cout<<"func(5), ";
func(5); //apel cu un singur argument
}
void func(inti, intj)
{ cout <<"argumente:i="<<i;
cout<<"j="<<j<<"\n";
}

84

Programul afieaz:

func(5, 99), argumente: i=5j=99


func(5), argumente:i=5j=10

Un apel fr argumente (func(); ) este eronat deoarece argumentul i nu are asociat o valoare implicit.
Alocarea/eliberarea dinamic a memoriei folosind operatorii new i delete
Alocarea i eliberarea dinamic a memoriei se face n C cu anumite funcii de bibliotec (malloc(),
free() i altele). Pe lng acestea, n C++ se introduc operatorii unari new (pentru alocare) i delete (pentru
eliberare). Operatorul new poate fi utilizat n urmtoarele forme:
pt_la_tip = new tip;
pt_la_tip = new tip(val_init);
pt_la_tip = new tip[n];
unde:
- tip = tipul variabilei dinamice, care poate fi un tip de date oarecare;
-

pt_la_tip = o variabil pointer de tip tip;

val_init = expresie cu a crei valoare se iniializeaz variabila dinamic.

Varianta a treia se utilizeaz pentru alocarea memoriei pentru n elemente de tipul tip (un tablou cu n
elemente) i n este o expresie ntreag. Se pot aloca i tablouri multidimensionale, specificnd toate
dimensiunile. Iniializarea tablourilor nu este posibil. Operatorul new aloc spaiul necesar, corespunztor
tipului, i ofer ca rezultat:
-dac alocarea a reuit un pointer de tipul (tip *) coninnd adresa zonei de memorie alocate; -n caz contrar
(memorie insuficient sau fragmentat), un pointer cu valoarea NULL (=0).
Exemplul urmtor ilustreaz sintaxa pentru toate variantele:
int n=15, *pi1, *pi2, *pit1, *pit2;
85

pi1=new int;
//variabila intreaga initializata
pi2=new int(23)
//variabila intraga initializata cu 23
pit1=new int[330];
//tablou unidimensional int
pit2=new int[n][3*n];
//tablou bidimensional int
Operatorul delete are sintaxa
delete pt_la_tip;
unde pt_la_tip este adresa obinut n urma unei alocri cu operatorul new, utilizarea altei valori fiind
ilegal, comportarea programului ntr-o astfel de situaie fiind nedefinit.
Ca i n cazul utilizrii funciilor de alocare i eliberare dinamic a memoriei, este
necesar ca, dup ce unei variabile pointer i s-a atribuit o adres cu operatorul new,
s nu i se atribuie alt adres i s nu se ias din blocul n care pointerul este
declarat dect dup eliberarea vechii zone de memorie cu operatorul delete. n caz
contrar, vechea zon de memorie va rmne ocupat, fra a mai putea fi utilizat, ceea
ce poate duce, mai ales prin repetarea n cursul execuiei a operaiilor de mai sus la
mari consumuri inutile de memorie. De reinut c grelile de mai sus nu sunt semnalate
n cursul comiplrii sau al execuiei.

Funcii "inline"
Directiva preprocesor #define ofer posibilitatea utilizrii macrodefiniiilor cu argumente. Pentru fiecare
apelare preprocesorul insereaz n textul programului, n locul apelului, textul macrodefiniiei n care se
substituie numele argumentelor formale cu textele care reprezint argumentele efective (de reinut c este o
simpl nlocuire de texte, fr nicio verificare de corectitudine sintactic). Textul rezultat este preluat apoi
de compilatorul propiu_zis. Princialul avantaj al macrodefiniiei faa de funcie este obinerea unui timp de
execuie mai scurt, deoarece codul obiect este inserat efectiv n program i se elimin operaiile asociate
apelului unei funcii (salvri i restabiliri de registre ale unei microprocesorului, transfer de argumente prin
stiv, etc), iar principalul dezavantaj este creterea dimensiunii codului obiect generat de compilator,
deoarece pentru fiecare apel se insereaz codul surs corespunztor instruciunilor din macrodefiniie.
86

Procedeul este avantajos atunci cnd codul obiect rezultat din compilarea macrodefiniiei nu este prea lung
i se fac puine inserri de macrodefiniii, dar, n momentul execuiei, se fac multe execuii ale operaiilor
aprute ca urmare a utilizrii macrodefiniiilor, ceea ce se poate ntmpla dac substituirile de macrodefiniii
se fac n interiorul blocurilor unor instruciuni repetitive.
Un dezavantaj special este constituit de faptul c "expresiile" din macrodefiniii sunt de fapt simple texte
care nu sunt supuse nici verificrii corectitudinii, nici nu sunt evaluate nainte de substituire, ceea ce poate
duce la efecte nedorite.
Un exemplu simplu l reprezint macrodefiniia:
#define patrat(x) x*x
...
int i, j, rez;
rez=patrat(i); //cirect:rez=i*i;
rez=patrat(i+1); //eroare:rez=i+1*i+1, deci
//rez=2*i+1
care, dup cum se vede, nu reuete s defineasc o funcie care s calculeze ptratul unei expresii. Eroarea
de mai sus poate fi nlturat definind patrat(x) astfel:
#define patrat(x) (x)*(x)
...
rez=patrat(i+1); //corect:rez=(i+1)*(i+1);
Nici acum ns nu se elimin toate posibilitile de eroare, ceea ce se vede mai jos:
rez=patrat(++i); //eroare :rez=(++i)*(++i),
//dubla incrementare
C++ ofer posibilitatea declarrii funciilor "inline" care combin avantajele funciilor propriu-zise cu cele
ale macrodefiniilor. Definiiile acestora sunt compilate ca i cele ale funciilor obinuite, dar, dar, spre
deosebire de funciile obinuite, la fiecare apelare codul obiect al funciei inline este inserat n codul
programului de ctre compilator. n acest fel, ca i n cazul macrodefiniiilor se elimin operaiile aferente
87

apelului. Funciile inline pstreaz ns toate proprietiile funciilor n privina verificrii validitii
apelurilor, modului de calcul i transfer al argumentelor, etc.
Se pstreaz avantajul creterii vitezei, da i dezavantajul creterii dimensiunii codului. Nu mai este
posibil compilarea separat a unei funcii inline sau utilizarea unui pointer ctre o asemenea funcie.
Funciile inline se definesc i se urilizeaz similar cu cele obinuite, cu excepia adugrii specificatorului
inline. Problema din exemplul de mai sus se poate rezolva complet cu o funcie inline, dup cum urmeaz:
inline int patrat(int x){ return x*x; }
...
rez=patrat(++i);
Operatorul de rezoluie
n C++ este definit operatorul de rezoluie (: : ) care permite accesul la un identificator global, dintr-un bloc
n care acesta nu este vizibil datorit unei redeclarri. Se mai numete operator de acces. De exemplu:
char sir[20]="Sir global";
void func()
{
char *sir; //variabila locala
sir="Sir local";
puts(::sir); //afiseaza sirul global
puts(sir); //afiseaza sirul local
}

6.2.2.3 Extensii orientate pe obiecte


n aceast seciune prezentm felul cum conceptele orientate pe obiecte din capitolul 3 sunt utilizate la C+
+.

6.2.2.3.1 Clase i obiecte


C++ permite declarri i definiri de clase. Instanierile claselor se numesc obiecte. Reamintim programul de
desenare dat ca exemplu n capitolul 4. Acolo am construit o clas Punct. n C++ aceasta arat aa:

88

class Punct {
int _x, _y; // coordonatele punctului
public: // inceputul sectiunii interfata
void setX(const int val);
void setY(const int val);
int getX() { return _x; }
int getY() { return _y; }
};
Punct unpunct;
Se declar o clas Punct i se declar un obiect unpunct. Putem s gndim o definiie de clas ca pe o
definiie de structur cu funcii (sau "metode"). n plus, se pot specifica mai detaliat "drepturi de acces". De
exemplu _x i _y sunt private, deoarece datele membre ale claselor sunt private. Prin urmare, trebuie s se
"comute" explicit drepturile de acces pentru a se declara urmatoarele ca publice. Aceasta se face prin
utilizarea cuvntului cheie public urmat de ":". Orice membru care urmeaz acest cuvnt cheie este accesibil
din afara clasei.
Putem s comutm iari la drepturi de acces private ncepnd o seciune privat cu cuvntul cheie private.
Acest lucru este posibil de cte ori este necesar:
class Foo {
// private implicit...
public:
// ceea ce urmeaza este public pana...
private:
//...aici, unde se revine la private...
public:
//... si iarasi la public.

89

};
Amintim c o structur struct este o combinaie de diferite date membre care sunt accesibile din afar.
Putem acum s exprimm o structur cu ajutorul unei clase, n care toi membrii sunt declarate ca publice:
class Struct {
public: // Membrii structurii sunt implicit publici
// date, metode
};
C++ face exact acelai lucru cu struct. Structurile sunt tratate ca i clasele. n timp ce membrii claselor
(definite cu class) sunt implicit privai, membrii structurilor (definite cu struct) sunt publici. Totui, putem
s utilizm de asemenea private : pentru a comuta la o seciune privat n structuri.
S revenim la clasa noastr Punct. Interfaa ei ncepe cu seciunea public n care definim patru metode,
cte dou pentru fiecare coordonat pentru a atribui i, respectiv, a furniza valoarea sa. Metodele pentru a
atribui valori sunt numai declarate. Funcionalitatea lor efectiv urmeaz s fie definit. Metodele pentru
furnizarea valorii au un corp de funcie. Ele sunt definite n interiorul clasei, sau cu un termen englez uzual,
sunt metode inline.
Acest tip de definire a metodelor este util pentru corpuri de funcie mici i simple. El de asemenea
mbunatete performana, deoarece corpurile metodelor inline sunt "copiate" n cod oriunde apare o
apelare a unei astfel de metode.
Dimpotriv, apelrile metodelor de obinere a valorii vor avea ca rezultat o apelare de funcie "adevarat".
Definim aceste metode n afara declaraiei de clas. Aceasta face necesar s se declare crei clase aparine o
anumit definiie de metod. De exemplu, o alt clas ar putea s defineasc o metod setX() care este
diferit de aceea din Punct. Trebuie s putem defini domeniul de definiie; utilizm deci operatorul de
rezoluie "::":
void Punct::setX(const int val) {
_x = val;
}

void Punct::setY(const int val) {

90

_y = val;
}
Aici definim metoda setX() (setY()) n interiorul domeniului clasei Punct. Obiectul unpunct poate s
utilizeze aceste metode pentru a a-i atribui i a furniza informaii despre el nsui:
Punct unpunct;
unpunct.setX(1); // Initializare
unpunct.setY(1);
//
// x este necesar incepand de aici, deci il definim aici si
// il initializam cu coordonata x a lui unpunct
//
int x = unpunct.getX();
Apare ntrebarea: cum "tiu" metodele din ce obiect sunt invocate? Aceasta se face prin transferul implicit
al unui pointer la obiectul care invoc metoda. Putem accesa acest pointer n nteriorul metodei prin
cuvntul cheie this. Definiiile metodelor setX() i setY() utilizeaz membrii clasei _x i _y respectiv.
Dac sunt invocai de un obiect, aceti membri sunt "automat" atribuii obiectului corect. Putem s utilizm
this pentru a ilustra ce se ntampl de fapt:
void Punct::setX(const int val) {
this->_x = val; // Aceasta se utilizeaza pentru a referi
// obiectul invocant
}
void Punct::setY(const int val) {
this->_y = val;
}

91

Aici utilizm explicit pointerul this pentru a dereferi explicit obiectul invocant. Din fericire, compilatorul
insereaz automat aceste dereferiri ale membrilor clasei, deci putem s utilizm efectiv primele definiii ale
metodelor setX() i setY(). Totui, uneori are sens s se tie c exist un pointer this disponibil care indic
obiectul invocant.
n mod curent avem nevoie s apelm metodele de atribuire pentru a iniializa un obiect punct. Totui, am
putea dori s iniializm punctul atunci cnd l definim. Utilizm, prin urmare, metode speciale numite
constructori.

6.2.2.3.2 Constructori
Constructorii sunt metode pentru a iniializa un obiect n momentul definirii sale. Lrgim clasa noastr
Punct astfel nct s iniializeze un punct cu coordonatele (0, 0):
class Punct {
int _x, _y;
public:
Punct() {
_x = _y = 0;
}
void setX(const int val);
void setY(const int val);
int getX() { return _x; }
int getY() { return _y; }
};
Constructorii au acelai nume cu clasa (astfel sunt recunoscui ca fiind constructori). Ei nu au valoare de
ntoarcere. Ca i alte metode, ei pot avea argumente. De exemplu, putem dori s iniializm un punct cu alte
coordonate dect (0, 0). Definim deci un al doilea constructor avnd dou argumente ntregi n interiorul
clasei:
class Punct {

92

int _x, _y;


public:
Punct() {
_x = _y = 0;
}
Punct(const int x, const int y) {
_x = x;
_y = y;
}
void setX(const int val);
void setY(const int val);
int getX() { return _x; }
int getY() { return _y; }
};
Constructorii sunt apelai implicit atunci cnd se definesc obiecte ale claselor lor
Punct apunct; // Punct::Punct()
Punct bpunct(12, 34); // Punct::Punct(const int, const int)
Cu constructorii putem s iniializm obiectele n momentul definirii aa cum am cerut n capitolul 1 pentru
lista simplu nlnuit. Putem acum s iniializm o clas Lista n care constructorii s iniializeze corect
obiectele lor.
Dac dorim s crem un punct din alt punct, copiind deci proprietile unui obiect n unul nou creat, uneori
trebuie s programm procesul de copiere. De exemplu, s considerm clasa Lista care aloc dinamic
memorie pentru elementele sale. Dac dorim s crem o a doua list care este o copie a primei, trebuie s
alocm memorie i s copiem elementele individuale. n clasa noastr Punct adugm deci un al treilea
constructor care se ocup de copierea corect a valorilor de la un obiect la cel nou creat:
class Punct {

93

int _x, _y;


public:
Punct() {
_x = _y = 0;
}
Punct(const int x, const int y) {
_x = x;
_y = y;
}
Punct(const Punct &din) {
_x = din._x;
_y = din._y;
}
void setX(const int val);
void setY(const int val);
int getX() { return _x; }
int getY() { return _y; }
};
Al treilea constructor are o referina constant la un obiect al clasei Punct ca argument i atribuie lui _x i
_y valorile corespunztoare ale obiectului furnizat.
Acest tip de constructor este att de important nct are propriul su nume: constructor de copiere. Este
foarte recomandabil s se dea pentru fiecare clas un astfel de constructor, chiar dac nu este la fel de
simplu ca n exemplul nostru. Constructorul de copiere este apelat n urmtoarele cazuri:
Punct apunct; // Punct::Punct()
Punct bpunct(apunct); // Punct::Punct(const Punct &)
Punct cpunct = apunct; // Punct::Punct(const Punct &)

94

Cu ajutorul constructorilor am satisfcut una din cerinele noastre privind implementarea tipurilor de date
abstracte: iniializarea n momentul definirii. Avem nevoie nc de un mecanism care s "distrug" automat
un obiect atunci cnd devine non-valid (de exemplu, din cauza prsirii domeniului su. Prin urmare, clasele
pot s defineasc destructori.

6.2.2.3.3 Destructori
S considerm o clas Lista. Elementele listei sunt adugate i terse n mod dinamic. Constructorul ne
ajut s crem o list iniial vid. Totui, atunci cnd ieim din domeniul definiiei unui obiect list, trebuie
s ne asigurm c memoria alocat este eliberat. Definim prin urmare o metod special numit destructor
care este apelat o dat pentru fiecare obiect n momentul distrugerii sale:
void foo() {
Lista olista; // Lista::Lista() este initializata
// cu lista vida.
... // adauga/sterge elemente
} // Apelarea destructorului!
Distrugerea unui obiect are loc automat atunci cnd obiectul i prsete domeniul de definiie sau este
distrus n mod explicit. Ultima aciune are loc atunci cnd se aloc dinamic un obiect i l eliberm dac nu
mai este necesar.
Destructorii sunt declarai ca i constructorii. Astfel, i ei utilizeaz numele clasei, dar prefixat cu simbolul
~ (tilda):
class Punct {
int _x, _y;
public:
Punct() {
_x = _y = 0;
}
Punct(const int x, const int y) {
_x = xval;

95

_y = yval;
}
Punct(const Punct &din) {
_x = din._x;
_y = din._y;
}
~Punct() { /* Nu se face nimic! */ }
void setX(const int val);
void setY(const int val);
int getX() { return _x; }
int getY() { return _y; }
};
Destructorii nu au argumente. Este chiar eronat s se defineasc argumente, deoarece destructorii sunt
apelai implicit n momentul distrugerii: nu exist deci ansa s se specifice argumente efective.

6.2.2.3.4 Motenire
n pseudo-limbajul nostru, am formulat motenirea prin "inherits from". n C++ aceste cuvinte sunt
nlocuite prin caracterul ":". De exemplu, s proiectm o clas pentru puncte tridimensionale. Dorim,
desigur, s reutilizm clasa noastr deja existent Punct. ncepem proiectarea clasei noastre dup cum
urmeaz:
class Punct3D : public Punct {
int _z;
public:
Punct3D() {
setX(0);
setY(0);
_z = 0;

96

}
Punct3D(const int x, const int y, const int z) {
setX(x);
setY(y);
_z = z;
}
~Punct3D() { /* Nimic de facut */ }
int getZ() { return _z; }
void setZ(const int val) { _z = val; }
};

6.2.2.3.4.1 Tipuri de motenire


Se observ din nou cuvntul cheie public utilizat n prima linie a definiiei clasei (signatura sa). Acesta
este necesar deoarece n C++ se disting dou tipuri de motenire: public i privat. Implicit clasele deriv
n mod privat una din cealalt. n consecin, trebuie s se informeze explicit compilatorul pentru a utiliza
motenirea public.
Tipul de motenire influeneaz drepturile de acces la membrii unor supraclase. Orice este declarat private
ntr-o supraclas va fi inaccesibil n subclas. Utiliznd motenirea public, orice este public rmne public.
Atunci cnd se utilizeaz motenirea privat, orice nu este privat n supraclas devine privat n subclas.
Efectul cumulat al dreptului de acces din supraclas i al modului de motenire asupra dreptului de acces
din subclas ai membrilor supraclasei este complet artat n tabelul Tabelul 6.6

Tip de motenire
private protected public
inaccesibil inaccesibil
private
protected
protected private
private
protected
public

inaccesibil

protected
public

Tabelul 6.6

97

Coloana din stnga listeaz drepturi posibile de acces pentru membrii supraclaselor. Un rol special are un al
treilea mod de acces: protected. Acesta este utilizat pentru membri care s fie direct utilizabili n subclase
dar care s nu fie accesibili din afar. Astfel, s-ar putea spune c membrii de acest tip sunt ntre private i
public prin aceea ca ei pot fi utilizai n ierarhia de clase avnd drept rdcin clasa corespunztoare,
utilizndu-se i tipul de motenire protected.
Urmtoarele coloane arat drepturile de acces care rezult pentru membrii unei supraclase n funcie de
tipul de motenire.

6.2.2.3.4.2 Construcie
Atunci cnd crem o instaniere a clasei Punct3D constructorul su este apelat. Deoarece Punct3D este
derivat din Punct, constructorul clasei Punct este apelat i el. Totui, acest constructor este apelat naintea
executrii corpului constructorului clasei Punct3D. n general, naintea execuiei corpului unui anumit
constructor, constructorii fiecrei supraclase sunt apelai pentru a iniializa partea din obiectul creat. Cnd
crem un obiect cu
Punct3D punct(1, 2, 3);
al doilea constructor al clasei Punct3D este invocat, dar, naintea executrii corpului constructorului,
constructorul Punct() este invocat pentru a iniializa partea Punct a obiectului punct. Este bine c am
definit un constructor fr argumente. Acest constructor iniializeaz coordonatele bidimensionale _x i _y
cu 0 (zero). Deoarece Punct3D este derivat numai din Punct nu exist alt apelare de constructor i corpul
lui Punct3D(const int, const int, const int) este executat. Aici se invoc metodele setX() i setY() pentru a
se iniializa explicit coordonatele bidimensionale. n plus, primete valoare i a treia coordonat _z. Acest
fapt este foarte nesatisfctor, deoarece am definit un constructor punct() care are dou argumente pentru
iniializa cu ele coordonatele. Astfel, trebuie numai s putem declara c, n loc de utilizarea constructorului
implicit Punct(), se va utiliza constructorul parametrizat Punct (const int, const int). Putem face aceasta
specificnd constructorul dorit dup un singur caracter ":" chiar naintea corpului constructorului Punct3D:
class Punct3D : public Punct {
...
public:
98

Punct3D() {... }
Punct3D(
const int x,
const int y,
const int z) : Punct(x, y) {
_z = z;
}
...
};
Dac am avea mai multe supraclase, am da apelrile lor de constructori ca pe nite liste cu elementele
separate prin virgule. De exemplu, s presupunem c o clas Parte definete numai un constructor cu un
argument. Atunci, pentru a crea un obiect al clasei Compusa trebuie s invocm Parte() cu argumentul
su:
class Compusa {
Parte parte;
...
public:
Compusa(const int parteParametru) : parte(parteParametru) {
...
}
...
};
Aceast iniializare dinamic poate s fie de asemenea utilizat cu tipuri de clase predefinite. De exemplu,
constructorii clasei Punct ar putea fi definii ca:
Punct() : _x(0), _y(0) {}
Punct(const int x, const int y) : _x(x), _y(y) {}

99

Este bine s utilizm aceast metod de iniializare ct mai des posibil, deoarece ea permite compilatorului
s creeze variabile i obiecte iniializate corect n loc s le creeze cu o valoare implicit i s utilizeze o
atribuire suplimentar, (sau alt mecanism) pentru a da valori.

6.2.2.3.4.3 Distrugere
Dac un obiect este distrus, de exemplu prin prsirea domeniului su de definiie, destructorul clasei
corespunztoare este invocat. Dac aceast clas este derivat din alte clase, destructorii lor sunt i ei
apelai, ajungndu-se la un lan recursiv de apelri.

6.2.2.3.4.4 Motenire multipl


C++ permite ca o clas s fie derivat din mai mult de o supraclas, aa cum am menionat pe scurt mai
nainte. Se poate cu uurin deriva din mai mult de o clas prin specificarea supraclaselor, separate prin
virgule:
class SirDesenabil : public Punct, public ObiectDesenabil {
...
public:
SirDesenabil(...) :
Punct(...),
ObiectDesenabil(...) {
...
}
~SirDesenabil() {... }
...
};
Conflictele de nume se rezolv prin utilizarea operatorului de rezoluie (::).
Nu vom utiliza acest tip de motenire n cele ce urmeaz. Pentru mai multe detalii se pot consulta [2], [3].

6.2.2.3. 5 Polimorfism
n pseudo-limbajul nostru puteam s decalarm metode ale unor clase ca fiind virtuale, fornd ca valoarea
lor s fie bazat pe coninut n loc de tipul obiectelor. Putem s utilizm aceasta i n C++:
100

class ObiectDesenabil {
public:
virtual void print();
};
Clasa ObiectDesenabil definete o metod print() care este virtual. Putem s derivm din aceast clas
alte clase:
class Punct : public ObiectDesenabil {
...
public:
...
void print() {... }
};
Din nou print() este o metod virtual, deoarece motenete aceast proprietate de la ObiectDesenabil.
Funcia display() care este capabil s afieze orice fel de obiect desenabil, poate fi definit ca:
void display(const ObiectDesenabil &ob) {
// pregateste tot ce este necesar
ob.print();
}
Atunci cnd se utilizeaz metode virtuale unele compilatoare avertizeaz dac destructorul clasei
corespunztoare nu este declarat i el virtual. Acest lucru este necesar dac se utilizeaz pointeri la subclase
(virtuale) atunci cnd acetia sunt distrui. Deoarece pointerul este declarat ca supraclas, n mod normal
destructorul su ar fi apelat. Dac destructorul este virtual, destructorul obiectului efectiv referit este apelat
(i atunci, recursiv, toi destructorii supraclaselor sale). Iat un exemplu:
class Culoare {
public:
101

virtual ~Culoare();
};
class Rosu : public Culoare {
public:
~Rosu(); // Mostenit virtual de la Culoare
};
class RosuDeschis : public Rosu {
public:
~RosuDeschis();
};
Utiliznd aceste clase definim o palet dup cum urmeaz:
Culoare *paleta[3];

paleta[0] = new Rosu; // Creaza dinamic un nou obiect Rosu


paleta[1] = new RosuDeschis;
paleta[2] = new Culoare;
Operatorul nou introdus new creeaz un nou obiect de tipul specificat n memoria dinamic i ntoarce un
pointer la el. Astfel, primul new ntoarce un pointer la un obiect alocat al clasei Rosu i l atribuie primului
element al tabloului paleta. Elementele lui paleta sunt pointerii la Culoare i, deoarece Rosu "este o"
Culoare, atribuirea este corect.
Operatorul opus lui new este delete care distruge un obiect referit de pointerul furnizat. Dac aplicm
delete elementelor lui paleta, se produc urmtoarele apelri de destructori:
delete paleta[0];
// Apeleaza destructorul ~Rosu() urmat de ~Culoare()
delete paleta[1];
// Apeleaza ~RosuDeschis(), ~Rosu() si ~Culoare()
delete paleta[2];

102

// Apeleaza ~Culoare()
Diferitele apelri de destructori apar numai din cauz c sunt utilizai destructori virtuali. Dac ei nu ar fi
fost declarai virtuali, fiecare delete ar fi apelat numai ~Culoare() (deoarece paleta[i] este de tipul pointer
la Culoare).

6.2.2.3. 6 Clase abstracte


Clasele abstracte se definesc exact la fel ca i clasele obinuite. Totui, unele din metodele lor sunt
specificate ca fiind n mod necesar definite de subclase. Se specific prototipul lor care conine tipul pe care
l ntorc, numele i argumentele, dar nu i definiia. Se poate spune c se omite corpul metodei, sau, altfel
spus, se specific "nimic". Aceasta se exprim adugnd "=0" dup prototipul metodei:
class ObiectDesenabil {
...
public:
...
virtual void print() = 0;
};
Aceast definiie de clas va fora fiecare clas derivat de la care urmeaz s se creeze obiecte s
defineasc o metod print(). Aceste declaraii de metode sunt numite i metode pure.
Metodele pure trebuie de asemenea s fie declarate virtuale, deoarece se dorete s se utilizeze obiecte
numai de la clasele derivate. Clasele care definesc metode pure se numesc clase abstracte.

6.2.2.3. 7 Suprancrcarea operatorilor


Reamintindu-se tipul abstract de date pentru numere complexe, Complex, putem crea o clas n C++, dup
cum urmeaz:
class Complex {
double _real,
_imag;

103

public:
Complex() : _real(0.0), _imag(0.0) {}
Complex(const double real, const double imag) :
_real(real), _imag(imag) {}
Complex sum(const Complex op);
Complex inm(const Complex op);
...
};
Vom putea atunci s utilizm numere complexe i s "calculm" cu ele:
Complex a(1.0, 2.0), b(3.5, 1.2), c;
c = a.sum(b);
Aici atribuim lui c suma numerelor a i b. Dei absolut corect, acesta nu este un mod foarte convenabil de
exprimare. Este mai natural s se utilizeze bine-cunoscutul "+" pentru a se exprima adunarea a dou numere
complexe. Din fericire, C++ ne permite s suprancrcm aproape toi operatorii si cu tipuri nou create. De
exemplu, am putea defini un operator "+" pentru clasa noastr Complex:
class Complex {
...
public:
...
Complex operator +(const Complex &op) {
double real = _real + op._real,
imag = _imag + op._imag;
return(Complex(real, imag));
}

104

...
};
n acest caz, am fcut operatorul + membru al clasei Complex. O expresie de forma:
c = a + b;
este tradus ntr-un apel de metod:
c = a.operator +(b);
Astfel, operatorul + necesit numai un argument. Primul argument este implicit furnizat de obiectul invocat
(n acest caz a).
Totui, o apelare a operatorului poate fi interpretat i ca o apelare obinuit de funcie, ca n:
c = operator +(a, b);
n acest caz operatorul suprancrcat nu este membru al unei clase. El este definit n exterior ca o funcie
suprancrcat normal. De exemplu, am putea s definim operatorul + astfel:
class Complex {
...
public:
...
double real() { return _real; }
double imag() { return _imag; }
// Nu este nevoie sa se defineasca operatorul aici!
};
105

Complex operator +(Complex &op1, Complex &op2) {


double real = op1.real() + op2.real(),
imag = op1.imag() + op2.imag();
return(Complex(real, imag));
}
n acest caz trebuie s definim metode de acces pentru prile real i imaginar deoarece operatorul este
definit n afara domeniului clasei. Totui, operatorul este att de strns legat de clas, nct ar putea avea
sens s se permit operatorului accesul la membrii privai. Aceasta se poate face prin declararea sa ca
prieten al clasei Complex.

6.2.2.3. 8 Prieteni
Putem defini funcii sau clase ca fiind prieteni ai unei clase pentru a le permite accesul la datele lor membre
private. De exemplu, n seciunea precedent am putea s dorim ca funcia pentru operatorul + s aib acces
la datele membre private _real i _imag ale clasei Complex. Prin urmare, declarm operatorul + ca prieten
al clasei Complex:
class Complex {
...
public:
...
friend Complex operator +(
const Complex &,
const Complex &
);
};
Complex operator +(const Complex &op1, const Complex &op2) {
double real = op1._real + op2._real,

106

imag = op1._imag + op2._imag;


return(Complex(real, imag));
}
Nu trebuie s folosim prieteni foarte des din cauz c acetia reprezint o nclcare a principiului
incapsulrii datelor. Dac trebuie s folosim prietenii foarte des, este deja un semn c este timpul s ne
restructurm graful de motenire.

6.2. 3 Cum se scrie un program


Pn acum am prezentat numai pri din programe foarte mici care ar putea cu uurin s fie incluse ntrun singur fiier. Totui, marile proiecte, de exemplu, un program pentru calendar, ar trebui s fie divizat n
pri uor de manipulat, denumite adesea module. Modulele sunt implementate n fiiere separate. Acum
vom discuta pe scurt cum se face modularizarea n C i C++. Aceast discuie este bazat pe UNIX i
compilatorul C++, GNU. Dac utilizai alt software de baz, ceea ce urmeaz poate cpta alt aspect. Acest
lucru este n mod special valabil pentru cei care utilizeaz medii de dezvoltare integrate, de exemlu Borland
C++.
n linii mari, modulele constau din dou tipuri de fiiere: descriere de interfa i fiiere de implementare.
Pentru a deosebi aceste tipuri, sunt utilizate mai multe sufixe ale numelor (numite i extensii) atunci cnd se
compileaz programe C i C++. Tabelul 6.7 arat unele dintre ele.
Tabelul 6.7

Tipuri de fiier
Descriere de iterfa (fiiere
"antet" sau "de inclus")
Fiier de implementare (surs)
n C
Fiier de implementare (surs)
n C++
Descriere de interfa (fiier
"model" sau "ablon")

Implementare

Extensii

Borland

.
h.H.hpp.HPP.hxx.HX
.h.hpp.hxx
.c.C
.c
.cpp.CPP
.cpp.c++.C.cc.cxx
.tpl.TPL
.tpl

UNIX
Borland
UNIX
Borland
UNIX
Borland
UNIX

107

n acest suport de curs utilizm .h pentru fiiere C++ i .tpl pentru fiiere care conin definiii de modele (n
englez template). Chiar dac scriem numai cod C, se poate utiliza.cc pentru a determina compilatorul s l
trateze ca C++. Acesta face mai simple combinaiile celor dou limbaje, dei mecanismele interne de
aranjare a numelor n program ale compilatorului difer de la un limbaj la altul.

Paii compilrii
Procesul de compilare preia fiierele.cc, le preproceseaz (nlturnd comentariile i incluznd fiierele
antet) i le translateaz n fiiere obiect. Sufixele tipice pentru aceste fiiere sunt.o.obj.
Dup terminarea cu succes a compilrii, mulimea de fiiere obiect rezultat este prelucrat de un editor de
legturi (n englez linker). Acest program combin fiierele i bibliotecile necesare i creaz un program
executabil. Sub UNIX acest fiier este denumit a.out dac nu se specific altfel. Aceti pai sunt ilustrai n
Figura 6.1.

.cc

compilator
compilator

.h,.tpl

.cc

editor
editor de
de
legturi
legturi

biblioteci

.out
Figura 6.1
Compilatoarele moderne permit combinarea celor doi pai. De exemplu programele mici date ca
exemplemai sus pot fi compilate i legate cu compilatorul GNU C++ dup cum urmeaz ("exemplu.cc"este
numai numele unui exemplu, desigur ):
108

gcc exemplu.cc

Despre stil
Fiierele antet sunt utilizate pentru a descrie interfaa fiierelor de implementare. n consecin, sunt incluse
n fiecare fiier de implementare care utilizeaz interfaa unui fiier de implementare particular. Aa cum s-a
menionat mai sus aceast includereeste efectuat prin copierea coninutului fiierelui antet la fiecare
apariie a directivei de preprocesor # include ajungndu-se la un fiier C++ pur uria. Pentru a evita
includerea unor copii multiple cauzat de dependenele multiple utilizm cod condiional. Preprocesorul
definete directive condiionale pentru verificarea diferitelor aspecte ale prelucrrii sale. De exemplu putem
verifica dac un macro este deja definit:
#ifndef

MACRO

#define MACRO /* defineste MACRO */


...
#endif
Liniile dintre #ifndef i #endif sunt incluse numai dac MACRO nu este nc definit. Putem utiliza acest
mecanism pentru a mpedica efectuarea unor copii multiple ale unui fisier antet:
/*
** Exemplu de fisier antet care 'verifica' daca el este
** deja inclus. Presupunem ca numele fisierului antet
** este 'antetulmeu.h'
*/
#ifndef __ANTETULMEU_H
#define __ANTETULMEU_H
/*
** Aici sunt declaratiile din interfata
*/

109

#endif /* __ANTETULMEU_H */
__ANTETULMEU_H este un nume unic asociat fiecrui fiier antet. Prima dat cnd fiierul este inclus,
__ANTETULMEU_H nu este definit, astfel nct fiecare linie este inclus i prelucrat.Prima linie definete
un macro numit __ANTETULMEU_H. Dac accidental fiierul ar urma s fie inclus a doua oar (n timpul
prelucrrii aceluiai fiier de intrare), __ANTETULMEU_H este definit, deci tot ceea ce urmeaz pn la
#endif este srit.

6.2.4 Exerciii
1. Polimorfism.
Explicai de ce
void display(const ObiectDesenabil ob);
nu produce ieirea dorit.

7 Rezolvrile exerciiilor
Aceast seciune prezint exemple de rezolvri pentru exerciiile de mai sus.

7.1 O trecere n revist a tehnicilor de programare


1. Discutarea modulului
Lista-Simplu-Inlantuita-2
(a) Definiia interfeei modulului Intreg-Lista
MODULE Intreg-Lista
DECLARE TYPE int_reprez_lista_t;
int_reprez_lista_t int_lista_create();
BOOL int_lista_append(int_reprez_lista_t this,

110

int date);
INTEGER int_lista_getFirst(int_reprez_lista_t this);
INTEGER int_lista_getNext(int_reprez_lista_t this);
BOOL int_lista_isEmpty(int_reprez_lista_t this);
END Intreg-Lista;
Aceast reprezentare introduce probleme suplimentare care sunt cauzate de nesepararea traversrii de
structura de date. Dup cum v amintii, pentru a itera asupra elementelor listei am utilizat o instruciune de
ciclare cu urmtoarea condiie:
WHILE date IS VALID DO
date a fost iniializat printr-o apelare a list_getFirst(). Procedura de tip list de ntregi int_list_getFirst()
ntoarce un ntreg, prin urmare nu exist ceva cum ar fi "ntreg invalid" care s poat fi utilizat pentru
verificarea terminrii ciclului.
2. Diferenele ntre programarea orientat pe obiecte i alte tehnici. n programarea orientat pe obiecte
obiectele schimb mesaje unul cu altul. n celelalte tehnici de programare, datele sunt schimbate ntre
proceduri sub controlul unui program principal. Pot coexista obiecte de acelai fel, dar fiecare cu starea sa
proprie. Acest fapt este n contrast cu abordarea modular, n care fiecare modul are numai o stare global.

7.2 Tipuri abstracte de date


1. TAD Intreg.
(a) Ambele operaii sum i dif pot s fie aplicate pentru orice valoare a lui N. Astfel, aceste operaii pot fi
aplicate oricnd. Nu sunt restricii pentru utilizarea lor. Totui, se poate descrie acest lucru cu o precondiie
care are valoarea true.
(b) Definim trei noi operaii: inm, imp i abs. Ultima va ntoarce valoarea absolut a unui ntreg. Operaiile
sunt definite dup cum urmeaz:
inm(k)
imp(k)
abs()
111

Operaia inm nu cere nici o precondiie, la fel ca sum i dif. Postcondiia este, desigur rez=n*k. Urmtoarea
operaie imp necesit s avem k diferit de 0 (zero). Prin urmare, definim precondiia: k diferit de 0. Ultima
operaie abs ntoarce valoarea lui N dac N este pozitiv sau 0 sau -N dac N este negativ. Din nou nu
conteaz ce valoare are N cnd aceast operaie este aplicat. Postcondiia este:
dac N>=0 atunci
abs=N
altfel
abs=-N.
2. TAD Fracie.
(a) O fracie simpl const din numrtor i numitor. Ambele sunt numere ntregi. Acest fapt este similar cu
exemplul referitor la numrul complex prezentat n seciune. Am putea alege celpuin dou stucturi de date
pentru a pstra valorile: un tablou sau un record.
(b) Prezentarea interfeei. Amintim c interfaa este chiar setul de operaii vizibile din exterior. Putem
descrie o interfaa a unei fracii ntr-o manier verbal, n consecin, avem nevoie de operaii:
pentru a obine valoarea numrtorului/ numitorului;
pentru a atribui o valoare numrtorului/ numitorului;
pentru a aduna o fracie, ntorcnd suma;
pentru a scdea o fracie, ntorcnd diferena;
...
(c) Dm mai jos unele axiome i precondiii pentru fiecare fracie care sunt de asemenea satisfcute pentru
TAD:
Numitorul trebuie s fie diferit de 0 (zero), altfel valoarea fraciei nu este definit.
Dac numrtorul este 0 (zero), valoarea fraciei este 0 pentru orice valoare a numitorului.
Orice numr ntreg poate fi reprezentat printr-o fracie al crei numrtor este numrul, iar numitorul este
1.
3. TAD-urile definesc proprietile unui set de instanieri. Acestea furnizeaz o viziune abstract asupra
acestor proprieti furniznd un set de operaii care pot fi aplicate asupra instanierilor. Acest set de operaii,
adic interfaa, este cel care definete proprietile instanierilor.
Utilizarea unui TAD este restricionat de axiome i precondiii. Ele definesc condiii i proprieti ale
mediului n care pot fi utilizate instanieri ale TAD-ului.
112

4. Avem nevoie s enunm axiome i s definim precondiii care s asigure utilizarea corect a
instanierilorTAD-urilor. De exemplu, dac nu declarm 0 (zero) ca fiind elementul neutru al adunrii
ntregilor, ar putea exista un TAD Intreg care s fac ceva prestabilit adunnd 0 la N. Aceasta nu este
ceeeace se ateapt de la un ntreg. Astfel, axiomele i precondiiilene dau un mijloc de a ne asigura c
TAD-urile "funcioneaz"aa cum dorim s o fac.
5. Descrierea relaiilor.
(a) O instaniere este un reprezentant efectiv TAD. (Este astfel un"exemplu" al su. Acolo unde TAD-ul
declar c utilizeaz un"numr ntreg cu semn" ca strctur de date, o instaniere pastreaz efectiv o valoare,
s spunem, "-5".
(b) TAD-urile generice definesc aceleai proprieti ale TAD-urilorlor corespunztoare. Totui, ele sunt
destinate altui tip particular.De exemplu, TAD Lista definete proprieti ale listelor. Astfel, am putea avea o
operaie adauga(elem) care adaugun nou element elem la list. Nu spunem de ce tip este elem, ci numai c
el va fiultimul element al listei dup aceast operaie. Dac utilizm un TAD generic Lista tipul acestui
element este cunoscut: el este dat de parametrul generic.
(c) instanierile aceluiai TAD generic ar putea fi considerate ca "rude". Ele ar putea fi "veri" a instanierilor
unui alt TAD generic dac cele dou TAD-uri generice au n comun acelai TAD.

7.3 Concepte ale orientrii pe obiecte


1. Clas.
(a) O clas este implementarea efectiv a unui TAD. De exemplu, un TAD pentru ntregi ar putea s includ
o operaie atr pentru a atribui valori instanierilor sale. Aceast operaie este implementat diferit n limbaje
ca C sau Pascal. n C, semnul egal "=" definete operaia de atribuire pentru ntregi, n timpce n Pascal este
utilizat irul de caractere ":=". n consecin, clasele implementeaz operaii furniznd metode. De
asemenea, structura de date a TAD-ului este implementat de atributele clasei.
(b) Clasa Complex.
class Complex {
attributes:
Real real,
imaginar
methods:
113

:=(Complex c) /* Atribuie valoarea lui c */


Real parteReala()
Real parteImaginara()
Complex +(Complex c)
Complex -(Complex c)
Complex /(Complex c)
Complex *(Complex c)
}
Alegem simbolurile bine-cunoscute "+" pentru adunare, "-" pentru scdere, "/" pentru mprire i "*"
pentru nmulire pentru a implementa operaiile corespunztoare ale TAD Complex. Astfel, obiectele clasei
Complex pot fi utilizate ca mai jos:
Complex c1, c2, c3
c3 := c1 + c2
Se observ c se poate scrie o instruciune de adunare ca mai jos:
c3 := c1.+(c2)
Ai putea dori s nlocuii "+" cu "sum" pentrua ajunge la o reprezentare pe care am utilizat-o pn acum.
De fapt, se vede c "+" nu este altceva dectun alt nume pentru "sum".
2. Obiecte care interacioneaz.
3.Mesaje.
(a) Obiectele sunt entiti autonome care doar furnizeaz o interfa bine definit. Apare ca natural s se
vorbeasc despre obiecte ca i cum ele ar fi entiti active. De exemplu obiectele "sunt rspunztoare"
pentru ele nsele, "ele" pot respinge invocarea unei metode, etc. Aceasta deosebete un obiect de un modul,
care este pasiv. De aceea nu se vorbete despre apelri de proceduri. Se vorbete despre mesaje, prin care
unui obiect i se "cere" s invoce una din metodele sale.
(a) Internet-ul furnizeaz mai multe obiecte. Dou dintre cele mai bine cunoscute sunt "client" si "server".
De exemplu, se utilizeaz un client FTP (obiect) pentru a se accesadatele memorate pe un server FTP

114

(obiect). Acest fapt poate fi privit astfel: clientul "trimite un mesaj" server-ului prin care cere s i se
furnizeze datele memorate acolo.
(a) n mediul client/server avem n realitate dou entiti care actioneazla distan: procesele client i
server. n mod normal, aceste dou entiti schimb date ntre ele sub forma unor mesaje Internet.

7.4 Alte concepte orientate pe obiecte


1. Motenire
a) Definiia clasei Dreptunghi :
class Dreptunghi inherits from Punct {
attributes:
int _lungime, // Lungimea dreptunghiului
_latime // Latimea dreptunghiului
methods:
setLungime(int nouaLungime)
getLungime()
setLatime(int nouaLatime)
getLatime()
}
n acest exemplu definim un dreptunghi prin colul su din stnga sus (coordonatele sunt motenitede la
Punct ) i dimensiunile sale. S-ar fi putut defini i prin colurile din stnga sus i dreapta jos (deci prin dou
puncte).
Urmeaz s se adauge metode de acces pentru lungimea i limea dreptunghiului.
(b)Obiecte tridimensionale. O sfer este definit printr-un centru i o raz. Centrul este un punct n spaiul
tridimensional, astfel, putem defini clasa Sfera
ca:
class Sfera inherits from Punct-3D {
attributes:
115

int _raza;
methods:
setRaza(int nouaRaza)
getRaza()
}
Aceasta este similar clasei Cerc n spaiul bidimensional (plan). Aici Punct-3D este un Punct cu o
dimensiune suplimentar.
class Punct-3D inherits from Punct {
attributes:
int _z;
methods:
setZ(int nouZ);
getZ();
}
n consecin, Punct-3D i Punct sunt legate printr-o relaie de tipul "este-un".
(c)Funcionalitatea lui move(). move(), aa cum este definit n seciunea 4.2, permite obiectelor
tridimensionale s se deplaseze dup axa X, deci numai ntr-o singur dimensiune. Ea face aceasta doar prin
modificarea prii bidimensionale a obiectelor tridimensionale. Aceast parte bidimensional este definit
de clasa Punct i motenit direct sau indirect de obiectele tridimensionale.
(a) Graful de motenire (vezi Figura 7.1)

116

ObiectDesenabil

Punct

Dreptunghi

Punct-3D

Cerc

Sfera
Figura 7.1
(e) Alt graf de motenire. n acest exemplu, clasa Sfera motenete de la Cerc i adaug simplu a treia
coordonat. Acesta are avantajul c o sfer poate fi tratat ca un cerc (de exemplu raza sa poate s fie uor
modificat cu metodele/funciile care traseaz cercul). El are dezavantajul c"distribuie" tratarea obiectului
(centrul n spaiul tridimensional) prin ierarhia de motenire: de la Punct prin Cerc la Sfera. Astfel, aceast
tratare nu este accesibil ca un tot.
2. Motenire multipl. Graful de motenire din Figura 4.9 introduce evident conflicte de nume cu
proprietile clasei A.
Totui, aceste proprieti sunt determinate unic prin urmrirea cii de la D la A. Astfel, D poate schimba
proprietile lui A motenite de la B urmnd calea de motenire prin B. La fel, D poate schimba proprietile
lui A motenite de C urmnd calea de motenire prin C. Prin urmare, acest conflict de nume nu duce n mod
necesar la eroare, ct timp cile sunt indicate.

7.5 Mai mult despre C++


1. Polimorfism. Se utilizeaz signatura
void display(const ObiectDesenabil obj);

117

S observm nti c n C++ argumentele funciilor i metodelor sunt transmise prin valoare. Prin urmare,
obj va fi o copie a argumentului efectiv dat la apelarea funciei. Aceasta nseamn c ObiectDesenabil
trebuie s fie o clas din care s se poat crea obiecte. Nu ar fi acesta cazul, dac ObiectDesenabil Ar fi o
clas abstract (aa cum ar fi dac print() ar fi definit ca o metod pur).
Dac exist o metod virtual print() care este definit de clasa ObiectDesenabil, atunci (deoarece obj este
numai o copie a argumentului efectiv) aceast metod este invocat. Ea nu este metoda definit de clasa
argumentului efectiv (din cauz c acesta nu mai joac un rol semnificativ!).
2. Adugarea metodei de tergere. Nu dm un program, dar dm algoritmul. Metoda sterge() tebuie s
itereze aupra listei pn cnd ntlnete un nod cu elementul de date cerut, s tearg acel nod i s ntoarc
1. Dac lista este vid sau elementul de date nu poate fi gsit, ntoarce 0(zero).
n timpul iterrii, sterge() trebuie s compare succesiv elementul de date dat cu cele din list. Prin urmare,
poate exista o comparaie ca:
if (date == curent->date()) {
// element gasit
}
Aici utilizm operatorul de egalitate "==" pentru a compara cele dou elemente. Cum aceste elemente pot fi
de orice tip, ele pot fi mai ales obiecte ale claselor definite de utilizator. A pune ntrebarea: cum este
"egalitatea" definit pentru aceste noi tipuri? n consecin, pentru a se permite metodei sterge() s lucreze
corect, lista va fi utilizat numai pentru tipuri pentru care se definesc operatorii "==" i "!=" n mod
corespunztor. Altfel sunt utilizate comparri implicite, ceea ce ar putea duce la rezultate ciudate.
3. Clasa ListaNumrat. O list numrat este o list care are asociat i numrul elementelor sale. Astfel,
cnd un element de date este adugat, numrul este mrit cu unu, iar cnd un element este ters, este
micorat cu unu. Din nou, nu dm implementarea complet, artm doar o metod ( adauga() ) i felul
cum implementarea este modificat:
class ListaNumarata : public Lista {
int _numar; // Numarul de elemente
...
public:
118

...
virtual void adauga(const T date) {
_numar++; // Incrementeaza si...
Lista::adauga(date); //... adauga la lista
}
...
}
Nu orice metod poate fi implementat astfel. La unele metode trebuie s se verifice dac _numar necesit
s fie modificat sau nu. Totui, ideea principal este c fiecare metod de liste este expandat (sau
specializat ) pentru lista numrat.
4. Problema iteratorului. Pentru a rezolva problema iteratorului, ne-am putea gndi la o soluie n care
iteratorul conine o referin la lista lui corespunztoare.
n momentul crerii iteratorului aceast referin este iniializat pentru a indica lista dat. Metodele
iteratorului trebuie modificate pentru a utiliza aceast referin n locul pointer-ului _pornire.

Bibliografie
[1] T. Blanescu, S. Gavril, H. Georgescu, M. Gheorghe, L. Sofonea, I. Vduva
"Pascal i Turbo Pascal", vol 2, Ed. Tehnic, Bucureti, 1992
[2] K. Jamsa, L. Klander "Totul despre C i C++", Editura Teora, Bucureti, 2002
(traducere din limba englez)
[3] O. Catrina, I. Cojocaru "Turbo C++", Editura Teora, Bucureti, 1993
[4] H. Schildt: "C++ manual complet", Editura Teora, Bucureti, 2000
[5] Bjarne Stroustrup: "The C++ Programming Language", Adisson-Wesley, 3nd edition, 1997
[6] C. Spircu, I. Loptaru "POO: analiza, proiectarea i programarea orientat pe obiecte ", Editura Teora,
Bucureti, 1995

119

Resurse on-line
[web1] Object-Oriented System Development by Dennis de Champeaux, Douglas Lea, and Penelope Faure
(http://g.oswego.edu/dl/oosdw3/)
[web2]

Peter

Muller:

Introduction

to

Object-Oriented

Programming

(http://www.gnacademy.org/uu-gna/text/cc/material.html)
[web3] Bruce Eckel: Thinking in C++, 2nd Edition (http://www.bruceeckel.com/)
[web4] Online C++ tutorial (http://www.intap.net/~drw/cpp/index.htm)

120

Using

C++

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