Sunteți pe pagina 1din 78

UNIVERSITATEA "ALEXANDRU IOAN CUZA" IAI Facultatea de Informatic Departamentul de nvmnt la Distan

Dorel LUCANU

Introducere n Programarea Orientat-Obiect i C++

2006-2007

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Cuprins
Cuprins............................................................................................................ 1 I II Introducere ............................................................................................... 2 O scurt introducere n programarea orientat-obiect ............................. 3 II.A Ce este programarea orientat-obiect?.............................................. 3 II.B Obiecte i clase.................................................................................. 4 II.B.1 II.B.2 II.C.1 III IV POO: o prim definiie pentru obiecte i clase .......................... 4 Declararea claselor si obiectelor in C++.................................... 6 POO: a doua definiie pentru obiecte si clase ............................ 9

II.C Tipuri de date abstracte i obiecte..................................................... 9 II.D Utilizare de clase ............................................................................. 12 Programare structurat: de la C la C++.............................................. 15 Clase C++ ........................................................................................... 35

V Motenire ............................................................................................... 49 V.A POO: Relaiile de generalizare i specializare ................................ 49 V.B Derivare in C++ .............................................................................. 51 VI VII VIII A. B. Polimorfism ........................................................................................ 55 Clase parametrizate ............................................................................ 59 Studiu de caz: modelarea nscrierii la discipline i examinrii....... 62 Fiierul my_string.h............................................................................ 73 Fiierul my_string.cpp........................................................................ 74

Bibliografie ................................................................................................... 72

D. Lucanu

Programarea Calculatoarelor II

POO i C++

I Introducere
Acest manual este dedicat n special studenilor de la formele de nvmnt ID (nvmnt la Distan) i PU (Post universitare) de la Facultatea de Informatic a Universitii Alexandru Ioan Cuza din Iai. Cartea se dorete a fi un suport pentru disciplina Programarea Calculatoarelor II ce are ca obiectiv predarea programrii orientate-obiect (POO) utiliznd limbajul C++. Cartea nu face o prezentare exhaustiva nici a programrii orientateobiect i nici a limbajului C++. Sunt prezentate doar principiile de baz ale POO i modul de descriere a acestora n C++. Se presupune c cititorul manualului cunoate limbajul de programare C i de aceea cartea include numai acele elemente specifice de programare structurat pe care C++ le include. Se recomand ca orice cursant al acestei discipline s aib pentru consultare suplimentar un manual care prezint complet limbajul C++. Manualul este nsoit de o dischet ce incude fiierele cu exemplele utilizate. Citirea manualului trebuie s se fac ntr-o manier interactiv n sensul c exemplele prezentate s fie testate pe una din implementri (Visual C sau g++). Sunt incluse foarte multe exerciii din care o mare parte se refer la exemplele din fiierele de pe dischet. Structura manualului este urmtoarea: Capitolul II face o mic introducere n POO. Se pune accent pe definiia i utilizarea claselor i obiectelor. Se face un prim pas n declararea claselor i obiectelor n C++. Elementele de programare structurat (procedural) specifice limbajului C++ sunt prezentate n Capitolul III. Cel de-al patrulea capitol face o prezentare detaliat a reprezentrii claselor n C++. Mecansimul de motenire n POO i implementarea cu relaia de derivare din C++ este prezentat n capitolul V. n Capitolul VI sunt prezentate principalele forme de polimorfism: parametric, suprascriere i legare dinamic (funcii virtuale). Clasele parametrizate sunt prezentate pe scurt n capitolul VII. Ultimul capitol include prezentarea unui studiu de caz ce modeleaz parial activitile dintro facultate.

D. Lucanu

Programarea Calculatoarelor II

POO i C++

II O scurt introducere n programarea orientat-obiect II.A Ce este programarea orientat-obiect?


Paradigma orientat-obiect (corect din punct de vedere a limbii romne ar fi orientat spre obiecte) se refer la o tehnologie bazat pe obiecte i clase. Paradigma ofer cel mai potrivit cadru de lucru pentru ingineria software. Obiectele au devenit elemente centrale att n analiza, ct i proiectarea i implementarea sistemelor software. Dei nu exist un punct de vedere unanim asupra cerinelor ce trebuie satisfcute de o abordare orientatobiect, urmtoarele patru sunt ntlnite n majoritatea abordrilor: identitatea, clasificarea, polimorfismul i motenirea. Identitatea nseamn c datele sunt cuantificate n enititi distincte i discrete, numite obiecte. Clasificarea nseamn c obiectele cu aceeai structur a datelor (atribute) i aceeai comportare (operaii) sunt grupate n clase. Polimorfismul se refer la faptul c aceleai operaii (cu aceleai nume i cu aceleai tipuri de argumente) pot avea comportri diferite n clase diferite. Motenirea se refer la partajarea unor atribute i operaii de-a lungul mai multor clase pe baza unei relaii ierarhice. Aceste cerine sunt realizate cu ajutorul urmtoarelor ingrediente orientate-obiect: abstractizare, ncapsulare (ascunderea informaiilor), combinarea datelor cu comportarea, generalizare, specializare, legare dinamic. Abstractizarea const n focalizarea ateniei asupra aspectelor eseniale ale obiectului i ignorarea proprietilor accidentale. ncapsularea sau ascunderea informaiilor se refer la separarea aspectelor externe ale unui obiect, care pot fi accesibile altor obiecte, de detaliile de implementare intern care nu trebuie s fie accesibile altor obiecte. Suma elementelor membre vizibile prin care un obiect interacioneaz cu celelalte obiecte se numete interfa. n abordrile orientate-obiect, datele i operaiile asupra acestora sunt combinate ntr-o singur entitate. n abordarea procedural, datele, precum i fiecare operaie asupra acestora, constituie entiti distincte. Generalizarea este operaia prin care aspectele comune a mai multor clase mai specializate, sunt grupate ntro singur clas mai general. Specializarea este dual generalizrii, n sensul c o clas general poate fi specializat n mai multe clase cu trsturi specifice. Clasele generalizate sunt legate de cele specializate prin 3

D. Lucanu

Programarea Calculatoarelor II

POO i C++

intermediul relaiei de motenire. Legarea dinamic este n strns legatur cu polimorfismul i se refer la mecanismul prin care se determin comportarea corect a unei operaii polimorfice. Un limbaj de programare orientat-obiect include capabilitai de construire a programelor utiliznd paradigma orientat-obiect. Limbajele de programare orientat-obiect modeleaz clasele cu ajutorul tipurilor, iar obiectele prin intermediul variabilelor. De asemenea, includ mecanisme proprii prin care implementeaz polimorfismul i motenirea. Primul limbaj orientat-obiect care a permis lucrul cu clase, obiecte, clase de motenire i operaii polimorfice a fost Simula (1967). n prezent C++ i Java sunt cele mai populare limbaje cu capabilti de programare orientat-obiect. Java a fost creat ca o simplificare a limbajului C++ care s permit executarea pe orice tip de calculator, urmnd principiul scrie o dat, execut oriunde. C++ este un limbaj mixt care include att elemente de programare structurat imperativ ct i elemente de programare orientat-obiect. C++ a fost scris de Bjarne Sroustrup la Laboratoarele Bell n perioada 1983-1985. C++ este o extensie orientat-obiect a limbajului C. El combin trsturile orientate-obiect din Simula cu puterea i eficiena lui C. De la crearea sa i pn n prezent, C++ a cunoscut o dezvoltare continu. Dintre cele mai semnificative, menionm ARMC++ care a adugat excepiile i abloanele i ISO C++ care a adugat RTTI (Run Time Type Identification), spaii de nume i biblioteca standard (STL).

II.B Obiecte i clase


II.B.1 POO: o prim definiie pentru obiecte i clase
Un obiect este caracterizat de: Un nume prin intermediul cruia obiectul poate fi referit. O mulime de stri: la un moment dat un obiect se poate afla ntr-o singur stare. ntr-un program, starea obiectului este dat de o colecie de variabile de tip dat (atribute). Acceptm principiul conform cruia un obiect nu poate avea acces direct la starea altui obiect. Aceast 4

D. Lucanu

Programarea Calculatoarelor II

POO i C++

proprietate a unui obiect de a-i ascunde anumite informaii se numete ncapsulare sau ascunderea informaiei. O colecie de operaii, numite i servicii, cu ajutorul crora un obiect interacioneaz cu alte obiecte i i poate schimba starea. O operaie care schimb starea unui obiect se mai numete i metod. Exist dou metode ce ofer servicii speciale: metoda constructor, care se execut la crearea obiectului (definete starea iniial a acestuia), i metoda destructor, care se execut la dispariia obiectului. Un obiect poate avea cele puin o metod constructor i cel mult o metod destructor. Colecia de operaii descrie comportarea obiectului. Prin clas nelegem descrierea unuia sau a mai multor obiecte ce pot fi precizate printr-un set uniform de atribute i operaii. (a) Exemplul GNPA Ne propunem s definim un generator de numere pseudo-aleatoare (GNPA) ca un obiect. Vom utliza un generator congruenial liniar, n care numerele pseudo-aleatoare sunt generate cu formula de recuren x n +1 = ax n + b(mod m) , valoarea iniial x0 fiind dat. Starea unui astfel de GNPA este dat de patru variabile: m, a, b, x, unde m, a, b au semnificaiile din formul iar x va memora ultimul numar pseudo-aleator generat. Operaia principal a unui GNPA este cea care returneaz urmtorul numar pseudo-aleator generat. Notm aceast operaie cu nrAleat(). Observm c secvena de start este puternic dependent de valoarea de start x0 . De aceea vom considera nc o operaie reInit() care s stabileasc o nou valoare de start. Aceste elemente descriu n mod uniform toate generatoarele congrueniale liniare despre care discutm si deci pot forma o clas, pe care o notm Gnpa. Convenim s utilizm notaia UML pentru reprezentarea grafic a claselor. Clasa Gnpa este reprezentat n Figura 1.

D. Lucanu

Programarea Calculatoarelor II
Gnpa m a b x nrAleat() reInit()
Figura 1

POO i C++

II.B.2

Declararea claselor si obiectelor in C++

n C++ descrierea unei clase are dou pri: declararea clasei i definiiile funciilor care implementeaz metodele clasei. Includem declararea clasei ntr-un fiier antet (contor.h) iar definiiile (implementrile) metodelor ntrun fiier .cpp (contor.cpp). Aceast regul nu este obligatorie dar este recomandat. Cele dou pri ale unei clase pot fi incluse i n acelai fiier. Declararea clasei Gnpa este:
typedef unsigned long ulong; class Gnpa { public: Gnpa(); Gnpa(ulong, ulong, ulong, ulong); ulong nrAleat(); void reInit(ulong); private: ulong m, a, b, x; };

Numele ulong este un alias pentru tipul unsigned long. Observm mai nti c datele i operaiile care descriu obiectele GNPA au fost incluse n aceeai unitate sintactic desemnat de cuvntul cheie class. Remarcm apoi cele dou seciuni ale clasei: public i private. n seciunea public se declar acele elemente membre care pot fi accesate de ctre alte obiecte. Este firesc ca cele dou operaii nrAleat() i reInit() s fie declarate publice deoarece ele descriu serviciile pe care un GNPA le ofer celorlalte obiecte. Tot n seciunea public sunt declarate i metodele constructor i 6

D. Lucanu

Programarea Calculatoarelor II

POO i C++

destructor. n C++ constructorii au aceleai nume cu numele clasei. Clasa Gnpa are doi constructori: primul fra parametri ce va atribui valori implicite atributelor, iar cel de-al doilea avnd ca parametri valorile iniiale ale atributelor. Constructorul fr parametri se numete constructor implicit. Prezena constructorului implicit este obligatorie. Dac nu se declar nici un constructor, atunci sistemul va construi automat unul implicit. n seciunea private se declar variabile ce descriu starea obiectului i funcii care ajut la implementarea interfeei i nu trebuie s fie accesibile celorlalte obiecte. Un GNPA trebuie s fie singurul care are controlul asupra variabilelor m, a, b i x; orice alt obiect nu trebuie s aib acces la aceste variabile. Definiiile operaiilor ce apar n declararaia unei clase sunt date separat:
Gnpa::Gnpa() { m = (1UL << 31) - 1; a = 630360016; b = 0; x = 1; } Gnpa::Gnpa (ulong mPar, ulong aPar, ulong bPar, ulong xPar) { m = mPar; a = aPar; b = bPar; x = xPar; } ulong Gnpa::nrAleat() { x = (a * x + b) % m; return x; } void Gnpa::reInit(ulong x0) { x = x0; }

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Secvena :: se numete operator de rezoluie. El este utilizat pentru a preciza clasa (spaiul de nume) la care aparine un nume. Astfel, construcia Gnpa::nrAleat() refer funcia nrAleat() din clasa Gnpa. Pot exista i alte clase care s includ operaii cu numele nrAleat(). Constructorul implicit Gnpa() atribuie pentru atributele valorile ce corespund generatorului congruenial liniar SIMSCRIPT. Valoarea iniial a lui m este numrul prim Merssene 2 31 1 iar cea a lui a este 14 29 (mod 2 31 1) . Aceste valori au un caracter aleator bun pentru secvena de numere generate. Constructorul Gnpa(mPar,aPar,bPar,xPar) atribuie valori iniiale explicite pentru atributele generatorului. Faptul c Gnpa are doi constructori, are ca efect existena mai multor posibiliti de creare a obiectelor. O declaraie de forma:
Gnpa gnpa;

creeaz un obiect apelnd constructorul implicit. Un apel de forma:


Gnpa gnpa(1UL << 31, 65539, 0, 1);

creeaz un obiect apelnd constructorul explicit. Valorile corespund generatorului RANDU care, dei este foarte cunoscut, nu are proprieti aleatoare foarte bune. Operaiile publice ale unui obiect sunt referite asemntor cmpurilor unei structuri. Aici este o secven de program care afieaz primele 20 numere pseudo-aleatoare generate ctre obiectul gnpa:
for (i = 0; i < 20; ++i) printf("%4d %d\n", i, gnpa.nrAleat()); return 0;
Exerciiul 1

nlocuii n definiia clasei Gnpa cuvntul cheie class cu struct. Testai programul. Ce se ntmpl? Dei C++ permite utilizarea ambelor construcii pentru definiia claselor, se recomand utilizarea construciei class.
Exerciiul 2

Adaugai n funcia main instruciunea:


gnpa.x++;

i apoi compilai programul. Ce eroare ai obinut? 8

D. Lucanu

Programarea Calculatoarelor II

POO i C++

II.C Tipuri de date abstracte i obiecte


II.C.1 POO: a doua definiie pentru obiecte si clase
O clas este o implementare a unui tip de date abstract. Ea definete atribute i metode care implementeaz structura de date i, respectiv, operaiile tipului de date abstract. Un obiect este o instan a unei clase. El este unic determinat de numele su i definete o stare care este reprezentat de valorile atributelor sale la un anumit moment particular. n consecin, o clas definete proprietile i comportarea unei mulimi de obiecte. Exist i posibilitatea ca mulimea de obiecte s fie vid, caz n care clasa se numete clas abstract. O clas care are obiecte se numete clas concret. (a) Exemplul Stiva Reaminitim c o stiv (list LIFO) este o list peste care se pot executa urmtoarele operaii: push() inserarea unui element, pop() eliminarea ultimului element introdus n stiv, top() ntoarce ultimul element introdus n stiv, esteVida() testeaz daca stiva este vid. Aici prezentm clasa StivaChar care implementeaz cu ajutorul tablourilor stivele ce memoreaz caractere. Reprezentarea grafic a clasei este dat n Figura 2.
StivaChar tab varf push() pop() top() esteVida()
Figura 2

Descrierea C++ a clasei StivaChar este:


class StivaChar { public: Stiva(); ~Stiva(); void push(char); void pop();

D. Lucanu

Programarea Calculatoarelor II

POO i C++

char top(); bool esteVida(); private: char tab[MAX_STIVA]; int virf; };

Prezentm ca exemplu definiia operaiei push():


void Stiva::push(char c) { if (virf == MAX_STIVA-1) throw "Depasire superioara."; tab[++virf] = c; }

Remarcm ca element de noutate utilizarea instruciunii throw. Aceasta este o parte component a mecanismului de tratare a excepiilor din C++. Ideea de baz a acestui mecanism este foarte simpl: cu ajutorul instruciunilor if i throw, ori de cte ori apare o excepie, se arunc o structur de date ce descrie excepia. n exemplul de mai sus, ori de cte ori se depete capacitatea de memorare a tabloului (dimensiunea maxim a stivei), se arunc irul de caractere "Depasire superioara.". O secven de program ce utilizeaz cod susceptibil de a produce excepii, utilizeaz instruciunile try i catch pentru prinderea excepiilor. Iat o secven de program foarte simpl care prinde excepiile aruncate de stiv:
Stiva s; char c='a'; try { while (true) { s.push(c++); cout << s.top() << endl; } } catch (char *mes_err) { cout << mes_err << endl; }

10

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Codul care utilizeaz stiva este inclus ntr-un bloc try. Ori de cte ori execuia acestui cod produce o excepie, execuia blocului try se termin i controlul se d urmtoarei instruciuni, care trebuie s fie o instruciune catch. Aceasta va prinde structura de date aruncat de ctre apariia excepiei i, n funcie de tipul excepiei, va executa codul de tratare a acesteia. n exemplul de mai sus, la o prim vedere, pare c instruciunea while nu se termin niciodat. Capacitatea de memorare a stivei este limitat i atunci cnd va aprea prima depire a acesteia, execuia blocului try se termin i controlul execuiei programului este dat instruciunii catch imediat de dup try. Structura de date aruncat de excepie este referit de variabila pointer mes_err, dat ca parametru instruciuni catch. Secvena de tratare a excepiei const aici n afiarea irului aruncat. Despre excepii vom discuta mai pe larg n seciunea III (j).
Exerciiul 3

Modificai clasa StivaChar astfel nct tabloul care memoreaz stiva s fie memorat n memoria dinamic (heap). Pentru alocare dinamic utilizai operatorul new prezentat n subsectiunea Alocare dinamic. Dimensiunea tabloului va fi dat ca parametru al metodei constructor. Astfel, o declaraie de forma
Stiva s(50);

va declara o stiv care poate memora cel mult 50 de elemente. Dac operatorul new nu poate aloca memorie, atunci va arunca o excepie. Eliberarea memoriei ocupate de tablou va reveni destructorului ~Stiva(). Rolul destructorului este descris n seciunea III .
Exerciiul 4

S se descrie urmtoarele clase: 1. StivaDouble care memoreaz numere raionale double; 2. StivaPunct2D care memoreaz puncte n plan. 3. StivaPunct3D care memoreaz pointeri la puncte n spaiu, punctele fiind memorate in memoria dinamic (heap). Scriei programe demo care s testeze noile clase. 11

D. Lucanu

Programarea Calculatoarelor II

POO i C++

II.D Utilizare de clase


n POO apare deseori situaia cnd trebuie s utilizm clase deja existente. De exemplu, este foarte probabil s avem nevoie de o clas care sa fie responsabil cu manipularea irurilor. Cele mai multe implementri ale limbajului C++ pun la dispoziie astfel de clase. Recomandm utilizarea clasei string din STL. n cazul n care nu exist o astfel de clas, atunci se poate utiliza cea ataat acestui curs. Pentru a utiliza clasa string, este suficient s cunoatem doar interfaa (inclus n fiierul my_string.h). Aceast clas va fi utilizat n multe din exemplele din acest curs. Cei care sunt interesai de modul n care sunt implementate operaiile acestei clase, pot consulta fiierul my_string.cpp (anexa B). Interfaa clasei string din fiierul my_string.h (anexa A) este:
class string { public: // Constructori/destructori string(); string(char* new_string); string(const string& new_string); ~string(); // Operatii int size() const; const char* c_str() const; friend string operator+ (const string& lhs, const string& rhs); string& operator+= (const string& a_string); string& operator= (const string& a_string); friend bool operator< (const string& lhs, const string& rhs); friend bool operator> (const string& lhs, const string& rhs); friend bool operator== (const string& lhs, const string& rhs); friend ostream& operator<< (ostream& os, const string& a_string); private: char* char_arr; int string_length; };

12

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Exist trei constructori: constructorul implicit creeaz obiectul ir vid, al doilea constructor creeaz un obiect ir pornind de la un ir C dat ca parametru, iar cel de-al treilea este constructorul de copiere (creeaz un obiect string avnd ca parametru alt obiect string). Operaia size() ntoarce lungimea irului, iar operaia c_str() ntoarce reprezentarea C a irului memorat de obiect. Operatorul + ntoarce un obiect ir obinut prin concatenarea a dou iruri, operatorul = realizeaz operaia de atribuire cu obiecte iruri, operatorii relaionali realizeaz operaiile de comparaie corespunztoare, iar operatorul << scrie irul memorat de obiect ntr-un flux de ieire (a se vedea seciunile III (c)(d)). Urmtoarea secven de cod include cteva exemple de utilizare a clasei string:
#include "my_string.h" ... string s1("123"); // creare sir dintr-un sir C string s2; // creare sir vid s2 = s1; // operatorul de atribuire s1 = s1 + s2; // operatorul de concatenare cout << s1 << endl; // afisarea unui sir string *psa = new string[5]; // creare tablou de obiecte sir psa[0] = "abc"; // apelare constructor si atribuire

Pentru a utiliza clasa string din STL este suficient s nlocuim includerea fiierului my_string.h cu declaraiile:
#include <string> using namespace std;

Aici putem observa unul din marele avantaje aduse de POO: putem schimba o implementare a unui tip de dat cu alt implementare fr s modificm programul. Singura condiie care trebuie ndeplinit este ca interfaa s fie aceeai. Clasa string poate fi utilizat i n definiia altei clase. Clasa Cont declarat mai jos descrie un cont bancar. Titularul contului, numrul de cont i data ultimei depuneri sunt reprezentate ca obiecte string. Remarcm c 13

D. Lucanu

Programarea Calculatoarelor II

POO i C++

operaia actualizeazaSold() este declarat privat, ea nefiind accesibil celorlalte obiecte.


class Cont { public: Cont(); ~Cont(); void depune(double); void extrage(double); void setDob(double); private: string titular; string nrCont; string dataUltOp; double sold; double rataDob; void actualizeazaSold(); }
Exerciiul 5

S se scrie un program care s utilizeze clasa string pentru a afia un numr long n litere. De exemplu, pentru intrarea 7123 va afia "spatemiiosutadouazecisitrei".

14

D. Lucanu

Programarea Calculatoarelor II

POO i C++

III Programare structurat: de la C la C++


n acest capitol descriem pe scurt o parte din construciile particulare ale limbajului C++ utilizate n scrierea programelor structurate. (a) Structuri Structurile n C++ pot include i operaii (a se vedea i Exerciiul 1):
struct Data { int zi, ln, an; void init(int o_zi, int o_luna, int un_an); void aduna_an(int n); void aduna_luna(int n); void aduna_zi(int n); void afiseaza(); };

Astfel, o structur este echivalent cu o clas n care toi membrii sunt publici:
class Data { public: int zi, ln, an; void init(int o_zi, int o_luna, int un_an); void aduna_an(int n); void aduna_luna(int n); void aduna_zi(int n); void afiseaza(); };

Funciile membre sunt definite la fel ca la clase:


void Data::init(int o_zi, int o_luna, int un_an) { zi = o_zi; ln = o_luna; an = un_an; }

Variabilele de tip structur sunt utilizate la fel ca obiectele:


Data azi; // echivalent cu struct Data azi; azi.init(4, 3, 2003);

15

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Construciile sintactice struct i class sunt echivalente n sensul c orice tip declarat cu struct poate fi descris cu class i reciproc. Recomandm utilizarea construciei class ori de cte ori se modeleaz o clas n sensul programrii orientate-obiect i a construciei struct atunci cnd se modeleaz o structur de date obinuit. (b) Conversia de tip (type casting) Sintaxa pentru conversia de tip este diferit. Reamintim mai nti modul n care se scrie explicit o conversie de tip n C:
int n; float x; x = (float) n;

Aceeai conversie n C++ este descris astfel:


int n; float x; x = float(n);

Notaia funcional utilizat de C++ este mult mai sugestiv i, n plus, este n plin concordan cu utilizarea constructorilor:
class Complex { public: ... Complex(double); ... private: double re, im; } Complex z; double z = 2.7; z = Complex(y);

Clasa Complex de mai sus este responsabil cu manipularea numerelor complexe. Pentru a modela incluziunea mulimii numerelor reale n mulimea numerelor complexe, este suficient s adugm la definiia clasei un constructor cu un parametru double. Acest constructor va fi apelat ori de cte ori se va dori o conversie din double n Complex. 16

D. Lucanu
Exerciiul 6

Programarea Calculatoarelor II

POO i C++

S se completeze o definiie minimal a clasei Complex. S se adauge un constructor care s converteasc int-uri n obiecte Complex. (c) Fluxurile de intrare/ieire standard Ca i limbajul C, C++ nu include n definiia sa capabiliti de tratare a fiierelor. n schimb, orice implementare a limbajului vine mpreun cu un pachet de clase, numit iostream, responsabile cu manipularea fluxurilor de intrare/ieire. Un flux este un tip de date care descrie la nivel abstract un fiier detept. Ierarhia de clase iostream va fi descris pe larg ntr-o seciune ulterioar. Aici vom discuta cele dou obiecte care modeleaz tastatura ca flux de intrare (istream) i monitorul ca flux de ieire (ostream). Obiectul istream responsabil cu introducerea datelor de la tastatur se numete cin i rolul lui este asemntor fiierului standard stdin din C. Citirea (a se citi extragerea) datelor din fluxul cin se realizeaz de catre operatorul >>:
int a; double x; cin >> a; cin >> x;

Operatorul >> este un operator binar: partea stng este un flux de intare (aici cin) i partea dreapt este numele variabilei (referina locaiei) care va memora data extras din flux. Tipul variabilei din dreapta operatorului va determina n mod unic lungimea informaiei extrase precum i algoritmul de conversie din format extern n format intern. Extragerea informaiilor se face urmnd principiul FIFO (ca la tipul coad). Ca i n C, evaluarea unei expresii produce o valoare. Evaluarea operatorului >> are ca rezultat fluxul obinut dup extragere. Aceasta permite scrierea de expresii ce includ mai multe apariii ale operatorului >>. Cele dou instruciuni-expresie de mai sus pot fi unite n una singur:
cin >> a >> x;

Deoarece clasa string are definit operatorul >>, caractere se realizeaz la fel de simplu:
string s; cin >> s;

citirea unui ir de

17

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Responsabil cu alocarea de memorie este definiia operatorului >> din clasa string. Operatorul >> poate fi utilizat i pentru citirea irurilor n format C:
char s[100]; cin >> s;

Obiectul ostream, responsabil cu afiarea datelor pe monitor, este cout i rolul lui este asemntor cu cel al fiierului standard stdout din C. Afiarea (a se citi introducerea) datelor n fluxul cout este realizat de ctre operatorul <<:
cout << a; cout << x; cout << s;

Ca i >>, operatorul << este binar: partea stng este un flux de ieire i partea dreapt este o expresie. Rezultatul evalurii expresiei este convertit n format extern i adugat la flux urmnd tot principiul FIFO. Algoritmul de conversie este determinat de ctre tipul expresiei. Evaluarea operatorului << are ca rezultat fluxul obinut dup introducerea datei. De aceea, instruciunile de mai sus pot fi combinate n una singur:
cout << a << x << s;

Introducerea unui marcator de sfrit de linie se face utiliznd macroul endl:


cout << O linie. << endl << Alta linie. << endl;
Exerciiul 7

n fiierul ex/c2cpp/c2cpp1.cpp este inclus un exemplu de program care citete i afieaz un numr i un ir. 1. S se execute programul pentru intrarea Numar>100 i Sir>Doua cuvinte.. S se explice de ce irul este afiat doar parial. 2. S se completeze programul cu citirea i afiarea i altor tipuri de date predefinite.

18

D. Lucanu
Exerciiul 8

Programarea Calculatoarelor II

POO i C++

S se scrie un program care citete un ir de numere i afieaz suma acestora. Se vor considera trei cazuri: 1. numerele sunt ntregi; 2. numerele sunt raionale; 3. numerele sunt complexe. (d) Fiierele ca fluxuri Scrierea i citirea datelor din fiiere este modelat n C++ tot cu ajutorul fluxurilor. Pregtirea unui fiier din care urmeaz s se citeasc date este realizat printr-o secven de program de forma:
ifstream fintrare(intrare.dat); if (!fintrare) { cout << Eroare citire date din intrare.dat << endl; exit(1); } // citire date

sau de forma (echivalent):


fstream fintrare; fintrare.open(intrare.dat, ios::in); if(!fintrare) { cout << Eroare citire date din intrare.dat << endl; exit(1); } // citire date fintrare.close()

n a doua form se observ clar c fintrare este un obiect al clasei ifstream i c open() i close() sunt dou metode ale acestei clase. Recomandm citirea manualului de C++ pentru a vedea i celelalte metode ale clasei. Citirea datelor din fiier se poate face tot cu operatorul >>. Dm ca exemplu citirea unei matrici:
fintrare >> m >> n; for (i = 0; i < m; ++i) for (j = 0; j < n; ++j) fintrare >> a[i,j];

19

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Menionm faptul c operatorul >> ignor spaiile albe. Pentru o citire mai fidel din fiier se recomand utilizarea metodei fintrare.get(). Pregtirea unui fiier n care urmeaz s se scrie date este realizat printr-o secven de program de forma:
ofstream fiesire(iesire.dat); if (!fiesire) { cout << Eroare scriere date in iesire.dat << endl; exit(1); } // scriere date

sau de forma (echivalent):


fstream fiesire; fiesire.open(intrare.dat, ios::out); if (!fiesire) { cout << Eroare scriere date in iesire.dat << endl; exit(1); } // scriere date fiesire.close()

Scrierea datelor n fiier se poate face i cu operatorul <<. Dm ca exemplu scrierea unei matrici:
fiesire Matricea a[ << m << ][ << n << ] este: << endl; for (i = 0; i < m; ++i) { for (j = 0; j < n; ++j) fiesire << a[i,j] << ; fiesire << endl; }

Despre utilizarea fluxurilor pentru manipularea fiierelor n C++ vom discuta mai detaliat ntr-o seciune ulterioar.
Exerciiul 9

n fiierul ex/c2cpp/c2cpp10.cpp este inclus un exemplu de program care copie un fiier n alt fiier. Sunt prezentate dou variante. 20

D. Lucanu

Programarea Calculatoarelor II

POO i C++

1. S se execute programul i s se consulte apoi cele dou copii create de program. S se explice diferenele dintre ele. 2. Citii cu atenie programul i nelegei fiecare instruciune ce se refer la lucrul cu fiiere.
Exerciiul 10

S se scrie un program care citete un text dintr-un fiier de intrare i scrie ntr-un fiier de ieire textul citit dar n care toate vocalele sunt dublate. (e) Declaraii de variabile n limbajul C declaraiile i instruciunile sunt separate n cadrul unui bloc/funcii: n orice bloc/funcie declaraiile trebuie s precead instruciunile. n limbajul C++ aceast regul nu mai este valabil: declaraiile i instruciunile pot fi intercalate cu condiia ca o variabila s fie declarat nainte de a fi utilizat ntr-o instruciune. Astfel, n C++ este permis s scriem o secven de forma:
double a; a = 10; cout << a << endl; double b = 2.0l; a = 3.14 * b; cout << a << endl;

Aceast libertate de scriere a declaraiilor i instruciunilor poate duce la scderea lizibilitii programului. De aceea se recomand gruparea declaraiilor ntr-un singur loc astfel nct s poat fi gsite uor atunci cnd este nevoie i s nu ncurce citirea logic a programului. Un exemplu de utilizare a declariilor locale este oferit de variabilele de control ntr-o instruciune for:
for (int i = 1; i < 5; i++) s += i;

Problema cu declaraia de mai sus este urmtoarea: este vizibil variabila i n codul de dup for? Rspunsul la aceast ntrebare depinde de implementare (compilator), i de aceea se recomand evitarea utilizrii variabilei de control n exteriorul lui for. 21

D. Lucanu

Programarea Calculatoarelor II

POO i C++

(f) Referine O alt capabilitate pe care o are n plus C++ este posibilitatea de a lucra cu referine. O referin este un nume asociat unei locaii de memorie. Referinele n C++ trebuie deosebite de pointeri, care sunt variabile cu propriile lor locaii in care sunt memorate adrese. O declaraie de referin are urmtoarea sintax: tip& nume = expresie; Printr-o astfel de declaraie se d un nou nume ("alias") unei locaii de memorie deja cunoscute: expresie trebuie s produc prin evaluare o l-valoare (adresa unei locaii de memorie). Urmtoarea secven de cod declar o singur variabil cu dou nume: alb i white:
int alb = 0; int& white = alb;

n continuare aceast variabil poate fi referit cu oricare din cele dou

alb int white Figura 3

nume. Reprezentarea grafic a variabilei este dat n Figura 3. O referin trebuie s fie totdeauna iniializat cu o expresie ce indic obiectul refereniat. (g) Apel prin referin Exist dou mecanisme prin care se leag parametrii actuali ai unui apel de funcie la parametrii formali: apel prin valoare i apel prin referin. Apelul prin valoare se realizeaz n modul urmtor. Presupunem existena unei funcii f(T x) cu parametrul formal x de tip T transmis prin valoare. La apelul f(exp) al funciei f cu parametrul actual exp, care este o expresie de tip T, se ntmpl urmtoarele: 22

D. Lucanu

Programarea Calculatoarelor II

POO i C++

se creeaz o locaie temporar n care se memoreaz rezultatul evalurii expresiei; numele x "se leag" la aceast locaie; se execut instruciunile funciei f. Ori de cte ori x este utilizat ca o l-valoare n corpul funciei, el va referi locaia temporar. Apelul prin valoare este recomandat atunci cnd nu se dorete modificarea valorilor parametrilor de ctre funcie. Apelul prin referin difer de apelul prin valoare prin faptul c permite modificarea valorilor parametrilor. Presupunem existena funciei f(T& x) cu parametrul formal x de tip T transmis prin referin (indicat de existena simbolului &). La apelul f(exp) al funciei f cu parametrul actual exp, care este o expresie a crei valoare definete adresa unei locaii de memorie de tip T (este o l-valoare), se ntmpl urmtoarele: numele x "se leag" la locaia referit de exp; se execut instruciunile functiei f. Ori de cte ori x este utilizat ca o l-valoare n corpul funciei, el va referi locaia dat de expresie. n consecin, orice modificare asupra lui x n interiorul lui f va afecta direct locaia referit de exp. Exemplul cel mai concludent de utilizare a apelului prin referin este oferit de funcia care interschimb valorile a dou variabile:
void swap (int& x, int& y) { int aux = x; x = y; y = aux; }
Exerciiul 11

S se scrie o funcie care ntoarce att cmmdc ct i cmmc a dou numere ntregi.

23

D. Lucanu

Programarea Calculatoarelor II

POO i C++

(h) Funcii care ntorc referine Valoarea ntoars de o funcie poate fi de tip referin. n acest caz, deoarece rezultatul apelului desemneaz o locaie (o l-valoare), poate fi plasat n partea stng a unei atribuiri. n exemplul de mai jos, funcia max() are ca parametri dou variabile transmise prin referin i ntoarce adresa variabilei ce memoreaz valoarea maxim:
float& var_max(float& x, float& y) { if (x > y) return x; else return y; }

Prin execuia instruciunii:


var_max(a, b) *= 2.0;

variabila ce memora nainte de execuie o valoare mai mare i dubleaz valoarea.


Exerciiul 12

n fiierul ex/c2cpp/c2cpp4.cpp sunt incluse exemple de utilizare a referinelor. 1. Executai programul i comparai dac ieirea dat de program corespunde cu ceea ce ai intuit despre var_max(). 2. Scriei o funcie max_tab(int a[], int n)care ntoarce referina la componenta ce memoreaz valoarea maxim din tabloul a. (i) Funcii inline Noiunea de funcie inline a fost introdus n C++ pentru a oferi o alternativ la utilizarea excesiv a macro-definiiilor din limbajul C. Compilatorul nlocuiete orice apel al unei funcii inline cu codul acesteia. Utilizarea funciilor inline mrete viteza de execuie a programului prin evitarea operaiilor pe stiv, ce au loc n cazul apelurilor de funcii obinuite, dar pltete drept pre pentru aceasta creterea dimensiunii codului executabil. Declaraia unei funcii inline este format din declaraia unei funcii obinuite precedat de cuvntul cheie inline: 24

D. Lucanu

Programarea Calculatoarelor II

POO i C++

inline double ipot(double a, double b) { return sqrt(a*a + b*b); };

Un apel z = ipot(x, y); va fi nlocuit de instrucinea z = sqrt(x*x + y*y);. Sintaxa pentru definiia claselor permite declararea implicit a unei funcii inline:
class Data { public: Data(int o_zi, int o_luna, int int getZi() { return zi; } int getLuna() { return luna; } int getAn() { return an; } void urm(); void prec(); private: int zi, luna, an; };

un_an); // functie inline // functie inline // functie inline

Declararea implicit de funcii membre inline n interiorul claselor scade gradul de lizibilitate a programului i de aceea se recomand declararea explicit.
Exerciiul 13

Exemplele de mai sus sunt incluse n fiierul ex/c2cpp/c2cpp5.cpp. Modificai programul astfel nct funcia main() s includ mai multe apeluri ale funciilor inline. Compilai i reinei dimensiunea codului obiect. Eliminai cuvntul cheie inline din definiiile funciilor corespunztoare, compilai i notai dimensiunea noului cod obiect. Comparai cele dou dimensiuni i justificai cele observate. (j) Excepii n seciunea II.C am vzut cum mecanismul de tratare a excepiilor din C++ poate fi utilizat pentru implementarea operaiilor unei clase. n aceast seciune ne propunem s prezentm acest mecanism ntr-un context mai larg. 25

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Autorul unei biblioteci de programe poate detecta apariia unei erori dar nu tie n general cum s procedeze cu ele. Pe de alt parte, utilizatorul bibliotecii tie ce trebuie s fac la apariia unei erori dar nu tie cum s le detecteze. Cele trei instruciuni din limbajul C++, throw, try i catch, sunt proiectate exact pentru a rezolva problemele de mai sus. throw. Ori de cte ori realizatorul bibliotecii detecteaz o eroare (excepie), o "paseaz" utilizatorului cu ajutorul instruciunii throw. try i catch. Utilizatorul bibliotecii utilizeaz catch pentru a "prinde" excepiile "aruncate" de ctre funciile din biblioteca utilizat. Secvena de instruciuni susceptibil de a produce excepii este inclus ntr-un bloc try {...}. Instruciunea catch trebuie s fie imediat dup un bloc try. S presupunem c dorim s scriem o funcie add(a, b) care ntoarce rezultatul corect al unei adunri de ntregi ntotdeauna. tim c tipul int include numere ntregi cuprinse ntre INT_MIN i INT_MAX (definite n limits.h). Atunci cnd se adun dou numere int, pot aprea dou tipuri de excepii: depire inferioar - cele dou numere sunt negative i suma lor este mai mic dect INT_MIN; depire superioar - cele dou numere sunt pozitive i suma lor este mai mare dect INT_MAX. La apariia oricrei dintre cele dou situaii, funcia add() arunc o excepie:
int add(int a, int b) { if ((b > 0) && (a > INT_MAX-b)) throw "Depasire superioara"; if ((b < 0) && (a < INT_MIN-b)) throw "Depasire inferioara"; return a+b; }

Urmtoarea secven aplic funcia add() peste numere generate aleator i arat cum eventualele excepii aruncate de aceast funcie sunt prinse i prelucrate: 26

D. Lucanu

Programarea Calculatoarelor II

POO i C++

try { srand(unsigned(time(NULL))); for (int i = 1; i < INT_MAX; i++) { temp = (i%2+1)*rand(); x = add(x, temp); temp = -((i+1)%2+1)*rand(); y = add(y, temp); } } catch (char *err) { cout << "EROARE: " << err; }

n exemplul de mai sus, tratarea excepiei const doar n afiarea mesajului aruncat de funcie. Putem rescrie funcia add() astfel nct blocul catch s cunoasc valorile care au produs excepia. Declarm mai nti o structur pentru reprezentarea informaiei aruncate de ctre add():
struct Dep { public: Dep(int v1, int v2) {vstg = v1; vdrp = v2;} int vstg; int vdrp; };

Funcia add() se rescrie astfel:


int add(int a, int b) { if ((b > 0) && (a > INT_MAX-b)) throw Dep(a, b); if ((b < 0) && (a < INT_MIN-b)) throw Dep(a, b); return a+b; }

Acum catch poate cunoate tipul excepiei i valorile care au produs aceast excepie:
try { ...

27

D. Lucanu

Programarea Calculatoarelor II

POO i C++

} catch (Dep err) { ... }

Dac n blocul try pot aprea mai multe tipuri de excepii, atunci se va aduga cte o instruciune catch pentru fiecare tip.
Exerciiul 14

S se scrie structuri pentru fiecare tip de excepie care poate s apar la operaiile elementare peste int: scdere, nmulire, mprire, modul. S se scrie funcii care realizeaz aceste operaii ntr-o manier sigur, adic semnalnd apariia oricrei excepii. Modificai programul demo de mai sus pentru a testa toate operaiile peste int descrise.
Exerciiul 15

n fiierele ex/c2cpp/c2cpp6_1.cpp i ex/c2cpp/c2cpp6_2.cpp sunt dou exemple de utilizare a excepiilor cu alt scop dect cel de depistare a erorilor. Citii cu atenie programele i nelegei logica lor. Executai programele i comparai dac ieirea programului corespunde cu ceea ce ai dedus din citirea programului. (k) Parametri implicii n C++ exist posibilitatea de a preciza pentru parametrii unei funcii valori implicite. Astfel, aceeai funcie poate fi apelat cu un numr variabil de parametri; parametrii care nu sunt precizai n apel sunt legai la valorile implicite. Considerm urmtoarea funcie foarte simpl:
int fct(int x=7, int y=9) { return x+y; }

Funcia poate fi apelat normal, preciznd ambii parametri; de exemplu, execuia instruciunii
cout << fct(2, 3);

va afia 6. Dar poate fi apelat i cu un singur parametru:


cout << fct(2);

28

D. Lucanu

Programarea Calculatoarelor II

POO i C++

i acum excuia instruciunii va afia va afisa 11, deoarece parametrul x va fi legat la paramterul actual 2 iar parametrul formal y la valoarea implicit 9; sau fr nici un parametru:
cout << fct();

caz n care execuia instruciunii va afia 16, deoarece ambii parametri formali sunt legai la valorile implicite (x la 7 i y la 9). (l) Suprancrcarea operatorilor n C, operatorul * poate fi utilizat cu diferite nelesuri: nmulire de int-uri, nmulire de long-uri, nmulire de float-uri, indirectare etc. Spunem despre operatorul * c este suprancarcat. n C++, utilizatorul poate aduga el operatorilor noi nelesuri. De exemplu, am putea aduga operatorului * nelesul de nmulire dintre un punct din plan i un scalar. Declaraia unui astfel de operator este asemntoare cu cea a unei funcii:
struct punct { int x,y; }; punct operator * (int a, punct p) { punct q; q.x = a * p.x; q.y = a * p.y; return q; }

Singura deosebire fa de funcii este prezena cuvntului cheie operator ntre tipul rezultatului returnat de operator i numele operatorului. Un alt operator care ar fi util pentru puncte ar fi cel care definete o relaie de ordine parial peste puncte:
bool operator <=(punct p, punct q) { return (p.x <= q.x && p.y <= q.y); }

Operatorii sunt binari i sunt utilizai n notaie infixat: 29

D. Lucanu

Programarea Calculatoarelor II

POO i C++

punct a,b; a.x = 1; a.y = 2; b = 5 * a; if (a <= b) cout << mai mic" << endl; else cout << incomparabil" << endl;

Se pot suprancrca i operatori unari. De exemplu putem defini p ca returnnd punctul (-p.x, -p.y):
punct operator - (punct p) { punct temp; temp.x = -p.x; temp.y = -p.y; return q; }
Exerciiul 16

S se suprancarce operatorii +, , * peste puncte: p + q ntoarce punctul (p.x+q.x, p.y+q.y), p - q ntoarce punctul (p.x-q.x, p.y-q.y) etc. S se scrie un program demo care utilizeaz aceti operatori. Un alt operator a crui suprancrcare este util este cel de scriere ntr-un flux de ieire:
ostream& operator << (ostream& o, punct p) { return o << "(" << p.x << ", " << p.y << ") "; };
Exerciiul 17

S se suprancarce operatorul de citire dintr-un flux de intrare >> pentru structura punct. S se scrie un program demo care s testeze utilizarea celor doi operatori. (m) Suprancrcarea funciilor i numele funciilor pot fi suprancrcate. Cel mai frecvent exemplu de funcie suprancrcat este cea care interschimb valorile a dou variabile: interschimbarea a dou int-uri 30

D. Lucanu

Programarea Calculatoarelor II

POO i C++

void swap(int& x, int& y) { int aux = x; x = y; y = aux; }

interschimbarea a doua double-uri


void swap(double& x, double& y) { double aux = x; x = y; y = aux; }

Funcia care va fi apelat n fiecare caz particular n parte este determinat de tipul parametrilor:
int m = 5; n = 8; swap(m, n); // este apelata swap(int&, int&) double a = 5.7l, b = 7.5l; swap(a, b); // este apelata swap(double&, double&)

Un alt exemplu de suprancrcare a numelui de funcii este oferit de constructori. Reamintim c o clas poate avea mai muli constructori; toi au numele clasei.
Exerciiul 18

n fiierul ex/c2cpp/c2cpp9.cpp sunt incluse exemple de suprancrcare a funciei swap(). Adugai definiii ale funcii swap() i pentru alte tipuri. Includei instruciuni care s testeze i noile funcii. (n) Template-uri Exemplul funciei swap() pune n eviden urmtorul dezavantaj: trebuie scris cte o funcie swap() pentru fiecare tip. Definirea unui nou tip presupune definirea unei noi funcii swap(). C++ pune la dispoziie un mecanism prin care tipul sa fie dat ca parametru. Sintaxa este urmtoarea:
template <class T> void swap(T& x, T& y) { T aux = x; x = y; y = aux; };

31

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Cuvntul cheie template semnaleaz faptul c urmeaz o definiie parametrizat. Parametrii definiiei sunt descrii ntre parantezele unghiulare. n definiia de mai sus exist un singur parametru, anume T. Cuvntul cheie class care precede T specific faptul c acest parametru este un tip. Pot exista mai muli parametri pentru o definiie. De asemenea, putem avea parametri int, double, punct etc. Utilizarea funciei ablon swap() presupune precizarea explicit sau implicit a tipului:
int m = 8, n = 15; swap<int>(m, n); // echivalent cu swap(m, n); double a = 10.0, b = 20.0; swap<double>(a, b); // echivalent cu swap(a,b);

La ntlnirea unui apel swap<int> compilatorul va genera o funcie swap_int(int x, int y), la ntlnirea apelului compilatorul va genera o funcie swap_double(double x, double y) etc.
Exerciiul 19

Fiierul ex/c2cpp/c2cpp8.cpp include definiia funciei swap() parametrizate i cteva exemple de utilizare a ei. S se adauge i alte utilizri i apoi s se testeze programul.
Exerciiul 20

Fiierul ex/c2cpp/template.cpp include include dou versiuni ale algoritmului de sortare naiva. Cele dou versiuni sunt exemple de programe generice deoarece gradul de generalitate este foarte mare. Genericitatea s-a obinut prin declararea tipului pentru componente ca parametru. 1. Comparai cele dou versiuni i decidei care este mai bun. Justificai decizia luat. 2. Scriei i ali algoritmi de sortare generici. 3. Scriei o funcie de cutare binar generic. (o) Alocare dinamic C++ i are proprii operatori de alocare i eliberare a memoriei dinamice. Vom vedea c aceti operatori au sarcini speciale atunci cnd creeaz sau distrug obiecte. Alocarea memoriei dinamice se face cu operatorul new: 32

D. Lucanu

Programarea Calculatoarelor II

POO i C++

alocare de variabile simple alocare de tablouri


double *pdbl = new double; double *dblarr = new double[5]; punct *poligon = new Punct[20];

Dac alocarea nu s-a putut efectua, atunci new ntoarce NULL. De aceea se recomand totdeauna testarea valorii ntoarse de new:
if (pdbl == NULL) throw "Memorie insuficienta";

Eliberarea memoriei ocupate de variabilele dinamice simple se face cu operatorul delete:


delete pdbl;

iar eliberarea tablourilor dinamice se face cu operatorul delete []:


delete [] dblarr; delete [] poligon;

Nu este nevoie de precizat numrul de componente ale tabloului; acesta este cunoscut de la creare (sarcina mecanismului de gestionare a memoriei dinamice = heap). Crearea unui obiect dinamic este mai complex dect cea a unei variabile simple deoarece presupune apelarea unui constructor. De exemplu, crearea obiectului string:
string *pstr = new string("Sir dinamic.");

presupune apelarea constructorului string(char *). Acum putem nelege de ce s-a ales ca numele constructorului s fie identic cu cel al clasei. Numele string comunic operatorului new dou lucruri: numele clasei utilizat la determinarea spaiului de memorie alocat i numele constructorului utilizat la iniializarea datelor membre i nu numai. Crearea unui tablou de obiecte dinamice presupune apelarea constructorului implicit pentru fiecare component. Execuia urmtoarei instruciuni:
string *strarr = new string[10];

provoac 10 apeluri ale constructorului string(). Analog, distrugerea unui obiect dinamic presupune apelul destructrului (reamintim ca acesta este unic pentu o clas). Execuia instruciunii: 33

D. Lucanu
delete pstr;

Programarea Calculatoarelor II

POO i C++

provoac apelul destructorului ~string(). Sarcina acestuia este de a elibera spatiul de memorie dinamic ocupat de tabloul de caractere care memoreaz irul. Distrugerea unui tablou de obiecte dinamice presupune apelul destructorului pentru fiecare component.
Exerciiul 21

S se modifice implementarea clasei string din my_string.cpp astfel nct constructorii i destructorii s afieze cte un mesaj specific ori de cte ori sunt apelai. S se scrie un program demo care s scoat n eviden apelurile constructorilor i destructorilor n cazul obiectelor simple i a tablourilor.

34

D. Lucanu

Programarea Calculatoarelor II

POO i C++

IV

Clase C++

(a) Definiie O clas C++ are 4 tipuri de elemente: o colecie de date membre; o colecie de functii membre; nivele de acces ale programului; un nume. Declararea unei clase se face dup una din urmtoarele scheme:
class nume_clasa { declarare membri privati public: declarare membri publici protected: declarare membri protejati private: declarare membri privati }

sau
struct nume_clasa { declarare membri publici public: declarare membri publici protected: declarare membri protejati private: declarare membri privati }

Pot exista mai multe seciuni cu acelai nivel de acces. Nu este obligatorie prezena tuturor nivelelor de acces i nici ordinea n care acestea apar. Totui, se recomand ca pentru o clas s fie cel mult o seciune pentru ficare nivel de acces i ordinea lor s fie cea de mai sus: public, 35

D. Lucanu

Programarea Calculatoarelor II

POO i C++

protected, private. Despre nivelul protected vom discuta n

seciunea dedicat derivrii claselor. Considerm ca exemplu clasa care implementeaz data calendaristic:
class Data { public: Data(int o_zi = zi_curenta(), int o_luna = luna_curenta(), int un_an = an_curent()); ~Data() {} void aduna_zi(int n); void aduna_luna(int n); void aduna_an(int n); Data operator ++(int); // postfix Data& operator ++(); // prefix void set_zi(int zi_noua); void set_luna(int ); void set_an(int an_nou); int get_zi() const; int get_luna() const; int get_an() const; friend ostream& operator << (ostream&, Data&); static Data azi(); private: int zi, luna, an; static int zi_curenta(); static int luna_curenta(); static int an_curent(); bool esteAnBisect(); int nrZileLuna(); int nrZileAn(); };

(b) Datele membre Sunt declarate ca variabile ntr-o manier similar celei de la structuri. Exemple de date membre in Data sunt: zi, luna i an.

36

D. Lucanu

Programarea Calculatoarelor II

POO i C++

(c) Funcii membre Operaiile asupra datelor membre sunt realizate de ctre funcii declarate n corpul clasei, numite funcii membre. Exemple de funcii membre n Data sunt aduna_zi(), aduna_luna(), ..., get_an(). Implementarea acestor funcii (definiiile lor) este inclus n fiierul data.cpp. Se recomand ca declararea clasei s fie inclus ntr-un fiier header iar implementrile sa fie incluse ntr-un fiier .cpp separat. Distingem dou tipuri de funcii membre speciale: constructori i destructori. n definiia clasei Data, funcia membr
Data(int o_zi = zi_curenta(), int o_luna = luna_curenta(), int un_an = an_curent());

este o metod constructor, iar


~Data();

este o metod destructor. Constructorii pot fi mai muli i se deosebesc de celelalte metode prin aceea c vor avea tot timpul numele clasei. Constructorul fr parametri se numete implicit. Poate exista cel mult un destructor i acesta va avea tot timpul numele format din numele clasei precedat de caracterul "~". Constructorii sunt apelai la crearea obiectelor iar destructorii la distrugerea obiectelor. (d) Ascunderea informaiei Ascunderea informaiei este un mecanism formal prin care se restricioneaz accesul utilizatorilor clasei la reprezentarea intern a clasei. Se realizeaz prin precizarea nivelului de acces la membrii clasei. Pentru nceput, distingem dou nivele de acces: public i private. Cel de-al treilea nivel, protected, l vom discuta la seciunea V.B dedicat derivrii V.B . Un membru public este accesibil de oriunde din program. Un membru private poate fi accesat numai de funciile membre sau de prietenii clasei (vor fi definii mai trziu). n clasa Data datele membre sunt private (ascunse). Un utilizator al clasei Data nu va cunoate modul de reprezentare a acestora; el va putea aciona 37

D. Lucanu

Programarea Calculatoarelor II

POO i C++

asupra acestora numai prin intermediul funciilor membre declarate n seciunea public. n afar de datele membre, mai sunt declarate private o serie de funcii (membre) care ajut la implementarea funciilor membre publice sau prietene. Un program care utilizeaz clasa Data nu va putea include instruciuni de forma:
n = azi.nrZileLuna();

(e) Obiectele unei clase Un obiect este o instan a unei clase i este declarat ca o variabil a tipului definit de clas. La creearea unui obiect se aloc spaiu pentru toate datele membre. De exemplu, definiia
Data azi;

aloc spaiu pentru memorarea zilei, lunii i anului. Asupra acestora se poate aciona cu ajutorul metodelor:
azi.set_zi(28); ln = azi.get_luna();

Obiectele pot fi atribuite unul celuilalt. De exemplu,


ieri = azi;

este echivalent cu:


ieri.zi = azi.zi; ieri.luna = azi.luna; ieri.an = azi.an;

(f) O clasificare a funciilor membre Funciile membre au diferite roluri asupra datelor membre. Aceste roluri ar putea fi clasificate n: manageri: se refer la activitatile de iniializare (constructorii), atribuire (operatorii =, += de la string), copiere (constructorul de copiere string(const string& new_string)), conversie, eliberare memorie (destructori). implementori: furnizeaz capabiliti asociate clasei la nivel abstract (sunt incluse n specificarea clasei independent de implementare). 38

D. Lucanu Exemple:

Programarea Calculatoarelor II

POO i C++

Data::aduna_zi(), stiva::push(), stiva::pop().

Data::aduna_luna(),

funcii ajuttoare: sunt utilizate la descrierea implementorilor. Exemple: Data::nrZileLuna(),Data::nrZileAn(). funcii de acces: permit accesul la datele membre. Exemple: Data::set_zi(), Data::get_zi(). (g) Funcii membre const O funcie membr poate schimba valorile datelor membre (starea obiectului) sau nu. Dac o funcie membr nu modific nici o dat membr, atunci se poate preciza acest lucru explicit prin adugarea cuvntului cheie const la prototipul funciei. Prezena acestui cuvnt cheie previne modificrile accidentale ale datelor membre, acestea fiind sesizate de compilator. Exemple: Data::get_zi(), Data::get_luna(), Data::get_an(). (h) Pointerul this Fiecare obiect al unei clase i are propriile sale copii ale datelor membre dar exist o singur copie pentru fiecare funcie membr a clasei. De exemplu, obiectele azi i ieri i au propriile copii ale datelor membre zi, luna i an, dar exist o singur copie pentru funciile membre aduna_zi(), aduna_luna() etc. Se pune urmtoarea ntrebare: cum sunt legate numele datelor din funciile membre la copiile instanelor clasei? Rspunsul este dat de pointerul this. Implementarea funciei membre Data::aduna_zi() poate fi gndit ca fiind:
get_zi_Data (Data *this) { return this->zi; }

Acum, instruciunea azi.get_zi() este echivalent cu get_zi_Data (&azi) iar ieri.get_zi() este echivalent cu get_zi_Data (&ieri). Pointerul this poate fi utilizat n descrierea funciilor membre: 39

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Data& Data::operator ++() { this->zi++; return (*this); }


Exerciiul 22

Implementarea operatorului ++ de mai sus nu este complet pentru c nu ine cont de sfritul de lun i de an. S se scrie o implementare corect a acestui operator. (i) Prietenii unei clase n anumite situaii particulare, ascunderea informaiei este prea restrictiv. Sunt funcii, operatori sau chiar clase care au nevoie de acces la membrii privai ai unei clase date. n C++, o clas i poate declara o list de prieteni. Prietenii unei clase au acces la membrii privai ai acesteia (atenie pe cine i alegi prieten!). Un prieten al clasei poate fi o funcie, un operator sau chiar o alt clas. Un exemplu l constituie operatorul << declarat n clasa Data; pentru a scrie datele membre ntr-un flux de ieire, are nevoie de accesul direct la datele membre (declarate private):
ostream& operator << (ostream &o, Data &d) { o << d.zi << ' ' << d.luna << ' ' << d.an; return o; }

Un alt exemplu clasic l constituie implementarea listelor liniare cu liste nlnuite. Avem dou clase: NodLLin - pentru descrierea unui nod al listei nlnuite, i LLin - pentru descrierea listei liniare. Funciile care implementeaz operaiile peste lista liniar trebuie sa aib acces la informaiile din noduri. O soluie mai puin comod ar fi s declarm fiecare funcie membr a clasei LLin prieten a clasei NodLLin. Dar este mult mai comod s declarm ntreaga clas LLin ca fiind prieten a clasei NodLLin. Mai mult chiar, membrii clasei LLin sunt singurii care au acces la membrii unui nod:

40

D. Lucanu

Programarea Calculatoarelor II

POO i C++

class NodLLin { public: friend class LLin; private: Elt elt; NodLLin *pred, *succ; }; class LLin { public: . . . private: NodLLin *prim, *ultim; . . . };

(j) Membri statici Uneori este necesar ca toate obiectele unei clase particulare s aib acces la aceeai variabil, fr a avea fiecare copia sa. Un astfel de exemplu este o variabil care numr cte instane ale clasei au fost create n timpul execuiei programului. O soluie ar fi s declarm aceast variabil ca fiind global. Dar n acest caz ea nu ar mai fi specific clasei respective. Soluia cea mai potrivit este oferit de datele membre statice. Pentru o dat membr static exist o singur instan corespunztoare clasei, indiferent de numrul obiectelor definite. n exemplul de mai jos:
class A { public: A() { nr_instante++; } ~A() { } int get_nr_inst() { return nr_instante; } private: static int nr_instante; };

clasa A are o singur dat membr static nr_instante care este incrementat de ctre constructor. Valoarea acesteia poate fi accesat prin 41

D. Lucanu

Programarea Calculatoarelor II

POO i C++

itermediul funciei membre A::get_nr_inst(). Iniializarea datei membre statice se face printr-o instruciune de forma:
int A::nr_instante = 0;

Deoarece aceast dat membr este unic pentru ntreaga clas, iniializarea ei nu poate fi n responsabilitatea constructorului, care este apelat la crearea fiecrui obiect A. Instruciunile:
A a; A b; cout << a.get_nr_inst() << endl; cout << b.get_nr_inst() << endl;

vor produce ieirea:


2 2

Deoarece funcia membr A::get_nr_inst() acioneaz numai asupra datelor membre statice, ea nsi poate fi declarat ca funcie membr static:
static int get_nr_inst() {return nr_instante; }

(k) Tipul unui membru al clasei Prin declaraia:


int (*fint)();

se definete un pointer fint la o funcie fr parametri ce ntoarce o valoare int. Dac vom scrie:
fint = Data::get_zi;

vom obine o eroare. De ce? Pentru c un pointer la o funcie membr trebuie s se potriveasc n trei elemente: numrul i tipurile parametrilor, tipul valorii returnate i tipul clasei al crei membru este. Dac declarm:
int (Data::*fint)();

atunci atribuirea de mai sus este legal.

42

D. Lucanu

Programarea Calculatoarelor II

POO i C++

(l) Spaiul de nume si domeniul de vizibilitate al unei clase Fiecare clas i are propriul spaiu de nume. Numele membrilor au domeniul de vizibilitate format din spaiul de nume definit de clas. n exemplul de mai jos:
class Data { public: ... int get_zi() {return zi;} private: int zi, luna, an; }

numele zi a fost utilizat nainte de a-l defini. Acest fapt a fost posibil pentru c domeniul su de vizibilitate este format din ntregul spaiu de nume definit de clasa Data. Dac ntr-o funcie membr se definete o variabil cu acelai nume cu cel al unei date membre, atunci data membr devine ascuns n acea funcie. Ea poate redeveni vizibil dac se adaug la numele ei i clasa (vzut ca spaiul de nume) n care a fost definit:
void Data::aduna_luna(int n) { int luna = Data::luna + n; }

Evident, nu recomandm astfel de soluii care pot duce la confuzii periculoase. Domeniul de vizibilitate al unei clase poate fi: global; intern unei clase - cnd este definit n interiroul unei clase n oricare dintre seciuni (public, private sau protected); local - cnd este definit n interiorul unei funcii membre sau nemembre. n exemplul de mai jos:
class LLin { public:

43

D. Lucanu

Programarea Calculatoarelor II

POO i C++

class Nod { public: void set_elt(Elt un_elt); . . . private: Elt elt; Nod *leg; }; void insereaza(int, Elt); ... private: Nod *prim; ... };

clasa Nod este declarat n interiorul clasei LLin; domeniul ei de vizibilitate este format din clasa LLin. Astfel are sens sa scriem:
void LLin::insereaza(int k, Elt un_e) { Nod *p = new Nod; p->set_elt(un_e); ... };

Dac nlocuim instruciunea


p->set_elt(un_e);

cu
p->elt = un_e;

atunci copilatorul va semnala o eroare: numele p->elt nu este vizibil pentru funcia (). Pentru implementarea metodei Nod::set_elt() trebuie s scriem ceva de forma:
void LLin::Nod::set_elt(Elt un_elt) { ... }

Analog, pentru a declara un obiect Nod n programul principal trebuie s precizm numele extins al clasei (spatiu de nume + nume clas):
LLin::Nod n;

44

D. Lucanu

Programarea Calculatoarelor II

POO i C++

(m) Obiecte membre unei clase Obiectele unei clase pot aprea ca date membre ale altei clase. n clasa Student, descris mai jos, data membr nume este un obiect string:
class Student { public: Student(char*); Student(char*, int); ~Student() {} private: string nume; int virsta; };

Constructorul clasei Student trebuie s includ iniializarea obiectelor membre. Deoarece aceasta presupune la rndul ei apelarea de constructori, iniializarea obiectelor membre este precizat nainte de instruciunile din corpul constructorului:
Student::Student(char *un_nume, int o_virsta) : nume(un_nume), virsta(o_virsta) { //nimic }

Ordinea apelrii constructorilor pentru definiia:


Student(Ionescu,22);

este:
string::string(char*) virsta = 22 Student::Student(char*, int)

Dei virsta este o variabil int (i nu un obiect), iniializarea ei poate fi fcut n aceeai manier cu cea a obiectelor membre. Ordinea apelrii constructorilor pentru obiectele membre este dat de ordinea n care acestea sunt declarate n definiia clasei.

45

D. Lucanu

Programarea Calculatoarelor II

POO i C++

(n) Funcii membre care ntorc referine la date membre private Proprietatea de ascundere a informaiei poate fi violat prin scrierea de funcii membre care ntorc referine la datele membre. Considerm urmtoarea clas foarte simpl:
class A { public: A(int x = 0): elt(x) { } int& get_elt() { return elt; } private: int elt; };

Urmtorul cod este corect i va produce ieirea 6:


A a(5); (a.get_elt())++; cout << a.get_elt() << endl;

Funcia get_elt() ntoarce o referin la locaia de memorie referit de data membr privat elt. Aceast funcie poate fi utilizat pentru a modifica data membr privat. Practic, aceast dat a devenit ntr-un fel public pentru c get _elt() este public. (o) Iniializarea membru cu membru, constructorul de copiere X::X(const X&) Un obiect poate fi iniializat prin apelarea unui constructor, dnd ca argumente (implicite sau explicite) valorile pentru datele membre:
Data azi(4,4,2000);

sau preciznd ca valoare iniial un alt obiect al clasei:


Data data_n = azi;

n acest din urm caz, este apelat automat un constructor X::X(const X&). n cazul clasei Data acesta este generat automat i este echivalent cu:
Data::Data(const Data& d) { zi = d.zi; luna = d.luna; an = d.luna; }

46

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Acum, iniializarea de mai sus este echivalent cu:


Data data_n(azi);

n cazul cnd iniializarea unui obiect presupune alocare dinamic de memorie, atunci ramne n sarcina proiectantului clasei s defineasc explicit constructorul de copiere. De exemplu, pentru a putea scrie ceva de forma:
string s("Programare C++"); string curs = s;

trebuie inclus n clasa string definiia explicit a constructorului de copiere:


string::string(const string& new_string) { string_length = new_string.size(); char_arr = new char[string_length + 1]; strcpy(char_arr, new_string.char_arr); }

Dac exist obiecte membre, atunci constructorul trebuie s le iniializeze pe acestea la nceput prin apelarea unui constructor:
Student::Student(const& Student un_st) : nume(un_st.nume) { virsta = un_st.virsta; }

(p) Operatorul de atribuire X::operator=(const X&) Asa cum am precizat i mai sus, obiectele pot fi atribuite unul altuia. Aici vom ncerca s explicm mai n detaliu ce se ntmpl cnd atribuim un obiect altui obiect. Dac avem definiiile:
Data azi(4,4,2000); Data ieri;

atunci pentru a realiza atribuirea:


ieri = azi;

urmtoarea instan a operatorului X& X::operator=(const X&) este apelat automat: 47

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Data& Data::operator=(const Data& d) { zi = d.zi; luna = d.luna; an = d.an; }

Ca i n cazul constructorului de copiere, dac avem alocare dinamic, atunci acest operator trebuie redefinit:
string& string::operator= (const string& a_string) { if ((*this) != a_string ) { if ( string_length != a_string.size()) { delete[] char_arr; string_length = a_string.size(); char_arr = new char[string_length+1]; } strcpy(char_arr, (char*) a_string ); } return (*this); }
Exerciiul 23

Descrierea complet a date calendaristice aa cum a fost ea fcut aici se gsete n dierctorul ex/c2cpp/data. 1. S se compileze i s se testeze progarmul demo. 2. S se adauge o dat membr numeZi care s memoreze numele zilei din sptmn (Luni, Mari etc). Aceast dat membru va fi memorat dinamic. 3. Ce funcii membru trebuie modificate pentru a putea manipula noua structura a datei calendaristice? S se modifice aceste funcii. 4. S se adauge noi funcii membre specifice noii structuri.

48

D. Lucanu

Programarea Calculatoarelor II

POO i C++

V Motenire
Motenirea este mecanismul prin care elementele specifice (specializate) ncorporeaz structura i comportarea elementelor generale.

V.A POO: Relaiile de generalizare i specializare


O generalizare este o relaie ntre o clas general, numit superclas sau clas printe, i o clas specializat a celei generale, numit subclas sau clas copil. Generalizarea nseamn c obiectele clasei copil pot fi utilizate oriunde apar obiecte ale clasei printe dar nu i reciproc. Cu alte cuvinte, un obiect copil poate substitui un obiect printe. Un obiect copil motenete atributele i operaiile printelui. Adesea, are atribute i operaii suplimentare dar acest lucru nu este obligatoriu. Specializarea este inversa relaiei de generalizare. Prezentm exemplul claselor Student i Profesor. Proiectarea clasei Student: Sunt un student: mi cunosc ID-ul, mi cunosc numele, tiu disciplinele pe care le urmez, pot s-mi spun ID-ul, pot s-mi spun numele, pot s m nscriu la o nou disciplin.
Student id nume discUrmata getId() getNume() addDiscUrmata()

Figura 4

49

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Proiectarea clasei Profesor: Sunt un profesor: mi cunosc ID-ul, mi cunosc numele, tiu disciplinele pe care le predau, pot s-mi spun ID-ul, pot s-mi spun numele, pot s predau o nou disciplin.
Profesor id nume discPredate getId() getNume() addDiscPredate() examineaza()
Figura 5

Clasele Student i Profesor au n comun atributele ID i numele i aciunile asupra acestor atribute. Este mult mai economic ca acestea s fie descrise o singur dat ntr-o clas separat general Persoana i apoi atributele i operaiilor acesteia s fie motenite de cele dou clase specializate Student i Profesor. Generalizm: Sunt o persoan: mi cunosc ID-ul, mi cunosc numele, pot s-mi spun ID-ul, pot s-mi spun numele. Specializm: Sunt un student: motenesc atributele i operaiile de la Persoana, tiu disciplinele pe care le urmez, pot s m nscriu la o nou disciplin. 50

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Sunt un profesor: motenesc atributele i operaiile de la Persoana, tiu disciplinele pe care le predau, pot s predau o nou disciplin, Astfel, ntre clasa Persoana i clasa Student apare o relaie de generalizare/specializare. O relaie de acelai tip are loc ntre clasa Persoana i clasa Profesor. Acum cele trei clase formeaz o ierarhie: clasa Persoana este clasa printe si clasele Student i Profesor sunt clase copil (fiice). Aceast ierarhie este reprezentat grafic n Figura 6:
Persoana id nume getId() getNume()

Student discUrmata addDiscUrmata()

Profesor discPredate addDiscPredate() examineaza()

Figura 6

V.B Derivare in C++


Relaia de generalizare/specializare este implementat n C++ prin relaia de derivare. Ierarhia de mai sus este descris astfel:
class Persoana { public: Persoana(string = "", string = ""); ~Persoana(); string getNume() const; string getId() const; protected: string id; string nume; };

51

D. Lucanu

Programarea Calculatoarelor II

POO i C++

class Student : public Persoana { public: Student(string="", string=""); ~Student(); void addDiscUrmata(Disciplina*); private: Disciplina *discUrmate[MAX_nrDiscUrmate]; int nrDiscUrmate; }; class Profesor : public Persoana { public: Profesor(string = "", string = ""); ~Profesor(); void addDiscPredata(Disciplina *); void examineaza(Examen&) const; private: Disciplina *discPredate[MAX_nrDiscPredate]; int nrDiscPredate; };

Notm c nivelul de protecie al atributelor id i nume din clasa Persoana este acum protected n loc de private. Aceasta nseamn c cele dou nume sunt vizibile att n clasa proprietar Persoana ct i n clasele mostenitoare Student i Profesor. Dac am fi folosit private, atunci ele ar fi fost vizibile numai n clasa proprietar Persoana. Declaraia
class Student : public Persoana

precizeaz faptul c Student deriveaz din clasa Persoana. Din acest motiv, clasa Student se numete i clasa derivat iar clasa Persoana clas de baz. Cuvntul public constituie tipul de derivare si precizeaz modul n care se propag nivelurile de protecie a membrilor: ce e public n clasa de baz ramne public n clasa derivat; ce e protected n clasa de baz ramne protected n clasa derivat. n afar de public, mai exist urmtoarele tipuri de derivare: 52

D. Lucanu

Programarea Calculatoarelor II

POO i C++

protected: ce e public n clasa de baz devine protected n clasa derivat; ce e protected n clasa de baz ramne protected n clasa

derivat;
private: ce e public n clasa de baz devine private n clasa derivat; ce e protected n clasa de baz devine private n clasa derivat.

(a) Conversii standard n cazul motenirii publice au loc urmtoarele conversii: un obiect derivat poate fi convertit implicit la un obiect public de baz; o adres a unui obiect derivat poate fi convertit implicit la o adres public a unui obiect de baz; un pointer la un obiect derivat poate fi convertit implicit la un pointer public la un obiect de baz. Presupunem c avem urmtoarea funcie care afieaz id-ul i numele unei persoane:
void scrie(Persoana& p) { cout << p.getId().c_str() << ' ' << p.getNume().c_str(); cout << endl; }

Putem apela funcia scrie() cu parametru Profesor:


Profesor b("101", "Prof. Popescu"); scrie(b);

n apelul de mai sus are loc o conversie de la clasa derivat Profesor la clasa de baz Persoana. Putem de asemenea scrie:
Persoana a; a = b;

i n acest caz avem o conversie de acelai tip.


Exerciiul 24

n directorul ex/most/ se gsete un exemplu de ierarhie descris n C++. 53

D. Lucanu

Programarea Calculatoarelor II

POO i C++

1. Citii fiierul most.h i desenai ierarhia de clase descris. 2. Compilai i testai programul demo. 3. S se adauge instruciuni astfe nct fiecare metod constructor sau destructor s-i afieze numele atunci cnd este execuatat. 4. Scriei diferite programe demo care s evidenieze ordinea de apelare a constructorilor i destructorilor. 5. Programul include i un exemplu de motenire multipl. Care este clasa care beneficiaz de motenire multipl? Care sunt datele membre i funciile membre ale ei?

54

D. Lucanu

Programarea Calculatoarelor II

POO i C++

VI

Polimorfism

Polimorfismul se refer la posibilitatea de a folosi acelai nume cu nelesuri diferite. n C++, polimorfismul poate fi realizat n mai multe moduri. (a) Polimorfism parametric n subseciunile III (l) i III (m) am vzut cum pot fi suprancrcate numele operatorilor sau funciilor. Deoarece nelesul corect este ales n funcie de natura parametrilor, numim acest polimorfism ca fiind parametric. (b) Suprascriere O operaie a unei clase copil cu acelai prototip (signatur) cu cea a unei operaii a clasei printe suprascrie operaia clasei printe. Considerm din nou ierarhia Persoana. Presupunem c dorim s adugm o metod semneaza(). Un student va semna adaugnd prefixul "Student" iar un profesor va semna adaugnd prefixul "Profesor". Urmeaz ca fiecare dintre cele trei clase s aib propria sa metod semneaza():
class Persoana { . . . void semneaza(); }; class Student : public Persoana { . . . void semneaza(); }; class Profesor : public Persoana { . . . void semneaza(); }; void Persoana::semneaza() { cout << getNume() << endl; }

55

D. Lucanu

Programarea Calculatoarelor II

POO i C++

void Student::semneaza() { cout << "Student " << getNume() << endl; } void Profesor::semneaza() { cout << "Profesor " << getNume() << endl; }

n prezena declaraiilor
Persoana p("0000", "Strainu Stefan"); Student st("1111", "Ionescu Ion"); Profesor pr("2222", "Popescu Petru");

instruciunile
p.semneaza(); st.semneaza(); pr.semneaza();

vor produce ieirea:


Strainu Stefan Student Ionescu Ion Profesor Popescu Petru

n schimb, dac definim funcia


void semneaza(Persoana* p) { p->semneaza(); };

i executm apelurile
semneaza(&p); semneaza(&st); semneaza(&pr);

vom avea surpriza s obinem ieirea:


Strainu Stefan Ionescu Ion Popescu Petru

Aceasta se ntmpl din motiv c parametrii actuali ai funciei externe semneaza() sunt convertii la tipul de baz Persoana i de fiecare dat se va apela funcia membr semneaza() a acestei clase. 56

D. Lucanu
Exerciiul 25

Programarea Calculatoarelor II

POO i C++

Exemplul de mai sus se gsete descris complet n directorul ex/override. Compilai i executai programul demo. S se adauge o funcie membr frecventeazaCurs() care pentru profesor afieaz Pred. iar pentru student afieaz Ascult i ia notie.. Modificai programul demo pentru a testa noua funcie membru. (c) Funcii virtuale Problema din subseciunea anterioar poate fi rezolvat cu ajutorul funciilor virtuale:
class Persoana { . . . virtual void semneaza(); }; class Student : public Persoana { . . . virtual void semneaza(); }; class Profesor : public Persoana { . . . virtual void semneaza(); };

Pentru funciile membre virtuale exist posibilitatea cunoaterii la momentul execuiei a funciei corecte ce trebuie apelat. Aceasta se realizeaz prin memorarea pentru fiecare obiect al clasei a unei tabele de pointeri la funciile virtuale. Acum, instruciunile
semneaza(&p); semneaza(&st); semneaza(&pr);

vor produce ieirea corect:


Strainu Stefan Student Ionescu Ion Profesor Popescu Petru

57

D. Lucanu
Exerciiul 26

Programarea Calculatoarelor II

POO i C++

Exemplul de mai sus se gsete descris complet n directorul ex/polim. Compilai i executai programul demo. S se adauge funcie membr frecventeazaCurs() ca n Exerciiul 25 dar acum declarat virtual. Modificai programul demo pentru a testa noua funcie membru i a evidenia efectul declaraiei virtual.
Exerciiul 27

n directorul ex/c2cpp/most2 se gasete descris ierarhia din ex/c2cpp/most dar n care unele funcii sunt virtuale. 1. Care funcii au fost declarate virtuale? 2. Compilai i executai programul demo aa cum este precizat n comentarii. Explicai efectul funciilor declarate virtual.
Exerciiul 28

n directorul ex/con_bancar_v04 se gsete o ierarhie care descrie conturi bancare. 1. S se deseneze grafic ierarhia descris de fiierul cont.h. 2. S se compileze i s se execute programul demo. Care este rolul functiilor declarate virtual? 3. S se adauge un nou tip de cont bancar, de exemplu ContDebit. 4. S se modifice i s se testeze programul demo astfel nct s evidenieze utilizarea noul tip de cont.

58

D. Lucanu

Programarea Calculatoarelor II

POO i C++

VII

Clase parametrizate

Presupunem c dorim s scriem o clas Cell care s modeleze activitatea unei celule de memorie. Aceast clas va avea dou metode:
getVal() - ntoarce valoarea memorat n celul; setVal() - memoreaz o nou valoare n celul.

Celulele pot memora informaii diferite: caractere, numere ntregi, iruri de caractere etc. O soluie ar fi crearea mai multor clase: CharCell, IntCell, StringCell etc. Evident, o astfel de soluie nu este de loc comod pentru proiectantul claselor. Mult mai simplu ar fi dac am putea defini clasa Cell ntr-o manier parametrizat n care tipul informaiei memorate n celul s fie transmis ca parametru. n C++ acest lucru este posibil cu ajutorului "template"-urilor (abloanelor):
template <class Elt> class Cell { public: Cell(); ~Cell(); Elt getVal(); void setVal(Elt); private: Elt* val; };

Cuvntul cheie template specific faptul c definiia care urmeaz este parametrizat. Parametrii sunt descrii n interiorul parantezelor unghiulare. Cuvntul cheie class dintre parantezele unghiulare spune c parametrul Elt este un tip. Definiiile funciilor membre trebuie i ele precedate de o declaraie "template":
template <class Elt> Cell<Elt>::Cell() { val = new Elt; } template <class Elt>

59

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Cell<Elt>::~Cell() { delete val; } template <class Elt> Elt Cell<Elt>::getVal() { return *val; } template <class Elt> void Cell<Elt>::setVal(Elt oVal) { *val = oVal; };

Acum putem crea celule de char-uri:


Cell c; c.setVal('A');

sau celule de int-uri:


Cell x; x.setVal(100);

Exemplul complet se gsete n ex\cell. (a) Stive parametrizate Tipurile de date stiv, coad, lista liniar, arbori binari etc pot fi implementate cu ajutorul claselor paramterizate ntr-un mod elegant. S presupunem c utilizm ntr-un program dou tipuri de stive: de char-uri i stive de float-uri. O soluie neelegant ar fi definirea a doua clase: StivaChar si StivaFloat. Dac am avea mai multe tipuri de stive, atunci ar trebuie s definim tot attea clase a cror descrieri sunt asemntoare. O soluie parametrizat ne salveaz de aceast munc de rutin. Presupunem c stivele sunt implementate prin liste nlnuite. Mai nti definim clasa pentru nodurile listei:
template <class Elt> class NodStiva { public:

60

D. Lucanu

Programarea Calculatoarelor II

POO i C++

friend class Stiva; private: Elt elt; NodStiva *leg; };

Deoarece n definiia clasei NodStiva intervine numele clasei Stiva, definiia de mai sus trebuie precedat de o declaraie "forward" de tipul:
template <class Elt> class Stiva;

Clasa Stiva are definiia aa cum ne ateptam:


template <class Elt> class Stiva { public: Stiva() { virf = NULL; } ~Stiva() { } void push(Elt&); void pop(); Elt top(); private: NodStiva *virf; };

Acum putem declara stive de char-uri:


Stiva<char> cst;

sau de float-uri:
Stiva<float> fst;

Exemplul complet se gsete n ex\stiva_p.


Exerciiul 29

S se scrie implementri C++ ale listelor liniare parametrizate, cozilor paramatrizate, arborilor binari parametrizai.

61

D. Lucanu

Programarea Calculatoarelor II

POO i C++

VIII Studiu de caz: modelarea nscrierii la discipline i examinrii


Un profesor poate preda mai multe discipline. Un student poate urma mai multe discipline. Fiecare disciplin este finalizat printr-un examen. Simplificnd foarte mult, putem presupune c o facultate se compune dintr-o mulime de profesori, o mulime de studeni, un set de discipline i un set de examene. Se dorete scrierea unui program care s modeleze activitile din facultate. (a) Proiectarea claselor Substantivele subliniate din descrierea problemei candideaz la denumiri de clase. Clasele Student si Profesor au fost definite n seciunea V dedicat motenirii.
Clasa Disciplina

Stabilim mai nti responsabilitile clasei: Sunt o disciplin: mi cunosc codul, mi cunosc denumirea, mi cunosc programa analitic, mi cunosc titularul, pot schimba titularul, pot schimba programa analitic. Din descrierea de mai sus rezult c denumirea, codul i programa analitic candideaz pentru atribute. Ultimele dou aciuni candideaz la metode. ntre obiectele titular i disciplin apare o relaie de asociere ce va fi discutat mai trziu. Aceasta relaie este materializat prin metodele setTitular() i getTitular().

62

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Disciplina cod nume progAnal titular setTitular() getTitular() setProgAnal() getProgAnal() getCod() getNume()

Figura 7

Clasa Examen

Stabilim mai nti responsabilitile clasei: Sunt un examen: cunosc disciplina care se examineaz, cunosc data susinerii evalurii, cunosc studenii nscrii la examen i notele lor (dup evaluare), tiu s listez notele, tiu s nscriu studeni. Din descrierea de mai sus rezult c data i lista studenilor nscrii la examen mpreun cu notele lor vor candida la atribute. ntre obiectele examen i disciplin apare o relaie de asociere ce va fi reprezentat mai trziu. Ultimele dou aciuni candideaz la metode.

63

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Examen disc dat (stud,nota)[] adStud() listeazaNote() setNota()

Figura 8

Clasa Facultate

O facultate (mult simplificat) se compune din studeni, profesori, discipline i examene. Astfel, ntre facultate i aceste obiecte apare o relaie de compoziionalitate reprezentat grafic ca n Figura 9 Atributele i metodele

Figura 9

clasei Facultate materializeaz aceast relaie de compoziie (a se vedea Figura 9). Descrierea C++ a relaiei de compoziie:
class Facultate { public: Facultate(int=0, int=0, int=0, int=0); //. . . private:

64

D. Lucanu

Programarea Calculatoarelor II
Student* stud; int nrStud; int MAX_nrStud; //. . .

POO i C++

};

Facultate stud prof disc ex inscrieLaExam() examineaza() listeazaNote() listeazaStud() addStud() addProf() addDisc() addExam()

Figura 10

(b) Relaii de asociere n aceast seciune studiem relaiile de asociere ntre obiecte i/sau ntre clase. Aceste relaii sunt reprezentate n Figura 11. Relaiile sunt reprezentate prin linii care unesc obiecte/clase.

Figura 11

65

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Relaia student-disciplin

Un student poate urma mai multe discipline, iar o disciplin poate fi urmat de mai muli studeni; aceste proprieti ale relaiei sunt marcate de cele dou stelue de lng obiecte. Un student cunoate disciplinele pe cale le urmeaz iar disciplina nu cunoate studenii care o urmeaz; relaia poate fi navigat ntr-un singur sens i aceast proprietate ar trebui marcat printr-o sageat ce nu a mai fost inclus pentru a menine figura ct mai simpl. Implementarea C++ a relaiei:
class Student : public Persoana { public: . . . void addDiscUrmata(Disciplina*); private: Disciplina *discUrmate[MAX]; int nrDiscUrmate; }; class Disciplina { ... // nimic despre studentii inscrisi };

Relaia disciplin-profesor

O disciplin are ca titular un singur profesor iar un profesor poate fi titular la mai multe discipline. Disciplina i cunoate titularul iar profesorul i cunoate disciplinele pe care le pred; relaia poate fi navigat n ambele sensuri. Implementarea C++ a relaiei:
class Disciplina { public: ... void setTitular(Profesor*); Profesor* getTitular() const;

66

D. Lucanu

Programarea Calculatoarelor II

POO i C++

private: ... Profesor* titular; }; class Profesor : public Persoana { public: ... void addDiscPredata(Disciplina *); private: Disciplina *discPredate[MAX]; int nrDiscPredate; };

Relaia disciplin-examen

Pentru o disciplin pot fi programate mai multe examene, iar un examen se d exact la o disciplin. Examenul tie disciplina la care se evalueaz, iar disciplina nu tie examenele programate (navigare intr-un singur sens). Implementarea C++ a relaiei:
class Disciplina { // nimic despre examene (?) }; class Examen { public: //. . . Disciplina* getDisc() const; private: Disciplina* disc; //. . . };

67

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Relaia student-examen

Un student poate susine mai multe examene iar la un examen sunt evaluai mai muli studeni; relaia poate fi navigat ntr-un singur sens (examenul tie studenii nscrii pentru evaluare). Implementarea C++ a relaiei:
class Student : public Persoana { // nimic despre examene (?) }; class Examen { public: void addStudent(Student*); private: . . . struct { Student* stud; int nota; } eval[MAX_nrStudEx]; int nrStud; void setNota(Student*, int); };

(c) Comunicarea ntre obiecte Obiectele colaboreaz ntre ele pentru a realiza o anumit sarcin. Colaborarea se realizeaz prin transmiterea de mesaje. La primirea unui mesaj, un obiect execut o metod i poate transmite mesaje altor obiecte. Diagrama din Figura 12 descrie colaborarea ntre obiecte pentru a realiza examinarea.

68

D. Lucanu

Programarea Calculatoarelor II

POO i C++

3: examineaza() fii : Sco::Facultate 1: inscrieLaExam()


5: 2: ad list ea Stud za () No te( )

: Sco::Profesor

anonim

: Sco::Examen

Figura 12

Aceast colaborare se desfoar dup urmtorul scenariu: Cineva (un student, secretara, asitentul etc) transmite ctre facultate mesajul c dorete s nscrie studeni la un examen. Facultatea, ca rspuns la acest mesaj, execut metoda inscrieLaExam(). Pentru fiecare student, facultatea transmite examenului un mesaj. La primirea acestui mesaj, examenul execut metoda addStud() (adaug studentul):
void Facultate::inscrieLaExamen() { ... for (i=0; i<nrInscr; i++) { ... if (j < nrStud) ex[iEx].addStudent(&stud[j]); else cout << "Id student eronat." << endl; } };

Facultatea transmite profesorului mesajul s desfoare examenul:


void Facultate::examineaza() { ...

69

4: setNota()

D. Lucanu

Programarea Calculatoarelor II
prof[P].examineaza(ex[E]);

POO i C++

};

Drept rspuns, profesorul execut metoda examineaza(). Pentru fiecare student, profesorul transmite examenului nota luat de student.
void Profesor::examineaza(Examen& unEx) const { ... for (int i=0; i < unEx.nrStud; i++) { ... unEx.setNota(unEx.exam[i].stud, notaStud); } }

Dup terminarea examenului, facultatea transmite examenului s listeze studenii cu notele obinute:
void Facultate::listeazaNote() const { ... ex[iEx].listeazaNote(); }; Programele C++ se gsesc n directorul ex/facultate_v01.
Exerciiul 30

S se aduc urmtoarele modificri la aplicaia descris n ex/facultate_v01. 1. Imbuntirea dialogului program-utilizator. 2. Incrcarea i salvarea datelor din fiiere. 3. S se reproiecteze clasa Student astfel nct studenii s poat fi clasificai pe ani i pe grupe. 4. Este foarte probabil ca numrul real de studeni din facultate s depaeasc pe cel preconizat la nfiinarea facultii. S se scrie o metod care redimensioneaz facultatea. 5. Metoda de nscriere la examene poate fi mbuntit n modul urmtor: se introduce prima liter a numelui, se listeaz toi studentii care ncep cu acea liter (inclusiv ID-ul fiecaruia), dup care 70

D. Lucanu

Programarea Calculatoarelor II

POO i C++

utilizatorul introduce ID-ul studentului care se nscrie. Dac e necesar, studenii vor fi listai pe dou coloane. 6. Gestionarea dinamic a numrului de discipline predate de ctre un profesor. 7. Selectarea disciplinei la adugarea unui examen. 8. De modificat selectarea examenului la nscriere la examinare astfel nct s se poat selecta dintr-o list mare (peste 50 examene). Validarea examenului selectat. 9. Adaugarea de operatori << i >>. 10. Adugarea unui nivel de securitate. De exemplu, notele la un examen trebuie puse numai de profesorul titular. 11. Adugarea unui operator de tipul prof[id]. 12. Interfaa utilizator bazat pe meniu de comenzi. Exemplu de meniu: 1. Creare facultate. 2. Adaug student. 3. Adaug profesor. 4. Adaug disciplin. 5. Adaug examen. 6. Susine examen. 7. Afiare note examen. 8. Afiare studeni. 9. Terminare optiune> 13. Proiectarea OO a unei interfee utilizator, versiunea consol. 14. Adugarea unei interfee grafice. 15. n mod real, disciplinele si profesorii snt arondai catedrelor. S se adauge o clas Catedra i s se stabileasc relaiile facultate catedra discipline - profesor.

71

D. Lucanu

Programarea Calculatoarelor II

POO i C++

Bibliografie
H. Schildt: C++ manual complet, Teora, 2000 D. Kaler, M.J. Tobler, J. Valter: C++, Teora, 2000 Bjarne Stroustrup: The C++ Programming Language, Adisson-Wesley, 3nd edition, 1997 Stanley B. Lippman: C++ Primer, Addison Wesley, 1992 ***: Programming Languages - C++, ISO/IEC 14882, First edition, 199809-01
Resurse electronice

Peter Mller: Introduction to Object-Oriented Programming Using C++ http://www.gnacademy.org/uu-gna/text/cc/material.html Bruce Eckel : Thinking in C++, 2nd Edition, http://www.bruceeckel.com/ *** : Online C++ tutorial, http://www.intap.net/~drw/cpp/index.htm *** : SGI Standard Template http://www.sgi.com/tech/stl/ Library Programmer's Guide,

*** : Situl de documentatii electronice al facultatii: http://lib.info.uaic.ro/programming.php?catid=1&subcatid=2 *** : Pagina cursului Programare II de la Facultatea de Informatic, Universitatea Alexandru Ioan Cuza Iai, http://www.infoiasi.ro/fcs/CS1207.php

72

D. Lucanu

Programarea Calculatoarelor II

POO i C++

A.

Fiierul my_string.h
#ifndef STRING_H #define STRING_H #include <iostream.h> class string { public: // Constructori/destructori string(); string(char* new_string); string(const string& new_string); ~string(); // Implementori int size() const; const char* c_str() const; // Operatori friend string operator+ (const string& lhs, const string& rhs); string& operator+= (const string& a_string); string& operator= (const string& a_string); friend bool operator< (const string& lhs, const string& rhs); friend bool operator> (const string& lhs, const string& rhs); friend bool operator== (const string& lhs, const string& rhs); friend ostream& operator<< (ostream& os, const string& a_string); private: char* char_arr; int string_length; }; #endif

73

D. Lucanu

Programarea Calculatoarelor II

POO i C++

B.

Fiierul my_string.cpp
#include #include #include #include <iostream.h> "my_string.h" <string.h> <stdio.h>

// string - functiile membre string::string() { char_arr = new char[1]; char_arr[0] = '\0'; string_length = 0; } string::string(char* new_string) { string_length = strlen(new_string); char_arr = new char[string_length + 1]; strcpy(char_arr, new_string); } string::string(const string& new_string) { string_length = new_string.string_length; char_arr = new char[string_length + 1]; strcpy(char_arr, new_string.char_arr); } string::~string() { delete [] char_arr; } int string::size() const { return string_length; }

74

D. Lucanu

Programarea Calculatoarelor II

POO i C++

const char* string::c_str() const { return char_arr; } string operator+ (const string& lhs, const string& rhs) { char *pchar; pchar = new char[lhs.string_length + rhs.string_length + 1]; strcpy(pchar, lhs.char_arr); strcat(pchar, rhs.char_arr); return string(pchar); } string& string::operator+= (const string& rhs) { char *pchar; string_length += rhs.size(); pchar = new char[string_length + 1]; strcpy(pchar,char_arr); strcat(pchar, rhs.char_arr); delete char_arr; char_arr = pchar; return (*this); } string& string::operator= (const string& rhs) { string_length = rhs.string_length; delete char_arr; char_arr = new char[string_length + 1]; strcpy(char_arr, rhs.char_arr); return (*this); } bool operator< (const string& lhs, const string& rhs) { int i = 0;

75

D. Lucanu

Programarea Calculatoarelor II

POO i C++

while ((lhs.char_arr[i] == rhs.char_arr[i]) && (i < lhs.string_length) && (i < rhs.size())) i++; if (i == rhs.string_length) return false; if (i == lhs.string_length) return true; if (lhs.char_arr[i] < rhs.char_arr[i]) return true; return false; } bool operator> (const string& lhs, const string& rhs) { int i = 0; while ((lhs.char_arr[i] == rhs.char_arr[i]) && (i < lhs.string_length) && (i < rhs.size())) i++; if (i == lhs.string_length) return false; if (i == rhs.string_length) return true; if (lhs.char_arr[i] > rhs.char_arr[i]) return true; return false; } bool operator== (const string& lhs, const string& rhs) { int i = 0; while ((lhs.char_arr[i] == rhs.char_arr[i]) && (i < lhs.string_length) && (i < rhs.size())) i++; if ((i == lhs.string_length) && (i = rhs.string_length)) return true; return false; }

76

D. Lucanu

Programarea Calculatoarelor II

POO i C++

ostream& operator<< (ostream& os, const string& a_string) { os << a_string.char_arr; return os; }

77