Sunteți pe pagina 1din 61

Universitatea Politehnica din Timioara Facultatea de Automatic i Calculatoare

Dorin Berian

Adrian Coco

PROGRAMARE ORIENTAT PE OBIECTE


ndrumtor de laborator

Cuvnt nainte Acest ndrumtor se adreseaz studenilor din anul II Ingineria Sistemelor i anul II Informatic (Facultatea de Automatic i Calculatoare de la Universitatea Politehnica din Timioara) la disciplina Programare Orientat pe Obiecte. Materialul cuprinde 9 lucrri de laborator care cuprind att parte teoretic ct i parte aplicativ cu scopul deprinderii stilului de programare specific limbajelor de programare orientate pe obiecte, n particular limbajul C++.

Dorin Berian

Cuprins
Laboratorul 1: Completri aduse de limbajul C++ fa de limbajul C Laboratorul 2: ncapsularea prin intermediul claselor . Laboratorul 3: Pointeri la metode. Funcii inline. Membri statici ... Laboratorul 4: Constructori i destructori ... Laboratorul 5: Funcii i clase prietene ... Laboratorul 6: Motenirea (derivarea) claselor ... Laboratorul 7: Metode virtuale. Utilizarea listelor eterogene ..................................... Laboratorul 8: Motenire multipl ... Laboratorul 9: abloane n C++ .. Bibliografie ..........

7 13 17 23 29 33 41 49 55 61

Laborator 1 POO:
Completri aduse de limbajul C++ fa de limbajul C
Obiectivul laboratorului: Formarea unei imagini generale, preliminare, despre programarea orientat pe obiecte (POO) i deprinderea cu noile facilitai oferite de limbajul C++. Beneficiul: Completrile aduse limbajului C vin n sprijinul programatorului i i ofer acestuia noi instrumente de lucru, permi!ndu"i s reali#e#e programe mult mai compacte, ntr" un mod avantajos din mai multe puncte de vedere$ modulari#are, fiabilitate, reutili#area codului etc. %e e&emplu, suprancrcarea funciilor permite reutili#area numelui funciei i pentru alte funcii, mrind astfel li#ibilitatea programului. " " " " " Cuprins: 'aboratorul trata aspecte referitoare la$ intrri i ieiri( suprancrcarea funciilor( alocarea dinamic a memoriei (operatorii new i delete)( parametrii cu valori implicite( transferul prin referin(

1. Conceptele POO
)rincipalele concepte (caracteristici) ale POO sunt$ ncapsularea * contopirea datelor cu codul (metode de prelucrare si acces la date) n clase, duc!nd la o locali#are mai bun a erorilor i la modulari#area problemei de re#olvat( mo tenirea " posibilitatea de a e&tinde o clasa prin adaugarea de noi functionalitati polimorfismul * ntr"o ierar+ie de clase obtinuta prin mostenire, o metod poate avea implementari diferite la nivele diferite in acea ierar+ie(

!. "ntrri i ie iri
'imbajul C++ furni#ea# obiectele cin i cout, n plus fa de funciile scanf i printf din limbajul C. )e l!ng alte avantaje, obiectele cin i cout nu necesit specificarea formatelor. #$emplu:
cin >> variabila; cout << "sir de caractere" << variabila << endl;

,tili#area acestora necesita includerea +eader"ului bibliotecii de stream"uri, -iostream.+-. ,n stream este un concept abstract care desemnea# orice flu& de date de la o surs la o destinaie. Concret, stream"urile repre#int totalitatea modalitilor de reali#are a unor operaii de citire sau scriere. .peratorul >> are semnificaia de -pune la ...-, iar << are semnificaia de -preia de la ...-. #$emplu:
#include <iostream.h> void main(void) {

int a; float b; char c[20]; cout <<" astati un intre! " "<< endl; cin >> a; cout << " astati un numar real " " << endl; cin >> b; cout << " astati un sir de caractere " " << endl; cin >> c; cout << "#ti tastat " << a << "$ " << b << "$ " << c << "."; cout << endl;

%. &uprancrcarea funciilor. 'uncii cu parametrii implicii &uprancrcarea funciilor


'imbajul C00 permite utili#area mai multor funcii care au acelai nume, caracteristic numit suprancrcarea funciilor. 1dentificarea lor se face prin numrul de parametri i tipul lor. #$emplu:
int suma (int a$ int b) { return (a & b); % float suma (float a$ float b) { return (a & b); %

%ac se apelea# suma (3,5), se va apela funcia corespun#toare tipului int, iar dac se apelea# suma (2.3, 9), se va apela funcia care are parametrii de tipul float. 'a apelul funciei suma (2.3, 9), tipul valorii 23 va fi convertit automat de C00 n float (nu e nevoie de t4pecasting).

'uncii cu valori implicite


5ntr"o funcie se pot declara valori implicite pentru unul sau mai muli parametri. 6tunci c!nd este apelat funcia, se poate omite specificarea valorii pentru acei parametri formali care au declarate valori implicite. 7alorile implicite se specific o singur dat n definiie (de obicei n prototip). 6rgumentele cu valori implicite trebuie s fie amplasate la sf!ritul listei. #$emplu:
void adunare (int a'($ { ... ; % ... adunare (); ** <'> adunare ()); ** <'> adunare ()$ +); ** <'> double b')0)

adunare (($ )0); adunare ()$ )0); adunare ()$ +);

(. Operatorii new i delete


'imbajul C00 introduce doi noi operatori pentru alocarea dinamic de memorie, care nlocuiesc familiile de funcii -free- i -malloc- i derivatele acestora. 6stfel, pentru alocarea dinamic de memorie se folosete operatorul ne), iar pentru eliberarea memoriei se folosete operatorul delete. .peratorul -ne)- returnea# un pointer la #ona de memorie alocat dinamic (dac alocarea se face cu succes) i 9,'' dac alocarea de memorie nu se poate efectua. .peratorul -delete- eliberea# #ona de memorie la care pointea# argumentul su. #$emplu:
struct sistem { char nume[20]; float disc; int memorie; int consum; %; struct sistem ,-; void main(void) { - ' ne. sistem; -/>disc ' 0(0; -/>memorie ' )1; -/>consum ' 00; % delete -;

)rogramul pre#entat definete un pointer la o structur de tip sistem i aloc memorie pentru el, folosind operatorul ne). %e#alocarea memoriei se face folosind operatorul delete. %ac se dorete alocarea de memorie pentru un tablou de elemente, numrul elementelor se va trece dup tipul elementului pentru care se face alocarea. #$emplu:
- ' ne. sistem[20]; delete[] -;

" aloc memorie pentru 8: de elemente de tip sistem( " eliberarea memoriei

*. +ransferul prin referin


. referin este un alt nume al unui obiect (variabila). )entru a putea fi folosit, o referin trebuie iniiali#at in momentul declararii, devenind un alias (un alt nume) al obiectului cu care a fost iniiali#at. Folosind transferul prin referin, n funcii nu se va mai transmite ntreaga structur, ci doar adresa ei. ;embrii structurii pot fi referii folosind 2. sau 2"< * pentru pointeri. 5n ca#ul utili#rii referinelor, se lucrea# direct asupra obiectului referit. 9

Consider!nd un obiect 2& , prin ->&- se nelege 2referin la obiectul & . #$emplu:
void e- (int i) { i ' 3; % int n ' 4; e- (n); cout << n; / se va afi5a 4 void e- (int 2i) { i ' 3; % int n ' 4; e- (n);
cout << n; / se va afi5a 3

,eferina nu este un pointer ctre obiectul referit- este un alt nume al obiectului.

/. 0lte nouti aduse de C++ fa de C


a1 Comentarii de sf2r it de linie 'imbajul C admite delimitatorii C 2?@ @? pentru comentarii care se pot ntinde pe mai multe linii. C00 introduce delimitatorul 2?? pentru adugarea mai comod de comentarii de sf!rit de linie. Aot te&tul care urmea# dup 2?? p!n la sf!ritul liniei este considerat comentariu. #$emplu:
if (i '' 20) brea6; **iesire fortata din ciclu

b1 Plasarea declaraiilor 'imbajul C impune gruparea declaraiilor locale la nceputul unui bloc. C00 elimin acest inconvenient, permi!nd declaraii n interiorul blocului, de e&emplu imediat nainte de utili#are. %omeniul unei astfel de declaraii este cuprins intre po#iia declaraiei i sf!ritul blocului.

#$emplu: (rulai urmtoarea secven de program)


void main(void) { int i; cin >> i; int 7 ' (,i/); **declaratie 5i initiali8are de valoare cout << 7; %

c1 Operatorul de re3oluie 4operator de acces- operator de domeniu1

10

'imbajul C00 introduce operatorul de re#oluie ($$), care permite accesul la un obiect (sau variabil) dintr"un bloc n care acesta nu este vi#ibil, datorit unei alte declaraii. #$emplu: (rulai urmtoarea secven de program)
char s[20]' "variabila !lobala"; void afisea8a(void) { char s[20] ' 9variabila locala:; cout << ""s; **afisea8a variabila !lobal; cout << s; **afisea8a variabila locala %

d) 'uncii 5inline6 C00 ofer posibilitatea declarrii funciilor inline- care combin avantajele funciilor propriu"#ise cu cele ale macrodefiniiilor. 6stfel, la fiecare apelare, corpul funciei declarate inline este inserat n codul programului de ctre compilator. Fa de macrodefiniii (care presupun o substitutie de te&t ntr"o fa# preliminar compilrii), pentru funciile inline compilatorul inserea# codul obiect al funciei la fiecare apel. 6vantajul creterii de vite#a se pltete prin creterea dimensiunii codului. 6adar, funciile inline trebuie s fie scurte. #$emplu:
inline int com<arare(int a$int b) { if (a>b) return ); if (a<b) return 0; if (a''b) return /); %

Partea practic a laboratorului:


0plicaia 1 C se reali#e#e un program care preia de la tastatur urmtoarele informaii$ nume, prenume, v!rsta, adres, telefonul unei persoane. %up preluare, aceste informaii trebuie afiate. 0plicaia ! C se modifice urmtorul program astfel nc!t s devin funcional$
#include <iostream.h> void func=ie(int a')24$ double b$ double c')24.+(1$ char ,s'"<r!") { cout << ">n a'" << a << " b'" << b << " c'" << c << " s'" << s; %

11

void main(void) { func=ie(+(1$ +.($ ).+$ "a<el )"); func=ie(+(1$ +.($ ).+); func=ie(+(1$ +.(); func=ie(+(1.(); %

0plicaia % C se reali#e#e un program care calculea# produsul a dou numere reale i a dou numere comple&e, specificate prin parte reala i parte imaginar. Funciile de calcul al produselor vor avea acelai nume i parametri diferii. 7ntrebri: /. Ce este ncapsulareaE 8. Ce este motenireaE =. Ce este polimorfismulE B. Care sunt funciile de intrare?ieire n C00E D. ,nde trebuiesc plasate argumentele cu valori impliciteE F. Ce nseamn suprancarcarea funciilor n 'imbajul C00E G. Care sunt operatorii de alocare i de#alocare de memorie n limbajul C00E H. Ce este o referin E 3. Ieferina este un pointerE /:. Ce este operatorul de re#oluieE //. ,nde se pot plas declaraiile de variabile n cadrul 'imbajului C00E

12

Laborator 2 POO:
ncapsularea prin intermediul claselor
Scopul laboratorului: Prezentarea noiunilor de clas i obiect. Beneficiul: Clasele i obiectele folosite n POO i permit programatorului s realizeze programe mai compacte dect cele scrise n limbajele neobiectuale. De asemenea, pri din program pot fi mai uor reutilizate i noul program poate fi mai uor depanat. Scurt prezentare: cest laborator prezint noiunile de clas i obiect, precum i aspecte referitoare la! " definirea unei clase# " $ariabile i funcii membre# " declararea obiectelor#

1. ncapsularea ca principiu al POO


%n C++ ncapsularea este ndeplinit prin dou aspecte! &. folosirea claselor pentru unirea structurile de date i a funciilor destinate manipulrii lor# '. folosirea seciunilor pri ate i publice! care fac posibil separarea mecanismului intern de interfaa clasei# O clas reprezint un tip de date definit de utilizator, care se comport ntocmai ca un tip predefinit de date. Pe lng $ariabilele folosite pentru descrierea datelor, se descriu i metodele (funciile) folosite pentru manipularea lor. "nstan#a unei clase reprezint un obiect " este o $ariabil declarat ca fiind de tipul clasei definite. *ariabilele declarate n cadrul unei clase se numesc ariabile membru! iar funciile declarate n cadrul unei clase se numesc metode sau functii membru. +etodele pot accesa toate $ariabilele declarate n cadrul clasei, pri$ate sau publice. $embrii unei clase reprezint totalitatea metodelor i a $ariabilelor membre ale clasei. Sinta%a declarrii unei clase este urmtoarea:
specificator_clasa Nume_clasa { [ [ private : ] lista_membri_1] [ [ public : ] lista_membri_2] };

" " "

,pecificatorul de clas specificator&clasa poate fi! class# struct# union# &3

-umele clasei ('ume&clasa) poate fi orice nume, n afara cu$intelor rezer$ate limbajului C... ,e recomand folosirea de nume ct mai sugesti$e pentru clasele folosite, precum i ca denumirea claselor s nceap cu liter mare. (e/! class 0le$i) 1olosind specificatorii de clas 2struct3 sau 2union3 se descriu structuri de date care au aceleai proprieti ca i n limbajul C (neobiectual), cu cte$a modificri ! se pot ataa funcii membru# pot fi compuse din trei seciuni " pri$at, public i protejat (folosind specificatorii de acces pri ate, public i protected)# (iferen#a principal ntre specificatorii 2class3, 2struct3 i 2union3 este urmtoarea! pentru o clas declarat folosind specificatorul 2class3, datele membre sunt considerate implicit de tip pri ate, pn la prima folosire a unuia din specificatorii de acces public sau protected. Pentru o clas declarat folosind specificatorul 2struct3 sau 2union3, datele membre sunt implicit de tip public, pn la prima folosire a unuia din specificatorii pri ate sau protected. ,pecificatorul protected se folosete doar dac este folosit motenirea. Descrierea propriu"zis a clasei const din cele doua liste de membrii, prefi/ate de cu$intele c4eie 2pri ate3 i5sau 2public3. +embrii aparinnd seciunii 2public3 pot fi accesai din orice punct al domeniului de e/isten al respecti$ei clase, iar cei care aparin seciunii 2pri ate3 (att date ct i funcii) nu pot fi accesai dect de ctre metodele clasei respecti$e. 6tilizatorul clasei nu $a a$ea acces la ei dect prin intermediul metodelor declarate n seciunea public (metodelor publice). Definirea metodelor care aparin unei clase se face prefi/nd numele metodei cu numele clasei, urmat de 2!!3. ,imbolul 2!!3 se numete 2scope acces operator3 (operator de rezolu#ie sau operator de acces) i este utilizat n operaii de modificare a domeniului de $izibilitate. )%emplu:
class Stiva { int varf; int st[30]; public: void init void!; " }; void Stiva :: init { " } void!

%n stnga lui 2!!3 nu poate fi dect un nume de clasa sau nimic, n cel de"al doilea caz prefi/area $ariabilei folosindu"se pentru accesarea unei $ariabile globale ($ezi laboratorul &). %n lipsa numelui clasei n faa funciei membru nu s"ar putea face distincia ntre metode care poart nume identice i aparin de clase diferite. 14

)%emplu:
class Stiva { public: void init " }; class #levi { public: void init " }; void Stiva :: init { " } void #levi :: init { " }

void!;

void!;

void!

$$ metoda clasei Stiva

void!

$$ metoda clasei #levi

ccesarea membrilor unui obiect se face folosind operatorul 2.3 Dac obiectul este accesat indirect, prin intermediul unui pointer, se foloseste operatorul 7"87 Dup cum s"a mai spus, $ariabilele membru pri$ate nu pot fi accesate dect de metode care aparin clasei respecti$e.

Partea practic a laboratorului:


*plica#ia 1 , se scrie o aplicaie care implementeaz o sti$ cu ajutorul unui tablou. ,e $or implementa funciile de adugare n sti$, scoatere din sti$, afiare a sti$ei (toate elementele). *plica#ia 2 , se realizeze un program care implementeaz un meniu cu urmtoarele opiuni! +eniu! O limonada indulcita O limonada neindulcita fisare total incasari 9eire Clasa :emon pri$ate! total numar lamai (se foloseste cate una la fiecare limonada) 15

total numar cuburi de za4ar (cate ' la fiecare limonada indulcita) suma incasari (se incrementeaza cu pretul corespunzator) public! initializare (se specifica numarul de lingurite de za4ar si de lamai disponibile) bea o limonada indulcita ($erificare! mai este za4ar, mai este lamaie <) bea o limonada neindulcita ($erificare! mai este lamaie<) afisare total incasari Daca acele condiii nu se $erific, se afieaz mesajele corespunztoare. ntrebri: &. '. ;. =. >. ?. @. Ce este ncapsularea< Ce este o clasa< Ce este un obiect< Ce este o funcie membra< Care este diferena ntre clase i structuri< Pentru ce este utilizat 7scope acces operator7< *ariabilele membru pri$ate pot fi accesate i n afara clasei respecti$e<

16

Laborator 3 POO:
Pointeri la metode. Funcii inline. Membri statici
Scopul laboratorului: familiarizarea cu noiunile de pointer (n special cu noiunea de pointer la metode), funcii inline i membrii statici. Beneficii: utilizarea pointerilor la metode aduce o flexibilitate sporit n conceperea programelor; utilizarea funciilor inline crete viteza de execuie a programelor; utilizarea membrilor statici ajut la reducerea numrului de variabile globale;

1. Pointeri la metode
Dei o funcie nu este o variabil, ea are o localizare n memorie, care poate fi atribuit unui pointer. Adresa funciei respective este punctul de intrare n funcie; ea poate fi obinut utilizndu-se numele funciei, fr nici un argument (similar cu obinerea adresei unei matrici). Un pointer la metod desemneaz adresa unei funcii membru m. Metodele unei clase au implicit un parametru de tip pointer la obiect, care se transmite ascuns. Din acest motiv, parametrul n cauz nu apare in lista de parametri a funciei desemnate de pointer. Exemplu:
class clasa { int contor; public: void init (int nr = 0) { contor = nr; } int increment (void) { return contor++; } }; /* tipul "pointerLaMetoda" este un pointer la o metoda a clasei "clasa", metoda care nu are parametri si care returneaza "int" */ typedef int (clasa::*pointerLaMetoda)(void); void main(void) { clasa c1, *pc1 = &c1;

17

pointerLaMetoda pM = &(clasa :: increment); c1.init (1); pc1->init(2); int i = (c1.*pM)(); i = (pc1->*pM)(); }

Dup cum se observ n acest exemplu, pointerul poate pointa ctre orice metod a clasei clasa.

2. Funcii inline
Funciile inline sunt eficiente n cazurile n care transmiterea parametrilor prin stiv (operaie lent) este mai costisitoare (ca i timp de execuie) dect efectuarea operaiilor din corpul funciei. Exemplu:
class coordonate_3D { int X,Y,Z; // ATENTIE: // prin definirea metodei in interiorul declararii clasei, // functia membru devine implicit "inline" !! void translateaza(int TX, int TY, int TZ) { X+=TX; Y+=TY; Z+=TZ; } void tipareste(void); // declararea unei metode care nu este // implicit inline }; // Prefixarea definitiei metodei cu cuvintul cheie "inline" este // echivalenta cu definirea functiei membru in cadrul declaratiei // clasei inline void coordonate_3D::tipareste(void) { cout << "\n\tX=" << X << "\tY=" << Y << "\tZ=" << Z << "\n"; }

Deoarece procedura "translateaz" este de tip inline, ea nu este apelat, ci expandat atunci cnd este apelat.

18

Definirea unei funcii n cadrul unei clase se mai numete i declarare inline implicit. Metoda tipareste este declarat explicit, ea fiind doar declarat n cadrul clasei, iar n locul unde este definit este prefixat de cuvntul cheie inline. Metodele care nu sunt membru al unei clase nu pot fi declarate inline dect explicit. Este interzis folosirea n cadrul funciilor inline a structurilor repetitive (for, while, do while), i a funciilor recursive.

3. Membri statici
Principala utilizare a variabilelor membru statice const n eliminarea n ct mai mare msur a variabilelor globale utilizate ntr-un program. Cuvntul cheie static poate fi utilizat n prefixarea membrilor unei clase. Odat declarat static, membrul n cauz are proprieti diferite, datorit faptului c membrii statici nu aparin unui anumit obiect, ci sunt comuni tuturor instanierilor unei clase. Pentru o variabil membru static se rezerv o singur zon de memorie, care este comun tuturor instanierilor unei clase (obiectelor). Variabilele membru statice pot fi prefixate doar de numele clasei, urmat de operatorul de de rezoluie ::. Apelul metodelor statice se face exact ca i accesarea variabilelor membru statice. Deoarece metodele statice nu sunt apelate de un obiect anume, nu li se transmite pointerul ascuns this. Dac ntr-o metod static se folosesc variabile membru nestatice, e nevoie s se furnizeze un parametru explicit de genul obiect, pointer la obiect sau referin la obiect. Toi membrii statici sunt doar declarai n cadrul clasei, ei urmnd a fi obligatoriu iniializai. Exemplu
class exemplu { int i; public: static int contor; // variabila membru statica static inc (void) {i++;} // metoda statica void inc_contor (void) {contor++;} void init (void) {i = 0;} static void functie (exemplu *); // metoda statica } ob1, ob2, ob3; int exemplu::contor = 0; // initializarea variabilei statice

void exemplu::functie(exemplu *ptrEx) { // i += 76 // eroare - nu se cunoaste obiectul de care // apartine i ptrEx -> i++; // corect

19

contor ++; } void main(void) { ob1.init(); ob2.init(); ob3.init(); ob1.inc(); ob2.inc(); ob3.inc();

// corect

ob1.functie(&ob1); exemplu :: functie(&ob2); // functie();

// corect // corect

// incorect - in afara cazului in care // exista o metoda ne-membru cu acest nume

ob1.inc_contor(); ob2.inc_contor(); ob3.inc_contor(); exemplu :: contor+=6; }

Partea practic a laboratorului: Aplicaia 1 S se scrie un program care implementeaz o stiv de numere ntregi, utiliznd o clas. Membrii variabili ai clasei indic vrful stivei i tabloul de ntregi n care se rein elementele stivei. Funciile membru ale clasei vor fi: - o funcie pentru iniializarea pointerului n stiv; - o funcie pentru introducerea unei noi valori n stiv; - o funcie pentru scoaterea unei valori din stiv; - o funcie pentru afiarea ntregii stive; Se va folosi un meniu de selecie care va avea opiuni cerinele programului. Timp de rezolvare: 50 min. ntrebri: 1. 2. 3. 4. Ce sunt pointerii? Ce sunt pointerii la metode? Cum se declar pointerii la metode? Ce sunt funciile inline?

20

5. 6. 7. 8. 9. 10.

Cum se declar funciile inline? Care este avantajul folosirii funciilor inline? Care sunt restriciile impuse funciilor inline? Ce sunt membri statici? Membri statici aparin unei clase? Cum se declar u membru static?

Teme de cas:
Aplicaia 1 Implementai o coad de numere ntregi (structur de date de tip FIFO - first-in, firstout), utilizndu-se o clas. Membrii variabili ai clasei indic vrful stivei i tabloul de ntregi n care se rein elementele stivei. Funciile membru ale clasei vor fi: - o funcie pentru iniializarea pointerului n stiv; - o funcie pentru introducerea unei noi valori n stiv; - o funcie pentru scoaterea unei valori din stiv; Se va folosi un meniu de selecie care va avea ca opiuni cerinele programului.

Aplicaia 2 S se scrie un program care implementeaz o list simplu nlnuit utiliznd o clas. Membrii variabili ai clasei indic urmtorul element i coninutul unui element al listei (un ntreg). Funciile membru ale clasei vor fi: - o funcie pentru iniializarea listei; - o funcie pentru introducerea unei noi valori n list; - o funcie pentru scoaterea unei valori din list; Se va folosi un meniu de selecie care va avea ca opiuni cerinele programului.

21

22

Laborator 4 POO:
Constructori i destructori
Scopul laboratorului: prezentarea mecanismelor de iniializare i de distrugere a unor proprieti ale obiectelor, folosind constructorii i a destructorii. Beneficii: utilizarea constructorilor i a destructorilor ofer programatorului instrumentele necesare iniializrii unor proprieti ale obiectelor dintr-o clas, precum i a dezalocrii (distrugerii) acestora, n mod automat, fr a fi nevoie de aplearea unor funcii separate pentru aceasta. Folosirea contructorilor i a destructorilor permite scrierea programelor ntr-un mod mai compact i mai uor de nteles.

1. Constructori
Constructorul este o metod special a unei clase, care este membru al clasei respective i are acelai nume ca i clasa. Constructorii sunt apelai atunci cnd se instaniaz obiecte din clasa respectiv, ei asigurnd iniializarea corect a tuturor variabilelor membru ale unui obiect i garantnd c iniializarea unui obiect se efectueaz o singur dat. Constructorii se declar, definesc i utilizeaz ca orice metod uzual, avnd urmtoarele proprieti distinctive: - poart numele clasei creia i aparin; - nu pot returna valori; n plus (prin convenie), nici la definirea, nici la declararea lor nu poate fi specificat void ca tip returnat; - adresa constructorilor nu este accesibil utilizatorului; expresii de genul &X :: X() nu sunt disponibile; - sunt apelai implicit ori de cte ori se instaniaz un obiect din clasa respectiv; - n caz c o clasa nu are nici un constructor declarat de ctre programator, compilatorul va declara implicit unul. Acesta va fi public, fr nici un parametru, i va avea o list vid de instruciuni; - n cadrul constructorilor se pot utiliza operatorii "new" si "delete", - constructorii pot avea parametrii. O clas poate avea orici constructori, ei difereniindu-se doar prin tipul i numrul parametrilor. Compilatorul apeleaz constructorul potrivit n funcie de numrul i tipul parametrilor pe care-i conine instanierea obiectului.

Tipuri de constructori
O clas poate conine dou tipuri de constructori: - constructor implicit (default constructor); - constructor de copiere (copy constructor); Constructorii implicii se poate defini n dou moduri:

23

a. definind un constructor fr nici un parametru; b. prin generarea sa implicit de ctre compilator. Un astfel de constructor este creat ori de cte ori programatorul declar o clas care nu are nici un constructor. n acest caz, corpul constructorului nu conine nici o instruciune. O clas poate conine, de asemenea, constructori de copiere. Constructorul de copiere generat implicit copiaz membru cu membru toate variabilele argumentului n cele ale obiectului care apelaz metoda. Compilatorul genereaz implicit un constructor de copiere n fiecare clas n care programatorul nu a declarat unul n mod explicit. Exemplu:
class X { X (X&); X (void); }; // constructor de copiere // constructor implicit

Apelarea constructorului se copiere se poate face n urmtoarele moduri:


X obiect2 = obiect1;

sau sub forma echivalent:


X obiect2 (obiect1);

2. Destructorii
Destructorul este complementar constructorului. Este o metod care are acelai nume ca i clasa creia i aparine, dar este precedat de ~. Dac constructorii sunt folosii n special pentru a aloca memorie i pentru a efectua anumite operaii (de exemplu: incrementarea unui contor al numrului de obiecte), destructorii se utilizeaz pentru eliberarea memoriei alocate de constructori i pentru efectuarea unor operaii inverse (de exemplu: decrementarea contorului). Exemplu:
class exemplu { public : exemplu (); ~exemplu (); };

// constructor // destructor

Destuctorii au urmtoarele caracteristici speciale: - sunt apelai implicit n dou situaii: 1. cnd se realizeaz eliberarea memoriei alocate dinamic pentru memorarea unor obiecte, folosind operatorul delete (a se vedea linia 10 din programul de mai sus); 2. la prsirea domeniului de existen al unei variabile (vezi linia 17, variabila pb). Dac n al doilea caz este vorba de variabile globale sau definite n main, distrugerea lor se face dup ultima instruciune din main, dar nainte de ncheierea execuiei programului. Utilizatorul dispune de dou moduri pentru a apela un destructor:

24

1. prin specificarea explicit a numelui su metoda direct; Exemplu:


class B { public: ~B(); }; void { } main (void) B b; b.B::~B(); // apel direct : e obligatoriu prefixul "B::"

2. folosind operatorul delete (metod indirect a se vedea linia 10 din programul urmtor). Exemplu (este menionat ordinea executrii):
#define NULL 0 struct s { int nr; struct s *next; }; class B { int i; struct s *ps; public: B (int); ~B (void); }; B :: B (int ii = 0) // 3 si 7 { ps = new s; ps->next = NULL; i = ps->nr = ii; // 4 si 8 } // 5 si 9 B :: ~B (void) { delete ps; } void main (void) { B *pb; B b = 9; pb = new B(3); delete pb; } // 11 si 14 // 12 si 15 // 13 si 16 // 1 // // // // 2 6 10 17

25

ntrebri: Ce sunt constructorii? Cum se declar constructorii? Ce tip de dat poate returna un constructor? Constructorii pot avea parametri? Cum se apeleaz constructorii? Ce sunt constructorii de copiere? O clasa poate avea mai muli constructori? Dac da, atunci cum tie compilatorul s fac diferenierea ntre acetia? 8. Ce este un destructor? 9. Ci destructori poate avea o clas? 10. Cum se poate apela un destructor? 1. 2. 3. 4. 5. 6. 7.

Partea practic a laboratorului:


Aplicaia 1
S se realizeze o list simplu nlanuit de iruri de caractere (albume). Prototipul clasei este urmtorul:
class node { static node *head; node *next; char *interpret; char *melodie; public: node (char * = NULL); void display_all (); void citireAlbum (); }; node *node :: head = NULL; //initializare cap lista node :: node(char *ptr, char *ptr1) // constructorul clasei { } void node :: display_all () { } void node:: citireAlbum () { }

//pointer la lista //pointer catre urmatorul //element //numele unui obiect din lista //numele unei melodii din //lista //declararea constructorului //afiseaza nodurile listei //citirea informatiilor despre //album

26

void main() { }

Se va folosi un meniu, care s implementeze cerinele programului: citire album, creare list i afiarea ntregii liste (a ntregului album).

Teme de cas:
Aplicaia 1 S se dezvolte aplicaia de la laborator astfel nct s se poate modifica o valoare (a albumului), ordona cresctor dup melodie i terge un nod din list (un album). Aplicaia 2 Aceleai cerine, pentru o list dublu nlnuit.

27

28

Laborator 5 POO:
Functii si clase prietene
Scopul laboratorului: prezentarea mecanismelor de acces la datele membre ale unei clase prin intermediul funciilor friend (prietene) i a claselor friend. Beneficii: utilizarea funciilor friend i a claselor friend ofer programatorului mai mult flexibilitate n dezvoltarea unui program, dndu-i posibilitatea s acceseze variabilele membru ale unei clase. Astfel, dac ntr-un program este necesar accesarea variabilelor membru ale unei clase se poate utiliza o funcie friend n locul unei funcii membre.

1. Funcii friend
O funcie friend este o funcie care nu e membru a unei clase, dar are acces la membrii de tip private i protected ai clasei respective. Orice funcie poate fi friend unei clase, indiferent dac este o funcie obinuit sau este membru al unei alte clase. Exemplu: class exemplu { int a; int f (void); friend int f1(exemplu &); public: friend int M::f2(exemplu &, int); }; int f1(exemplu &ex) { return ex.f (); } int M :: f2(exemplu &ex, int j = 0) { if (ex.a > 7) return j++; else return j--; } Dup cum se observ, nu conteaz dac o funcie este declarat friend n cadrul seciunii private sau public a unei clase.

2. Clase friend
Dac se dorete ca toi membrii unei clase M s aib acces la partea privat a unei clase B, n loc s se atribuie toate metodele lui M ca fiind friend ai lui B, se poate declara clasa M ca i clas friend lui B.

29

Exemplu: class M { // ... }; class B { // ... friend class M; }; Relaia de friend nu este tranzitiv, adic dac clasa A este friend clasei B, iar clasa B este friend clasei C, aceasta nu implic faptul c, clasa A este implicit friend clasei C. Funciilor friend nu li se transmite parametrul ascuns this. Aceast caren este suplinit prin transmiterea unor parametrii obinuii de tip pointer, obiect sau referin la obiect. Exemplu:
class rational; // declarare incompleta class complex { double p_reala, p_imaginara; friend complex& ponderare (complex&, rational&); public:
complex (double r, double i) : p_reala (r), p_imaginara (i)

double get_real (void) {return p_reala;} double get_imaginar (void) {return p_imaginara;} }; class rational { int numarator, numitor; double val; public: friend complex& ponderare (complex& ,rational&); rational (int n1, int n2) : numarator (n1) { numitor = n2!=0 ? n2 : 1; val = ((double)numarator)/numitor; } double get_valoare(void) { return val; } }; // fiind "friend", functia ponderare are acces la membrii privati ai // claselor "complex" si "rational" complex &ponderare(complex& c,rational& r) { complex *t = new complex (c.p_reala *r.val, c.p_imaginara *r.val); return *t; }

30

// nefiind "friend", "ponderare_ineficienta" nu are acces la membrii // privati ai claselor "complex" si "rational" complex &ponderare_ineficienta (complex &c, rational &r) { complex *t = new complex (c.get_real()*r.get_valoare(), c.get_imaginar()*r.get_valoare()); return *t; } void main(void) { complex a(2,4),b(6,9); rational d(1,2),e(1,3); a = ponderare(a,d); b = ponderare(b,e); a = ponderare_ineficienta(a,d); b = ponderare_ineficienta(b,e); }

Partea practic a laboratorului


Aplicaia 1 Folosind funcii i clase friend, s se realizeze un program care implementeaz gestiunea unui magazin de CD-uri, folosind o clas CD. Fiecare obiect de tip CD are cmpurile: interpret, titlu i melodii_componente. Melodiile trebuie memorate sub forma unei liste simplu nlnuite. Programul trebuie s permit 2 opiuni: introducerea informaiilor pentru CD-uri i afiarea tuturor CD-urilor n ordinea introducerii (titlu, interpret, melodiile care le cuprind). Cele 2 funcii care realizeaz aceste operaii vor fi funcii friend ale clasei CD i nu metode ale acesteia. Timp de rezolvare: 50 min. ntrebri: 1. 2. 3. 4. 5. Ce sunt funciile prietene? Cum se declar funciile prietene? Ce sunt clasele prietene? Cum se declar clasele prietene? n ce seciune a clasei se declar funciile prietene?

Teme de cas
Aplicaia 1 S se dezvolte aplicaia de la laborator astfel nct s se poate modifica orice informaie despre CD-uri, s permit ordonarea n mod cresctor dup melodii precum i tergerea unui

31

nod din lista de CD-uri. De asemenea se vor folosi funcii prietene pentru accesul la membri privai ai clasei Aplicaia 2 Aceleai cerine, dar pentru o list dublu nlnuit.

32

Laborator 6 POO:
Motenirea (derivarea) claselor
Scopul laboratorului: prezentarea motenirii claselor, precum i a utilizrii constructorilor claselor derivate. Beneficiul: utilizarea motenirii permite realizarea de ierarhi de clase, ceea ce duce la o mai bun modularizare a programelor.
1.

Principiul motenirii (derivrii) claselor

Considernd o clas oarecare A, se poate defini o alt clas B, care s preia toate caracteristicile clasei A, la care se pot aduga altele noi, proprii clasei B. Clasa A se numete clas de baz, iar clasa B se numete clas derivat. Acesta este numit mecanism de motenire!. "n declara#ia clasei derivate nu mai apar informa#iile care sunt motenite, ele fiind automat luate $n considerare de ctre compilator. %u mai trebuie rescrise func#iile membru ale clasei de baz, ele putnd fi folosite $n maniera $n care au fost definite. &ai mult, metodele din clasa de baz pot fi redefinite 'polimorfism(, avnd o cu totul alt func#ionalitate. Exemplu:
//clasa "hard_disk" este derivata din clasa "floppy_disk" enum stare_operatie {REUSIT, ESE !" enum stare_protectie_la_scriere { #R$TE%&T, 'E#R$TE%&T ! class floppy_disk { protected(

!"

// cuvantul cheie "protected" permite // declararea unor mem)rii nepu)lici, care // sa poata fi accesati de catre // eventualele clase derivate din // "floppy_disk" stare_protectie_la_scriere indicator_protectie" int capacitate, nr_sectoare" pu)lic( stare_operatie formatare *+" stare_operatie citeste_pista *int drive, int sector_de_start, int numar_sectoare, void ,)uffer+" stare_operatie scrie_pista *int drive, int sector_de_start, int numar_sectoare, void ,)uffer+" stare_operatie prote-ea.a_la_scriere*void+"

class hard_disk ( pu)lic floppy_disk { int numar_partitii" pu)lic( stare_operatie parchea.a_disc *+" !"

33

stare_operatie hard_disk (( parchea.a_disc *+ { // $RE T( accesarea unui mem)ru de tip "protected" indicator_protectie / #R$TE%&T" //000 return REUSIT"

void functie *+ { hard_disk hd1" hd10formatare*+" // $RE T hd10indicator_protectie / 'E#R$TE%&T" // ER$&RE( incercare // de accesare a unui mem)ru prote-at !

)rin enun#ul: class hard_disk ( pu)lic floppy_disk se indic compilatorului urmtoarele informa#ii: * se creaz o clas numit hard_disk!, care este derivat 'motenit( din clasa floppy_disk!+ * to#i membrii de tip public! ai clasei floppy_disk! vor fi moteni#i 'i deci vor putea fi folosi#i( ca public! de ctre clasa hard,dis-!+ * to#i membrii de tip protected! ai unui obiect de tip floppy_disk! vor putea fi utiliza#i ca fiind protected! $n cadrul clasei hard_disk!+

! "eclararea unei clase derivate


)entru a declara o clas derivat dintr*o clas de baz se folosete urmtoarea sinta.:
specificator_clasa nume_clasa_derivata( 2modificator_acces_13 nume_clasa_)a.a_1 2 , 2 modificator_acces_4 3 nume_clasa_)a.a_4 3 2 , 000 3 3 { 2 2private( 3 lista_mem)ri_1 3 2 2protected( 3 lista_mem)ri_4 3 2 2pu)lic ( 3 lista_mem)ri_5 3 !" 2lista_o)iecte3"

"n func#ie de tipul lui specificator_clasa ' class! sau struct!(, modificator_acces_1 va lua valoarea implicit private sau public. /olul acestui modificator_acces_1 este ca, $mpreun cu specificatorii public!, private! sau 34

protected! $ntlni#i $n cadrul declarrii clasei de baz, s stabileasc drepturile de accesare a membrilor moteni#i de ctre o clas derivat. 1abelul urmtor sintetizeaz drepturile de accces a membrilor unei clase derivate $n func#ie de drepturile de accesare a membrilor clasei de baz i valoarea lui modificator_acces_1. "rept de acces #n clasa de ba$ public private )rotected )ublic private )/213C134 Modificator de acces public public public private private )/56A13 "rept de acces #n clasa derivat public inaccesibil protected private inaccesibil )/56A13

Cuvntul cheie protected! nu poate fi folosit ca modificator de acces $n cadrul unei rela#ii de motenire. /olul su se limiteaz la a permite accesarea din cadrul unei clase derivate a membrilor nepublici ai clasei de baz. 7unc#iile membre ale clasei derivate nu au acces la membrii priva#i ai clasei de baz. 2 alt observa#ie este aceea c orice friend 'func#ie sau clas( a unei clase derivate are e.act aceleai drepturi i posibilit#i de a accesa membrii clasei de baz ca oricare alt membru al clasei derivate. Exemplu:
class &n6a-at { private( char nume2573" // alte caracteristici( data de na8tere, adresa etc0 pu)lic( &n6a-at *+" &n6a-at *const char ,+" char ,6etname *+ const" dou)le calcul_salariu *void+" !" class 9uncitor( pu)lic &n6a-at { private( dou)le plata" dou)le ore" pu)lic( &n6a-at_4 *const char ,nm+" void calcul_plata *dou)le plata+" void nr_ore *dou)le ore+" dou)le calcul_salariu *+" !" class :;n.<tor( pu)lic 9uncitor { private(

85

dou)le comision" dou)le v;n.<ri" pu)lic( :;n.<tor *const char ,nm+" void setare_comision *dou)le comis+" void setare_v;n.<ri *dou)le van.+" dou)le calcul_plata *+" !" class 9ana6er( pu)lic &n6a-at { private( dou)le salariu_s<pt" pu)lic( 9ana6er *const char ,nm+" void setare_salariu * dou)le salary+" dou)le calcul_plata *+" !"

Cuvntul cheie const folosit dupa lista de parametri ai unei func#ii declar func#ia membru ca i func#ie read-only! 9 func#ia respectiv nu modific obiectul pentru care este apelat. 7unc#ia calcul_plata () poate fi scris pentru diferitele tipuri de anga:a#i :
dou)le &n6a-at_plata_ora (( calcul_plata *+ const { return plata , ore" ! dou)le :;n.<tor (( calcul_plata *+ const { return &n6a-at_plata_ora((calcul_plata*+ = commision , v;n.<ri" !

Aceast tehnic este folosit de obicei atunci cnd se redefinete o func#ie membru $ntr*o clas derivat. 6ersiunea din clasa derivat apeleaz versiunea din clasa de baz i apoi efectueaz celelalte opera#ii necesare.

%! &onstructorii claselor derivate


2 instan#iere a unei clase derivate con#ine toti membrii clasei de baz i to#i acetia trebuie ini#ializa#i. Constructorul clasei de baz trebuie apelat de constructorul clasei derivate.
// constructorul clasei &n6a-at_plata_ora
&n6a-at_plata_ora((&n6a-at_plata_ora*const char ,nm+(&n6a-at*nm+

{ !

plata / 707" ore / 707"

36

// constructorul clasei :;n.<tor :;n.<tor((:;n.<tor*const char ,nm+ ( &n6a-at_plata_ora*nm+ { commision / 707" v;n.<ri / 707" ! // constructorul clasei 9ana6er 9ana6er (( 9ana6er *const char ,nm+ ( :;n.<tor *nm+ { >eeklySalary / 707" !

Cnd se declar un obiect dintr*o clas derivat, compilatorul e.ecut $nti constructorul clasei de baz, apoi constructorul clasei derivate 'dac clasa derivat con#ine obiecte membru, constructorii acestora sunt e.ecuta#i dup constructorul clasei de baz, dar $naintea constructorului clasei derivate(.

'! &onversii #ntre clasa de ba$ i clasa derivat


<imba:ul &(( permite conversia implicit a unei instan#ieri a clasei derivate $ntr*o instan#iere a clasei de baza. 4e e.emplu:
9uncitor mn" :;n.ator van. *"#opescu Ion"+" mn / van." // conversie derivat /? )a.<

4e asemenea, se poate converti un pointer la un obiect din clasa derivat $ntr*un pointer la un obiect din clasa de baz. Conversia derivat) *= ba$a) se poate face: * implicit * dac pointer*ul derivat motenete pointer*ul baz prin specificatorul public+ * explicit: dac pointer*ul derivat motenete pointer*ul baz prin specificatorul private; Conversia ba$a> *= derivat) nu poate fi fcut dect explicit, folosind operatorul cast. Cnd se acceseaz un obiect printr*un pointer, tipul pointer*ului determin care func#ii membre pot fi apelate. 4ac se acceseaz un obiect din clasa derivat printr*un pointer la clasa de baz, pot fi apelate doar func#iile definite $n clasa de baz. 4ac se apeleaz o func#ie membru care este definit att $n clasa de baz ct i $n cea derivat, func#ia care este apelat depinde de tipul pointerului:
dou)le )rut, total" )rut / munc @? calcul_salariu *+" // se apelea.< 9uncitor (( calcul_salariu *+ total / van. @? calcul_salariu *+" // se apelea.< :an.ator (( calcul_salariu *+

&onversia invers trebuie fcut e.plicit:

37

9uncitor ,munc / Amunc_1" :an.ator ,van." van. / *:an.ator ,+ munc"

Aceast conversie nu este recomandat, deoarece nu se poate ti cu certitudine ctre ce tip de obiect pointeaz pointer*ul la clasa de baz. 2 problem care poate apare este, de e.emplu, calcularea salariului fiecrui anga:at din list. 4up cum s*a men#ionat anterior, func#ia apelat depinde de tipul pointerului, ca urmare este nesatisfcatoare apelarea func#ia calcul_salariu () folosind doar pointeri la clasa Angajat. 3ste necesar apelarea fiecarei versiuni a func#iei calcul_salariu () folosind pointeri generici, lucru posibil folosind func#iile virtuale 'vor fi prezentate $n laboratorul @(.

Partea practic a laboratorului


+plica,ia A se implementeze o aplica#ie care #ine eviden#a studen#ilor pe sec#iuni: $n campus respectiv $n ora. 2 posibil structur este cea prezentat mai :os:
class student { char ,name, ,prenume" int year, varsta" pu)lic( void display*+" student *char ,, int+" Bstudent*+" !" class on_campus ( pu)lic student { char ,dorm, ,room" pu)lic( void a_disp*+" // afisea.a informaCii0 *nume, an de // studiu, camin, camera+ pentru un // student on_campus*char ,, int, char ,, char ,+"// constructor Bon_campus *+" // destructor !" class off_campus ( pu)lic student { // Dn mod analo6 cu clasa precedent<( // campuri care indica adresa off_campus *strada, oras, // nr0+ a unui student 000 // constructorul clasei 000 // destructorul // functie de afisare a inform0 *nume, an de studiu, // adresa+ a unui student !"

// // // //

afisarea inform0 *nume si an de studiu+ pentru un student constructorul destructorul

38

void main *+ { // se declara cite o instanta a fiecarei clase // sa se afise.e informatiile referitoare la fiecare // persoana declarata !

.ntrebri: 1. Ce este motenireaC 0. Cum se realizeaz motenireaC 8. Care sunt drepturile de acces la membrii unei clase de baz $n func#ie de tipul moteniriiC ;. Ce sunt constructoriiC ?. Cum se apeleaz constructorii clasei de bazC B. Care constructor se apeleaz primul, al clasei de baz sau al clasei derivateC @. Cum se fac conversiile $ntre clasa de baz i clasa derivatC D. Cnd este folosit conversia implicitC E. Cnd este necesar conversia e.plicitC 1F. Cum se poate apela un destructorC /eme de cas: +plica,ia A se dezvolte aplica#ia de la laborator folosind pentru implementare liste de obiecte. +plica,ia Aceleai cerin#e pentru o list dublu $nln#uit.

39

40

Laborator 7 POO: Metode virtuale. Utilizarea listelor eterogene


Scopul Lucrrii: familiarizarea cu noiunile de metode virtuale precum i cu modul de utilizare a listelor eterogene. Beneficiul: - utilizarea metodelor virtuale va permite redefinirea / reutilizarea metodelor - utilizarea metodelor virtuale ne va permite s crem i s utiliz liste eterogene Scurt prezentare: n cadrul acestui laborator se vor studia: - metodele virtuale, - listele eterogene, Prezentarea laboratorului: Prin definiie, un tablou omogen este un tablou ce conine elemente de acelai tip. Un pointer la o clasa "B" poate pstra adresa oricrei instanieri a vreunei clase derivate din "B". Deci, avnd un ir de pointeri la obiecte de tip "B" , nseamn c, de fapt, putem lucra i cu tablouri neomogene. Astfel de tablouri neomogene se vor numi ETEROGENE. Una dintre caracteristicile limbajului C++ const tocmai n faptul c mecanismul funciilor virtuale permite tratarea uniform a tuturor elementelor unui masiv de date eterogene. Acest lucru este posibil datorit unor faciliti ce in de asocieri fcute doar n momentul execuiei programului, nu n momentul compilrii. Fie o clas "B" care posed o metod public "M". Din clasa "B" se deriv mai multe clase "D1", "D2", "D3",...,"Dn". Dac aceste clase derivate redefinesc metoda "M" se pune problema modului n care compilatorul este capabil s identifice corect fiecare dintre aceste metode. n mod obinuit identificarea funciei membru n cauz se poate face prin una din urmtoarele metode: 1. prin unele diferene de semnatur (n cazul n care metoda a fost redeclarat) 2. prin prezena unui scope resolution operator (o exprimare de genul int a=B::M() este univoc) 3. cu ajutorul obiectului cruia i se aplic metoda. O secven de genul: D1 d; d.M(); nu las nici un dubiu asupra metodei n cauz. n aceste cazuri, decizia este deosebit de simpla i poate fi luat chiar n faza de compilare. n cazul prelucrrii de liste eterogene situaia este mai complicat, fiind implicat o rezolvare mai trzie a asociaiilor ntre numele funciei apelate i funcia de apelat. Fie

41

irul eterogen de pointeri la obiecte de tipul "B", "D1", "D2" etc. Se presupune, de exemplu, c ntr-o procedur se ncearc apelarea metodei "M" pentru fiecare obiect pointat de catre un element al irului. Metoda de apelat nu va fi cunoscut n momentul compilrii deoarece nu este posibil s se stabileasc corect despre care din funcii este vorba ("M"-ul din "B" sau cel al unei clase derivate din "B"). Imposibilitatea identificrii metodei apare deoarece informaiile privind tipul obiectului la care pointeaz un element al irului nu vor fi disponibile dect n momentul executrii programului. n continuare se analizeaz un exemplu clasic de tratare a unei liste neomogene, care apoi este reluat utiliznd funcii virtuale. Exemplul : Se construiete un ir de 4 elemente ce conine pointeri att la clasa "BAZA" ct i la "DERIVAT_1" i "DERIVAT_2". #include <iostream.h> typedef enum {_BAZA_, _DERIVAT_1, _DERIVAT_2} TIP; class BAZA{ protected: int valoare; public: void set_valoare(int a) (valoare = a} void tipareste_valoare(void) { cout<<"Element BAZA cu VALOARE = "<<valoare<<"\n"; } }; class DERIVAT_1 : public BAZA{ public: void tipareste_valoare(void) { cout<<"Element DERIVAT_1 cu VALOARE = "<<valoare<<"\n"; } }; class DERIVAT_2 : BAZA { public: BAZA::set_val; //metoda "set_val" va fi fortata la "public" void tipareste_valoare(void) { cout<<"Element DERIVAT_2 cu VALOARE = "<<valoare<<"\n"; } };

42

class LISTA_ETEROGENA{ BAZA *pB; TIP t; public: void set(BAZA *p, TIP tp=_BAZA_) { pB = p; t = tp; } void tipareste_valoare(void) { if(t==_BAZA_) pB->tipareste_valoare(); else if(t==_DERIVAT_1) ((DERIVAT_1 *) pB->tipareste_valoare(); else ((DERIVAT_2 *) pB->tipareste_valoare(); } }; void main() { BAZA a[2]; DERIVAT_1 b1; DERIVAT_2 b2; LISTA_ETEROGENA p[4]; a[0].set_val(1); a[1].set_val(2); b1.set_val(3); b2.set_val(4); p[0].set(&a[0]); p[1].set(&b1,_DERIVAT_1); //se face o conversie implicita //"DERIVAT_1"->"BAZA*" p[2].set((BAZA *)&b2, _DERIVAT_2); //se face o conversie explicita //"DERIVAT_2"->"BAZA *" p[3].set(&a[1]); for(int i=0; i<4; i++) p[i].tipareste_valoare(); } n urma execuiei programului, pe ecran se vor afia urmtoarele mesaje: Element BAZA cu VALOARE = 1 Element DERIVAT_1 cu VALOARE = 3 Element DERIVAT_2 cu VALOARE = 4 Element BAZA cu VALOARE = 2

43

Pentru a reui o tratare uniform a celor 3 tipuri de obiecte a fost necesar crearea unei a 4-a clase, numit LISTA_ETEROGENA. Aceasta va pstra att pointer-ul la obiectul respectiv, ct i o informaie suplimentar, referitoare la tipul obiectului referit de pointer. Tiprirea informaiilor semnificative ale fiecarui obiect se va face n metoda LISTA_ETEROGENA::tipareste_valoarea. n aceast funcie membru sunt necesare o serie de teste pentru apelul metodei corespunznd fiecarui tip de obiect. Exemplul: Se prezint o modalitate mult mai simpl i elegant de a trata omogen un masiv de date eterogene. #include <iostream.h> class BAZA{ protected: int valoare; public: void set_val(int a) { valoare = a;} virtual void tipareste_valoare(void) { cout<<"Element BAZA cu VALOARE = "<<valoare<<"\n"; } }; class DERIVAT_1 : public BAZA { public: void tipareste_valoare(void) { cout<<"Element DERIVAT_1 cu VALOARE = "<<valoare<<"\n"; } }; class DERIVAT_2 : BAZA { public: BAZA::set_val; void tipareste_valoare(void) { cout<<"Element DERIVAT_2 cu VALOARE = "<<valoare<<"\n"; } }; class LISTA_ETEROGENA { BAZA *pB; public: void set(BAZA *p) {pB = p;}

44

void tipareste_valoare(void) { pB->tipareste_valoare(); } }; void main() { BAZA a[2]; DERIVAT_1 b1; DERIVAT_2 b2; LISTA_ETEROGENA p[4]; a[0].set_val(1); a[1].set_val(2); b1.set_val(3); b2.set_val(4); p[0].set(&a[0]); p[1].set(&b1); p[2].set((BAZA *) &b2); p[3].set(&a[1]); for(int i+0; i<4; i++) p[1].tipareste_valoare(); } n acest caz clasa LISTA_ETEROGENA are o metod tipareste_valoarea mult simplificat. n noua funcie membru nu mai este necesar nici testarea tipului de pointer i nici conversia pointerului memorat la tipul original. Acestea se realizeaz prin prefixarea metodei BAZA::tipareste_valoarea cu cuvntul cheie virtual. n urma ntlnirii cuvntului virtual compilatorul va lua automat deciziile necesare. Prefixarea unei metode "F" a clasei "B" cu cuvntul cheie virtual are urmtorul efect: toate clasele ce o motenesc pe "B" i nu redefinesc funcia "F" o vor moteni ntocmai. Dac o clas "D" derivat din "B" va redefini metoda "F" atunci compilatorul are sarcina de a asocia un set de informaii suplimentare, cu ajutorul crora se va putea decide (n momentul execuiei) care metod "F" va fi apelat. Observaii: 1. Deoarece funciile virtuale necesit memorarea unor informaii suplimentare, instanierile claselor care au metode virtuale vor ocupa n memorie mai mult loc dect ar fi necesar n cazul n care nu ar exista dect metode ne-virtuale. Nici FUNCIILE NE_MEMBRU i nici METODELE STATICE NU POT FI DECLARATE VIRTUALE.

45

2. Pentru ca mecanismul metodelor virtuale s poata funciona, metoda n cauz NU POATE FI REDECLARAT n cadrul claselor derivate ca avnd aceiai parametrii i returnnd un alt tip de dat. n schimb, este permis redeclararea cu un alt set de argumente, dar cu acelai tip de dat returnat. dar n aceast situaie, noua funcie nu va mai putea fi motenit mai departe ca fiind metod virtual. 3. Evitarea mecanismului de apel uzual pentru funciile virtuale se face foarte uor prin prefixarea numelui funciei cu numele clasei aparintoare urmat de "::" Exemplu: void DERIVAT::f1(void) { BAZA::tipareste_valoare(); //apelul metodei din clasa BAZA tipareste_valoare(); //apelul metodei din clasa DERIVAT } 4. Constructorii nu pot fi declarai virtual, nsa destructorii accept o astfel de prefixare. 5. O funcie virtual "F", redefinit ntr-o clas derivat "D" va fi vzut n noua sa form de ctre toate clasele derivate din "D". Aceast redefinire nu afecteaz cu nimic faptul c ea este virtual, metoda rmnnd n continuare virtuala i pentru eventualele clase derivate din "D". 6. Cnd se pune problema de a decide dac o metod sa fie sau nu virtual, programatorul va lua n considerare urmtoarele aspecte: - n cazul n care conteaz doar performanele unui program i se exclude intenia de a-l mai dezvolta n continuare, se va opta pentru metode nevirtuale. - n cazul programelor ce urmeaz s suporte dezvoltri ulterioare, situaia este cu totul alta. Vor fi declarate virtuale toate acele metode ale clasei "CLASA" care se consider c ar putea fi redefinite n cadrul unor clase derivate din "CLASA" i, n plus, modificarea va trebui s fie sesizabil la "nivelul" lui "CLASA". Toate celelalte metode pot rmne ne_virtuale. Partea practic a laboratorului: Aplicaia 1: Dndu-se clasa de baz Lista, s se construiasc dou clase derivate Stiva i Coada, folosind funciile virtuale store() (adaug un element n stiv sau coad) i retrieve() (terge un element din stiv sau coad). ntrebri: 1. Ce sunt metodele virtuale? 2. Cnd se utilizeaz funciile virtuale? 3. Ce nseamn cuvntul eterogen?

46

4. 5. 6. 7. 8.

Cum se va face diferenierea ntre metodele(redefinite) claselor derivate? Pot fi declarate ca fiind virtuale funciile nemembre? Pot fi declarate ca fiind virtuale metodele statice ale unei clase? Pot fi declarai constructorii ca fiind virtuali? Diverse aspecte privitoare la membri virtuali.

Teme de cas: Aplicaia1 Dndu-se clasa de baza Lista, s se construiasc dou clase derivate ListaNeordonata i ListOrdonata, folosind funciile virtuale pentru adugare a unui element n list i pentru afiarea elementelor unei liste. Aplicaia2 Dndu-se clasa de baza Lista, s se construiasc dou clase derivate ListaSimpl i ListDubl, folosind funciile virtuale pentru adugare a unui element n list i pentru afiarea elementelor unei liste.

47

48

Laborator 8 POO: Motenire multipl


Scopul lucrrii: aprofundarea mecanismelor de motenire, i anume a unui nou concept de motenire: motenirea multipl. Beneficiul: utilizarea motenirii multiple permite reutilizarea resurselor existente(a claselor) pentru a genera noi resurse. De exemplu, dac avem o clasa numit punct care deseneaz un punct i o clas culoare, atunci putem crea o nou clas linie (linia este format din puncte colorate), derivat din cele dou clase, folosindu-ne astfel de codul scris n clasa punct i n clasa culoare. Cu alte cuvinte putem realiza ierarhii de clase foarte complexe. Scurt prezentare: n cadrul acestui laborator se va studia mecanismul motenirii multiple. Prezentarea laboratorului: Exemplu: class B1 { //... } class B2 { //... } class B3 { //... } class D1 : B1 { B2 b1; B3 b2; }; Din punct de vedere al structurii de date, clasa D1 este echivalenta cu forma: class D1 : B1, B2, B3 { //... }; Din punct de vedere funcional exist totui diferene, cum ar fi, de exemplu, modul de apel al metodelor claselor membre. Motenirea multipl elimin aceste neajunsuri. Astfel, o clas de derivat poate avea simultan mai multe clase de baz. Exist totui i unele restricii: 1. O clas derivat D nu poate moteni direct de doua ori aceeai clas B.

49

2. O clas derivat D nu poate moteni simultan o clas B i o alt D1, derivat tot din "B". De asemenea, nu pot fi motenite simultan dou clase D1 i D2 aflate n relaia de motenire B ->... >D1->...->D2. Exemplu: class B { //... }; class C1 : B { // }; class C2 : C1 { // }; class D1 : B, C2 //EROARE { // }; class D2 : C1, C2 //EROARE { // }; n schimb, este corect o motenire de genul : class B { //... }; class D1 : B { // }; class D2 : B { // }; class D : D1, D2 { // }; Pentru a realiza o motenire multipl este suficient s se specifice dup numele clasei derivate o list de clase de baz separate prin virgul (evident, aceste nume pot fi prefixate cu modificatori de acces public sau privat). Ordinea claselor de baz nu este indiferent. Apelul constructorilor respectivi se va face exact n ordinea enumerrii numelor claselor de baz. Ca i n cazul motenirii simple, apelul 50

constructorilor tuturor claselor de baz se va efectua naintea eventualelor iniializri de variabilemembru. Mai mult, aceasta ordine nu poate fi modificat nici chiar de ordinea apelului explicit al constructorilor claselor de baz n cadrul constructorilor clasei derivate. Exemplu: class B1 { char c; public : B1(char c1) : c(c1) {}; }; class B2 { int c; public : B2(int c1) : c(c1) {}; }; class D : B1, B2 { char a, b; public : D(char a1, char b1) : a(a1), b(b1) B2((int) a1), B1(b) { //... } }; void main(void) { D d(3,3); } Indiferent de ordinea iniializrii variabilelor i de cea a constructorilor specificai explicit, n definirea constructorului clasei derivate: D(char a1,char b1) : a(a1),b(b1) B2((int) a1),B1(b1) {}; succesiunea operaiilor va fi urmtoarea : apelul constructorului B1, apoi B2 i, doar la sfrit, iniializarea lui a i b. O situaie cu totul particular o au aa-zisele CLASE VIRTUALE. Pentru a avea o imagine ct mai clar asupra problemei , s analizm exemplul de mai jos : Exemplu : class B { // };

51

class D1 : B { //... }; class D2 : B { // }; class M1 : D1,public D2 { // }; class M2 : virtual D1, virtual public D2 { // }; n urma motenirii claselor D1 i D2, clasa M1 va ngloba dou obiecte de tipul B (cte unul pentru fiecare din cele dou clase de baz). n practic, ar putea exista situaii n care este de dorit motenirea unui singur obiect de tip B. Pentru aceasta se va proceda ca i n cazul clasei M2: se vor prefixa cele dou clase de baz cu cuvntul cheie virtual. n urma acestei decizii, clasa M2 nu va conine dect O SINGURA INSTANTIERE A CLASEI "B". Cui aparine aceast unic instaniere (lui D1 sau D2)? Instanierea n cauz va aparine lui D1, adic primei clase virtuale (din lista claselor de baz) care a fost derivat din B. Definirea claselor virtuale ne oblig s modificm regulile de apelare a constructorilor claselor de baz. n cele ce urmeaz vom prezenta noul set de reguli : R1. Constructorii claselor de baz virtuale vor fi apelai INAINTEA celor corespunznd unor clase de baz ne-virtuale. R2. n caz c o clas posed mai multe clase de baz virtuale, constructorii lor vor fi apelai n ordinea n care clasele au fost declarate n lista claselor de baz. Apoi vor fi apelai (tot n ordinea declarrii) constructorii claselor de baz ne-virtuale, i abia n cele din urm constructorul clasei derivate. R3. Chiar dac n ierarhia claselor de baz exist mai mult de o instaniere a unei clase de baz virtuale, constructorul respectiv va fi apelat doar o singur dat. R4. Daca n schimb, n aceeai ierarhie exist att instanieri virtuale ct i ne-virtuale ale unei aceleiai clase de baz, constructorul va fi apelat: O SINGURA DATA - pentru toate instanierile virtuale; DE "N" ORI - pentru cele ne-virtuale (dac exist "N" instanieri de acest tip).

52

Partea practic a laboratorului: Aplicaia1 Studenii vor dezvolta structura de mai jos: enum Boolean {false, true}; class Location { protected: int X; int Y; public: Location(int InitX, int InitY) {X = InitX; Y = InitY;} int GetX() {return X;} int GetY() {return Y;} }; class Point : public Location { protected: Boolean Visible; public: Point(int InitX, int InitY); virtual void Show(); // Show si Hide sunt virtuale virtual void Hide(); Boolean IsVisible() {return Visible;} void MoveTo(int NewX, int NewY); }; class Circle : public Point { // Derivata din class Point care este // derivata din class Location protected: int Radius; public: Circle(int InitX, int InitY, int InitRadius); void Show(); void Hide(); void Expand(int ExpandBy); void Contract(int ContractBy); }; Aplicaia2 Aplicaia de mai sus va fi extins prin crearea claselor Line i Poligon.

53

ntrebri: 1. 2. 3. 4. 5. 6. 7. 8. 9. Ce este motenirea? Care sunt mecanismele motenirii? Ce este motenirea multipl? O clas poate motenii mai multe clase de baz? Ordinea claselor de baz, motenite de o clas, este importan? Ce sunt constructorii de copiere? Ce sunt clasele virtuale? Cum se apeleaz constructorii claselor derivate? Ordinea de apel a constructorilor claselor derivate este important?

Teme de cas: Aplicatia1 S se dezvolte, similar cu aplicaia prezentat la laborator, o aplicaie pentru clasele: Om, Student, OnCampus i OffCampus. Clasele Om i Student sunt considerate clase de baz. Aplicaia2 Aceleai cerine pentru clasele: Roat, Vehicul, AutoLimuzina i AutoTransport. Clasele Roat i vehicul vor fi considerate clase de baz. 1. Octavian Catrina, Iuliana Cojocaru, "TURBO C++", Editura Teora, Bucureti 1993. 2. Vasile Stoicu-Tivadar, Programare Orientata pe Obiecte, Editura Orizonturi Universitare, Timioara 2000, pp. 147-167, 223-259.

54

Laborator 9 POO: abloane n C++


n acest laborator se va discuta despre abloanele utilizate n C++. Vei utiliza abloanele pentru construirea unei clase tip colecie CStack care poate fi folosit pentru stocarea oricrui tip de obiect. Colecii i containere sunt dou denumiri date obiectelor care se folosesc pentru a stoca alte obiecte.

Ce sunt abloanele?
abloanele sunt folosite cu clase i funcii care accept parametri la folosirea clasei. Un ablon de clas sau de funcie poate fi asimilat unui bon de comand care include cteva spaii albe care se completeaz la expedierea comenzii, n loc de a crea o clas tip list pentru matrice de pointeri i o alt clas de acelai tip pentru char, va putea fi folosit o singur clas ablon drept clas tip list.

De ce s folosim abloanele?
abloanele sunt foarte utile n colecii, deoarece o singur clas tip colecie bine conceput care folosete abloane poate fi imediat refolosit pentru toate tipurile incorporate. n plus, utilizarea abloanelor pentru clasele tip colecie contribuie la eliminarea unuia dintre motivele principale care necesit folosirea conversiilor forate.

Cum se folosesc abloanele?


Sintaxa folosit pentru declararea i utilizarea abloanelor poate prea dificil la nceput. Ca n majoritatea cazurilor, practica este soluia. Sintaxa pentru utilizarea unui ablon este relativ simpl. O colecie de ntregi din clasa CStack este declarata sub forma:
CStack<int> stackOfInt;

Componenta <int> reprezint lista de parametri ai ablonului. Lista de parametri este utilizat pentru a instrui compilatorul cum s creeze acest exemplar al ablonului. Aceasta este cunoscut i sub numele de multiplicare, deoarece urmeaz a fi creat un nou exemplar al ablonului, folosind argumentele date. Nu v facei probleme n legtur cu semnificaia efectiv a parametrilor de ablon folosii pentru CStack; vom discuta despre acetia n continuare. De asemenea se poate folosi cuvntul cheie typedef pentru a simplifica citirea declaraiilor. Dac folosii typedef pentru a crea un tip nou, se poate folosi noul nume n locul unei sintaxe de ablon mai lungi, dup cum se prezint n listingul 1.

55

Listing 1. Utilizarea cuvntului cheie typedef pentru a simplifica declaraiile de ablon.


typedef CStack <int> INTSTACK; INTSTACK stivaDeInt; INTSTACK TotStivaDeInt;

O clas de abloane CStack


Crearea unei clase bazate pe abloane implic cu puin mai mult munc dect crearea unei clase non-ablon. n acest paragraf v vei crea propria clas stiv, bazndu-v pe abloane. O stiv este o colecie de obiecte care permite articolelor s fie eliminate sau adugate dintr-o singur poziie logic, i anume vrful stivei. Articolele sunt introduse (pushed on) sau extrase (popped off) din stiv. Dac ntr-o stiv se afl dou sau mai multe articole, se poate accesa numai ultimul element din stiv, dup cum se vede in Figura 2:

Fig. 2. La crearea unei definiii pentru abloanele dumneavoastr, este un procedeu comun utilizarea nlocuitorului T pentru a reprezenta tipul care va fi specificat ulterior, la multiplicarea ablonului. Un exemplu de ablon tip stiv este prezentat n listingul 3. Listing 3. Clasa de abloane CStack
template <class T> class CStack { public: CStack (); virtual ~CStack (); int IsEmpty () const; T Pop (); void Push (const T& item); private: CStack<T> (const CStack& T) {}; T* m_p; int m_nStored; int m_nDepth; enum {GROW_BY = 5}; }; // Constructori

56

template <class T> CStack<T>::CStack () { m_p = 0; m_nStored = 0; m_nDepth = 0; } template <class T> CStack<T>::~CStack () { delete [] m_p; } // Operaii template <class T> int CStack<T>::IsEmpty () const { return m_nStored == 0; } template <class T> void CStack<T>::Push (const T& item) { if (m_nStored == m_nDepth) { T* p = new T [m_nDepth + GROW_BY]; for (int i = 0; i <m_nDepth; i++) { p [i] = m_p [i]; } m_nDepth += GROW_BY; delete [] m_p; m_p = p; } m_p [m_nStored] = item; m_nStored ++; } template <class T> int CStack<T>::Pop () { m_nStored --; return m_p [m_nStored]; }

Declaraia unui ablon cere compilatorului s utilizeze un tip care va fi precizat mai trziu la multiplicarea efectiv a ablonului. La nceputul declaraiei se folosete urmtoarea sintax:
template <class T> CStack

Aceasta arat compilatorului c un utilizator al clasei CStack va furniza un tip cnd ablonul va fi multiplicat i c acel tip trebuie folosit oriunde este plasat T n ntreaga declaraie de ablon. Funciile membre ale clasei CStack folosesc o declaraie similar. Dac CStack ar fi fost o clas non-ablon, funcia membru IsEmpty ar fi avut urmtorul aspect:
int CStack<T>::IsEmpty () { return m_nStored;

57

Deoarece CStack este o clas ablon, sunt necesare informaiile referitoare la ablon. O alt modalitate de definire a funciei IsEmpty este inserarea informaiilor despre ablon ntr-o linie separat, nainte de restul funciei.
template <class T> int CStack<T>::IsEmpty () const { return m_nStored; }

Listingul 3 prezint un exemplu simplu care folosete clasa CStack. Listing 3. Utilizarea ablonului CStack
# include <afx.h> # include <iostream.h> # include "stack.h" int main (int argc, char* argv[]) { CStack<int> theStack; int i = 0; while (i <5) { cout <<"Pushing a " <<i <<endl; theStack.Push (i ++); } cout <<"Toate articolele inserate" <<i <<endl; while (theStack.IsEmpty () != FALSE) { cout <<"Extrag un " <<theStack.Pop () <<endl; } return 0; }

Utilizarea funciilor ablon


O alt utilizare important a abloanelor o constituie funciile ablon. Dac suntei familiarizat cu limbajul C, probabil ai utilizat funcii macro preprocesor pentru a crea funcii cu suprasolicitare redus (low-overhead functions). Din pcate, funciile macro pentru preprocesor nu sunt chiar funcii, i ca atare nu au nici un fel de modalitate de verificare a parametrilor. Funciile ablon permit utilizarea unei funcii pentru o gam larg de tipuri de parametri. n listingul 4 se prezint o funcie ablon care comut valorile a doi parametri. Listing 4. Exemplu de funcie ablon care i permut parametri.
template <class T>

58

void SchArg (T& foo , T& bar) { T temp; temp = foo; foo = bar; bar = temp; }

Utilizarea unei funcii ablon este foarte simpl. Compilatorul se ocup de toate. Exemplul din listingul 5 afieaz un mesaj nainte i dup apelarea funciei ablonului SchArg. Listing 5. Utilizarea SchArg pentru permutarea coninutului a dou obiecte char*.
# include <afx.h> # include <iostream.h> # include "scharg.h" int main () { char *szMsjUnu ("Salut"); char *szMsjDoi ("La Revedere"); cout <<szMsjUnu << "\t" <<szMsjDoi <<endl; SchArg (szMsjUnu, szMsjDoi); cout <<szMsjUnu << "\t" <<szMsjDoi <<endl; return EXIT_SUCCES; }

Partea practic a laboratorului


Aplicaia 1: S se scrie o aplicaie C++ care conine o clas ablon pentru implementarea unei liste simplu nlnuite. Lista va putea avea noduri de orice tip de data fundamental (char, int, double etc.). Se vor implementa funcii pentru adugarea, tergerea i cutarea unui nod din list, precum i o funcie pentru afiarea listei. Aplicaia 2: Aceeai aplicaie pentru o list dublu nlnuit.

59

60

Bibliografie:
1. Octavian Catrina, Iuliana Cojocaru, "TURBO C++", Editura Teora, Bucuresti, 1993. 2. Vasile Stoicu-Tivadar, Programare Orientata pe Obiecte, Editura Orizonturi Universitare, Timisoara 2000.
3. Erich Gamma, Richard Helm, R. Johnson, J. Vlissides, Design Patterns - Sabloane de proiectare, Editura Teora, Bucureti 2002

61

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