You are on page 1of 38

PROGRAMAREA ORIENTATĂ PE OBIECTE

Proiectarea orientatã obiect POO
utilizează elementele programării structurate

se bazează pe orientarea pe obiecte

extindere

organizarea de resurse soft ca şi o colecţie de obiecte, distincte şi discrete, care înglobează atât structuri de date cât şi funcţiile de prelucrare ale acestora.

programarea clasică orientarea pe obiecte

“ce trebuie făcut cu datele?” (proceduri care să transforme datele în rezultate) datele şi legăturile între acestea (elemente prin care se modelează obiectele lumii reale) identificarea şi dezvoltarea de componente software.

Ingineria software caracteristici ale componentelor:

• o componentă trebuie să aibă un set redus bine definit de responsabilităţi; • interacţiunea dintre componente trebuie să fie minimă; • identificarea componentelor se realizează în momentul în care se imaginează procesul de execuţie a sistemului; • o componentă reprezintă o pereche constituită din:

entităţi abstracte care realizează anumite acţiuni, adică îndeplinesc anumite responsabilităţi.

comportament

setul de acţiuni pe care le realizează; descrierea comportamentului unei componente se mai numeşte şi protocol.

stare

toate informaţiile memorate în interiorul ei.

Implementarea componentelor software se face prin intermediul construcţiilor de tip clasă, puse la dispoziţie de limbajele de programare orientate obiect.

În general, pentru modelul de date orientat pe obiect, se consideră definitorii următoarele concepte:

abstractizare, obiect, atribut, metodă,

clasă,

încapsulare,

moştenire,

polimorfism.

Abstractizarea procesul de simplificare a realităţii prin reţinerea caracteristicilor şi comportamentelor esenţiale şi prin abstractizare se izolează aspectele esenţiale de cele neesenţiale, în funcţie de perspectiva de pentru o aceeaşi problemă, pot exista mai multe abstractizări, deci mai multe modele. Selectarea obiectelor abstracte şi a claselor este un punct important în realizarea unui program. constituirea lor într-un model adecvat rezolvării problemelor. abordare a problemei de rezolvat

Obiectul
- o entitate individualizată prin nume - conţine o mulţime de date funcţii descriu proprietăţile şi nivelul acestora definesc comportamentul.

- obiectele pot fi clasificate în mulţimi. O mulţime de obiecte de acelaşi fel constituie o clasă de obiecte, descrisă prin modelul comun al obiectelor sale.

Clasa
- grup de obiecte care fac parte din ea - o clasă este o descriere a proprietăţilor şi comportamentului unui tip obiect; o clasă poate fi considerată ca un tip special de dată, iar obiectele sale ca variabile de acest tip. - pentru identificarea claselor unui sistem trebuie luate în consideraţie următoarele aspecte: ¤ Există informaţii care trebuiesc stocate sau analizate? Dacă da, ele sunt posibile candidate pentru o clasă. ¤ Există sisteme externe cu care va comunica sistemul construit? Dacă da, este normal să fie luate în considerare la modelarea sistemului. ¤Există biblioteci sau componente din proiecte anterioare care pot fi folosite? În caz afirmativ acestea conţin clase candidate. ¤ Sunt device-uri în sistem care vor trebui gestionate? Orice device conectat la sistem înseamnă o clasă candidat care sa-l gestioneze. ¤ Există părţi organizaţionale în sistem? Va fi nevoie de clase în care să fie implementate. Definirea unei clase se realizează prin: • precizarea operanzilor sau a atributelor; • funcţiile membre; • proprietăţile asociate componentelor pentru a stabili modalităţi de referire.

Obs: Atributele sunt de un anumit tip (de exemplu întregi, reale, caractere etc.). Setul de valori ale atributelor unui obiect la un moment dat formează starea curentă a obiectului respectiv. Funcţiile care definesc comportamentul obiectelor sunt cunoscute ca metode ale clasei. Împreună, atributele şi metodele sunt membrii clasei, identificabili prin nume.

O clasă este un tip de dată definit de utilizator printr-o construcţie de forma:

Date sau atribute Membrii Funcţii sau metode

Aşa cum variabilele în programe sunt de tip global şi local sau după modul de alocare sunt statice şi dinamice, analog, pentru a defini o anumită disciplină în manipularea claselor se asociază grupurilor de elemente din clasă un specificator de control al cărui câmp de acţiune este anulat de un altul. Specificatorii de control sunt: • public – ceea ce înseamnă că elementul respectiv este accesibil oricărei clase externe; • private – membrul este accesibil numai de funcţii din interiorul clasei sau de către funcţii prietene (friend) ale clasei; • protected – similar cu private dar accesul se extinde pentru funcţiile membre şi prietene derivate din clasa respectivă. Atunci, forma generală de declaraţie a unei clase devine:

Obs: - Efectul unui specificator de acces dureaza pâna la urmatorul specificator de acces. - Implicit, daca nu se declara nici un specificator de acces, datele sau functiile membre sunt de tip private.

int y=1) { p=x. se defineşte clasa Rational: #include<iostream. } }. } int numarator ( ) { return p. . nume_membru. indiferent de specificatorul de acces.o parte publica care reprezintă interfaţa clasei respective cu celelalte secţiuni din program Exemplul 1: pentru implementarea calculului cu numere raţionale.O clasă conţine deci: . . .La proiectarea unei clase se au în vedere următoarele aspecte: • funcţiile membru să acopere întreaga gamă de prelucrări. declararea unui obiect se face asemănător oricărei declaraţii de dată: nume_clasă nume_obiect. } int numitor ( ) { return q. . public: void nr (int x=0 . • între variabilele şi funcţiile membre să fie o concordanţă perfectă pentru a nu apare erori în execuţia programelor ce folosesc clase.O funcţie membră a unei clase are acces la toate datele membre din clasa respectivă.//a este un obiect al clasei rational a. 5). } main ( ) { rational a. în sensul că se creează o instanţă a acelei clase.numarator( )<<a. .q. q=y. o entitate concretă din mulţimea descrisă de clasa respectivă.Declararea unui obiect mai este numită şi instanţierea clasei. .numitor( ).nr (4 .Pentru a pune în evidenţă faptul că un membru aparţine unui obiect se utilizează formularea: nume_obiect.h> class rational { int p.o parte protejata/privata care asigura implementarea clasei. .Întrucât o clasă este un tip de dată..//apelam f-ctia nr din obiectul a cout<<a.

Exemplul 2: pentru implementarea calculului cu numere complexe. înmulţire. împărţire. scădere. se defineşte clasa Complex care trebuie să aibă o structură de tip articol pentru a grupa partea reală şi partea imaginară a numărului complex şi să conţină funcţii membre pentru operaţiile de adunare. Obiect al clasei Complex (ca şi variabilele. obiectele pot fi parametrii pentru funcţii) (componenta c. ridicare la putere şi extragere de radical.img a obiectului cpl) .

int y=1) { p=x. } int rational :: numarator ( ) { return p. } void rational :: nr (int x=0 . }. } int rational :: numitor ( ) { return q. Atunci când se descrie ulterior corpul unei metode (funcţii). int y).nr (4 .q. public: void nr (int x. cout<<a. . Altfel. astfel: tip_rezultat nume_clasă :: nume_metodă(lista parametrilor) corp metodă Exemplul 3: prin modificarea programului din exemplul 1 se obţine: #include<iostream. int numitor ( ). numele metodei este prefixat cu numele clasei din care face parte.h> class rational { int p. q=y. } }. pentru a specifica apartenenţa sa la clasa respectivă.numitor( ). clasa va conţine numai prototipul funcţiei. main ( ) { rational a. 5). a.numarator( )<<a. folosind operatorul de rezoluţie (::). int numarator ( ).Obs: Funcţiile vor fi descrise în cadrul clasei numai dacă au codul extrem de scurt şi nu conţin instrucţiuni de ciclare.

la fel ca în cazul declarării nu este iniţializat Pentru rezolvarea problemei iniţializării obiectelor există posibilitatea utilizării unor metode speciale.generat de compilator (cand nu exista constructori definiti de utilizator in clasa respectiva). iau valori in mod implicit. destructorii insa nu. Corpul unui astfel de constructor nu contine nici o instructiune. are ca efect alocarea de spaţiu în memorie. Un constructor fara parametri poarta denumirea de constructor implicit. b) cu parametri care. este necesară dezalocarea lor. .fara parametri (definit de utilizator) care poate contine in corpul sau orice instructiune valida. numite constructori. compilatorul de C++ genereaza automat un constructor respectiv destructor implicit. Un astfel de constructor „inline“ nu poate contine instructiuni cu caracter repetitiv. La terminarea ciclului de viaţă al obiectelor. *)in cazul in care o clasa nu dispune de constructori sau destructori. Declararea obiectelor oricărei variabile. Problema încheierii ciclului de viaţă al obiectelor este rezolvată prin utilizarea unor metode speciale numite destructori. . Constructorii si destructorii se declara si se definesc similar cu celelalte functii membre. in totalitate sau partial. destructorii se disting de constructori prin faptul ca numele lor este precedat de caracterul ~ *)nu pot returna nici un rezultat *)nu se pot utiliza pointeri catre constructori sau destructori *)constructorii pot avea parametri. Sintaxa: id_clasa (lista_parametri).CREAREA.//prototip Un constructor poate fi: a) implicit: . dar prezinta o serie de caracteristici specifice: *) numele lor coincide cu numele clasei careia ii apartin. INITIALIZAREA SI DISTRUGEREA OBIECTELOR UNEI CLASE Pentru crearea si distrugerea obiectelor in C++ functii membre speciale constructori destructori.

float complex::modul() {return sqrt(x*x+y*y). } . clrscr(). complex q.} }. // obiecte(instantieri) ale clasei complex getch().y. corespunzator fiecarei declarari a celor trei instante ale clasei complex.h> #include<conio. //datele clasei void display().} Testand programul se afiseaza pe ecran de trei ori mesajul: mesaj de la constructor. Obs: În cazul în care clasa prezintă constructori expliciţi valorile pentru iniţializare sunt transmise acestuia la declararea obiectului.} void complex::display() {cout<<x<<"+"<<y<<"*i".m.generat implicit de compilator. . Exemple de costructori definiti de catre programator: Ex1: #include<math. d) cu conversie de tip. //metoda clasei complex() //constructor fara parametri definit de catre programator {cout<<endl<<"mesaj de la constructor"<<endl.definit de utilizator.c) cu parametri care nu iau valori in mod implicit.h> #include<iostream. float modul(). //obiect(instantiere) a clasei complex void main() {complex q1. asemănător listei de parametri reali la apelul unei funcţii: nume_clasă nume_obiect(lista_valori).h> class complex {public: float x. q2. e) de copiere: .

} complex q.} complex(float yy). //datele clasei void display(). Ex2 #include<math. q. complex q2(6. clrscr().m. } float complex::modul() {return sqrt(x*x+y*y).Urmatorul exemplu contine in plus inca un constructor definit de catre programator. getch().h> #include<conio. cout<<endl.display(). y=0.h> class complex {public: float x.display(). void main() {cout<<"Obiectul global q=". complex q1.7).display(). complex::complex(float xx) {x=xx. }. cout<<"Obiectul local q1.y. float modul(). q1. retine valori reziduale". //se apleleaza constructorul cu un parametru q2. constructor ce are un parametru.h> #include<iostream. //metoda clasei complex() {cout<<endl<<"mesaj de la constructor"<<endl. } void complex::display() {cout<<endl<<x<<"+"<<y<<"*i". } .

float modul().display(). float yy) {x=xx.display(). complex::complex(float xx) {x=xx.} complex(float yy). q2. complex(float xx.h> class complex {public: float x. } complex::complex(float xx.display().3).h> #include<iostream. cout<<endl. cout<<"Obiectul local q1. //metoda clasei complex() {cout<<endl<<"mesaj de la constructor"<<endl. clrscr(). } . } void complex::display() {cout<<endl<<x<<"+"<<y<<"*i". //datele clasei void display(). complex q1. }.Urmatorul exemplu permite prin intermediul mecanismului de supraincarcare definirea unui constructor cu doi parametri: Ex3: #include<math.display(). } float complex::modul() {return sqrt(x*x+y*y). } complex q. void main() {cout<<"Obiectul global q=".1. complex q2(6. q1. cout<<"q3=".h> #include<conio.m. y=yy. complex q3(1. float yy).2. q3. q. cout<<"q2=". getch(). y=0.7). retine valori reziduale".y.

q1.} . //datele clasei void display().Crearea unui obiect temporar de tip complex (obiect cu q2. q4. float complex::modul() {return sqrt(x*x+y*y). fiind numiti constructori cu parametri impliciti. complex q1.display(). q3. // obiect initializat. conduce la: . complex q3(1.display(). .2.display(). } void complex::display() {cout<<endl<<x<<"+"<<y<<"*i". apelul nu se face in mod explicit void main() {cout<<"Obiectul global q=". } }. } complex q. //obiect neinitializat .3).Apelul constructorului pentru acest obiect temporar. apelul constructorului are forma unei simple declaratii . cout<<endl.Copierea acestui obiect temporar în q4.Obs: Ca orice alta functie în limbajul C++. q. din acest motiv s-a putut face declaratia "complex q. dar inaccesibil). // constructorul este apelat în mod explicit. apelul nu se face in mod explicit cout<<"Obiectul local q1 ". constructorii pot avea parametri impliciti .7). y=yy.1. //metoda clasei complex(float xx=0. x=xx. //obiect neinitializat .h> #include<iostream.m. Ex 4: Pentru clasa complex s-a definit un constructor cu parametri impliciti. Varianta constructorului cu parametri impliciti poate fi adoptata în toate cazurile în care constructorul nu necesita argumente.display().display(). clrscr(). La apelul explicit al constructorului: complex q2(6. // obiect initializatt.h> #include<conio. complex q4=complex(). getch(). . Daca toti parametrii unui constructor sunt impliciti. float modul().h> class complex {public: float x. complexq4=complex() evaluarea expresiei complex() cout<<"q2=".y. cout<<"q3=". float yy=0) {cout<<endl<<"mesaj de la constructorul cu parametri impliciti "<<endl." #include<math. o adresa precisa.

care sa permita copierea obiectelor.y. y=ob. } . se poate defini un contructor de copiere. Ex 5: #include<math. are. float complex::modul() {return sqrt(x*x+y*y).h> #include<iostream. x=ob. operatie posibila prin intermediul constructorului de copiere Pentru o clasa. folosind pentru aceasta operatie valorile primite ca argumente. } void complex::display() {cout<<endl<<x<<"+"<<y<<"*i". cout<<endl.x. float yy=0) {cout<<endl<<"mesaj de la constructorul cu parametri impliciti "<<endl. Obs: Daca programatorul nu defineste un constructor de copiere. //datele clasei void display().h> class complex {public: float x. Parametrul transmis prin referinta este obiectul a carui copiere se realizeaza. implicit. O alta forma de initializare care se poate face la crearea unui obiect este prin copierea datelor unui alt obiect de acelasi tip. } }. de obicei.m. x=xx. //metoda clasei complex(float xx=0.Constructori de copiere Functia principala a unui constructor este aceea de a initializa datele membre ale obiectului creat. constructorul de copiere pentru clasa nume_clasa.y. } complex(complex &ob) //constructor de copiere { cout<<endl<<"operatie de copiere ".h> #include<conio. y=yy. prototipul: nume_clasa (nume_clasa & ob). compilatorul genereaza un asemenea constructor. float modul().

în ordine inversa apelurilor constructorilor. complex q2=q1. . complementar.2.). Destructorul elibereaza memoria alocata de constructori. //se apeleaza constructorul de copiere cout<<"q3=". *)Destructorul nu are parametri si nu returneaza nici o valoare (antetul nu contine cuvântul cheie void. *)O clasa are un singur destructor. cout<<"q1=". clrscr(). incrementarea unui contor pentru instantele clasei). iar în corpul destructorului nu apare instructiunea return. Constructorii sunt folositi pentru alocarea memoriei. fata de constructori.1. initializarea datelor membru sau alte operatii (cum ar fi.display(). *)Daca programatorul nu a definit un destructor. q2. *)Destructorii se apeleaza la încheierea timpului de viata a obiectelor. compilatorul genereaza automat un destructor pentru clasa respectiva. q1. complex q3=complex(q1). Constructorul este apelat în momentul declararii obiectelor. la iesirea din blocul în care este recunoscut acel obiect. q3. *)Numele destructorului este precedat de semnul ~. Destructorul este apelat automat.display(). } Destructori Destructorii sunt metode ale claselor care actioneaza în sens invers.display(). Proprietatile destructorilor : *)Destructorul are acelasi nume ca si clasa a carui metoda este. getch().void main() {complex q1(1.3). //se apeleaza constructorul de copiere cout<<"q2=".

} void complex::display() {cout<<endl<<x<<"+"<<y<<"*i". } . } ~complex () {cout<<endl<<"am apelat destructorul pentru"<<endl.display().3. x=ob.} }.m.1).1.1. float modul(). //metoda clasei complex(float xx=0. cout<<"q1=".h> class complex {public: float x.2.3). cout<<endl.h> #include<iostream. } complex(complex &ob) { cout<<endl<<"operatie de copiere ". y=yy. float complex::modul() {return sqrt(x*x+y*y). y=ob.Ex 6: #include<math.2. x=xx. } void main() {complex q1(1.display().y.3. //datele clasei void display(). q2. complex q2(2. cout<<"q3=".h> #include<conio. //destructor display(). complex q3(3.2). q3. cout<<"q2=". float yy=0) {cout<<endl<<"mesaj de la constructorul cu parametri impliciti "<<endl.y. // getch(). q1.x.display().

O funcţie membră a unei clase definită (nu doar declarată) în interiorul clasei este implicit funcţie inline: Alt exemplu: Metoda “tipareste” este declarată explicit. iar în locul unde este definită este prefixată de cuvântul cheie “inline”. Utilizarea funcţiilor inline măreşte viteza de execuţie a programului prin evitarea operaţiilor pe stivă. ea fiind doar declarată în cadrul clasei. dar plăteşte drept preţ pentru aceasta creşterea dimensiunii codului executabil. Compilatorul înlocuieşte orice apel al unei funcţii inline cu codul acesteia. ce au loc în cazul apelurilor de funcţii obişnuite. Declaraţia unei funcţii inline este formată din declaraţia unei funcţii obişnuite precedată de cuvântul cheie inline: Sintaxa pentru definiţia claselor permite declararea implicită a unei funcţii inline. .Funcţii inline alternativă la utilizarea excesivă a macro-definiţiilor din limbajul C. În felul acesta se elimină operaţiile suplimentare de apel şi revenire din funcţie.

double y){ … } Obs: 1) Metodele care nu sunt membru al unei clase nu pot fi declarate inline decât explicit. for.O funcţie membră definită în afara clasei este implicit o funcţie normală (nu este inline). 2. care fac posibilă separarea mecanismului intern de interfaţa clasei. . funcţia set( ) din clasa Complex poate fi declaratã explicit inline : inline void Complex::set(double x. do-while). folosirea claselor pentru unirea structurile de date şi a funcţiilor destinate manipulării lor. deci nu pot fi utilizate decât în modulul de program în care au fost definite şi nu pot conţine instrucţiuni ciclice (while. Dar şi o astfel de funcţie poate fi declarată explicit inline: Fie exemplul următor: În acest exemplu. folosirea secţiunilor private şi publice. separarea elementelor unui tip de date abstract (clasă) în două părţi: structura dată de implementarea acestuia definirea datelor şi a funcţiilor membre comportarea accesată prin interfaţă declaraţiile datelor şi funcţiilor membre de tip public În C++ încapsularea este îndeplinită prin două aspecte: 1. 2)Funcţiile inline nu pot fi declarate funcţii externe. Încapsularea datelor (ascunderea informaţiei) ascunderea detaliilor de implementare ale unui obiect faţă de lumea exterioară (aplicaţiile care îl folosesc).

în acelaşi timp permite accesul la datele private ale clasei poate arăta astfel: Datele membre ale clasei (re şi im) sunt date de tip private. . etc. ci sunt comuni tuturor instanţierilor unei clase. care pot fi apelate din orice punct al domeniului de definiţie al clasei şi fac parte din interfaţa clasei. 1) date membre statice: Cea mai frecventă utilizare a datelor membre statice este de a asigura accesul la o variabilă comună mai multor obiecte. iar accesul la acestea este posibil prin intermediul funcţiilor membre publice set(). Odată declarat “static”. datele membre sunt declarate private sau protected şi nu pot fi accesate direct (pentru citire sau scriere) din funcţii nemembre ale clasei care nu sunt de tip friend (sau nu aparţin unei clase friend a clasei respective). Date şi funcţii membre de tip static Cuvântul cheie “static” poate fi utilizat în prefixarea membrilor unei clase. o implementare care respectă principiul încapsulării. datorită faptului că membrii statici nu aparţin unui anumit obiect. setre(). respectând principiul încapsulării. setim().Obs: 1) În general. membrul în cauză are proprietăţi diferite. pentru clasa Complex. De exemplu. 2) Pentru citirea sau modificarea unora dintre datele membre protejate în clasa respectivă se pot prevedea funcţii membre de tip public. dar. deci pot înlocui variabilele globale.

folosind operatorul de rezoluţie. Se poate urmări evoluţia diferită a celor două variabile v şi s prin crearea a două obiecte x şi y de tip S. şi prin apelul funcţiilor incs() şi incv() pentru obiectul x astfel: La execuţia acestui program se afişează conţinutul variabilelor s şi v pentru obiectele x şi y. se declară ( se iniţializează) explicit în afara clasei : Referirea (utilizarea) datelor membru statice se poate face astfel : Exemplul 1: Se consideră o clasă S care conţine o variabilă normală v şi o variabilă statică s. se definesc prin specificatorul static astfel: Datele membru statice. care sunt utilizate de către toate obiectele clasei.Datele membru statice. Mesajele afişate sunt următoarele: . Data de tip static este declarată în clasa S şi definită în afara acesteia.

Metodele statice. O funcţie membră statică are vizibilitatea limitată la fişierul în care a fost definită şi este independentă de instanţele (obiectele) clasei.Diferenţa între comportarea unei date membre de tip static şi a unei date normale este evidentă: după incrementarea variabilelor s şi v pentru obiectul x. folosind operatorul de rezoluţie. se definesc prin specificatorul static astfel: . Exemplul 2: În următorul exemplu. se numără câte Puncte sunt utlizate la un moment dat în program. obiectele x şi y văd aceeaşi valoare a variabilei statice s şi valori diferite ale variabilei normale v. utilizând ca dată comună pentru toate punctele ( întreaga clasă) Nr_Ob: 2) funcţii membre statice: O funcţie membră de tip static se declară în interiorul clasei şi se defineşte în interiorul clasei sau în afara acesteia.

. folosind operatorul de rezoluţie.Referirea ( utilizarea) metodelor statice se poate face astfel : Exemplu: Fie. Obs: Specificatorul static are în C++ două semnificaţii: aceea de vizibilitate restricţionată la nivelul fişierului în care sunt definite variabilele sau funcţiile şi aceea de alocare statică. de exemplu clasa Task care conţine o dată membră statică şi o funcţie membră statică: Apelul unei funcţii statice se poate face fie ca funcţie membră a unui obiect din clasa respectivă. aşa cum apare în primul apel din funcţia fs2(). adică obiectele există şi-şi menţin valorile lor de-a lungul execuţiei întregului program. fie prin specificarea clasei căreia îi aparţine.

_formali).funcţie independentă este prietenă a unei clase. de aceea ele pot fi specificate oriunde în cadrul descrierii clasei.float b=0). care nu este membră a clasei ci are numai acces la membrii de tip privat ai acelei clase. Complex(float a=0.p_reala<<x.2). }. în acest caz se spune că clasa y este prietenă a clasei x. O clasă este prietenă cu o altă clasă dacă ea are acces la datele membru ale acesteia. Exemplul 1: În acest exemplu funcţia Afişează va fi scoasă în afara clasei Complex şi va fi declarată ca funcţie prieten. // Clasă friend Obs: 1) O funcţie de tip friend este în esenţă o funcţie standard.toate funcţiile unei clase y sunt prietene ale altei clase x. 4) Modificatorii de acces nu au nicio influenţă asupra funcţiilor prieten. O funcţie. class Complex { float p_reala. // Funcţie friend globală b) friend Tip_funcţie Nume_clasă::Nume_funcţie(Listă_par. void Afiseaza(Complex x) { cout<<x. . Deci declararea unei funcţii prietene se poate face oriunde în cadrul declaraţiei clasei (atât in partea privata cat si in cea publica) si oferă funcţiei privilegiul de a avea acces la oricare dintre membri.// Funcţie friend membru c) friend Nume_clasă. } void main() { Complex a(1.Funcţii şi Clase Prietene (Friend) O funcţie este prietenă cu o clasă dacă are acces la datele membru private ale acelei clase. 3) Sunt posibile următoarele situaţii: . . Complex(Complex &x). indiferent de natura acesteia. respectiv o clasă prietenă se declară utilizând cuvântul friend astfel: a) friend Tip_funcţie Nume_funcţie ( Listă_parametri_formali ). . O funcţie prietenă poate fi o funcţie globală sau chiar o funcţie membru a altei clase. 2) Declaraţia unei funcţii prietene se poate face oriunde în cadrul declaraţiei clasei şi oferă funcţiei posibilitatea de a avea acces la oricare dintre membri. Afiseaza(a).funcţie membră a unei clase este prietena altei clase.p_imaginara. } .funcţie ce este prietena mai multor clase. p_imaginara. public: friend void Afiseaza(Complex x). Orice functie poate fi prietenă a unei clase.

La acelaşi rezultat se va ajunge folosind funcţia friend dif( ) a clasei X.Exemplul 2: //Să considerăm un tip definit de utilizator: #include <iostream.u*w. int q){u=k. int q){u=k.v*w.v=q.h> class X { int u.h> class X { int u. class Y { ……………… .v.} friend int dif(X w).v=q.3).dif(). Exemplu: class Y. #include <iostream. care nu mai este funcţie membră a acestei clase. Mai exact. folosind funcţia membră.3).v.u-w.} }. } Obs : Există două situaţii în declararea funcţiilor friend ale unei clase: a) se specifică faptul că funcţia membră a unei clase este funcţie friend a unei alte clase.v. public: X(int k. dif( ). cout<<"p="<<p. }. void main() { X z(6.} int dif(){return u*u-v*v. int p=z. public: X(int k. cout<<dif(z).} void main() { X z(6. // funcţie membră a clasei X ……………. // se predeclară clasa care conţine funcţii prietene ale unei alte clase X class X { …………… void F1(Y &a). }. se declară funcţia membră din prima clasă ca fiind de tip friend în a doua clasă. int dif(X w){return w. } Se obţine pentru p valoarea 27.

class X { ………………. class Y { ……………….suma numerelor complexe citite . friend X..constructori pentru initializare si copiere Sa se scrie un program care realizeaza urmatoarele: • citeste perechi de n numere care reprezinta fiecare.friend void X::F1(Y &a). b) se poate declara o întreagă clasă prietenă . //funcţie friend pentru clasa Y ……………….afisarea obiectelor complexe .numarul complex citit . partea reala si respectiv imaginara a unui numar complex • afiseaza: .accesul la partea reala si imaginara a obiectelor de tip complex .modul. void F1(Y &a). // se declară clasa X ca prietenă a clasei Y(toate funcţile membre // ale clasei X sunt funcţii friend pentru clasa Y) ………………. Exemplu: class Y.. }... ………………. }. Exemplul 3: Sa se defineasca tipul abstract de date complex care sa aiba functii membru pentru: . }. în care toate funcţiile membre ale sale sunt funcţii prietene ale unei alte clase.. argument .

.

Temă: Sa se defineasca o functie care ridica un numar complex la o putere intreaga pozitiva.este modulul numarului complex a – este argumentul nr. Complex . Aceasta se exprima prin relatia: ((r*cos(a)+i*sin(a)))n = rn * (cos(n*a)+i*sin(n*a)) unde: r . Un numar complex se poate ridica la o putere intreaga folosind formula lui MOIVRE.

a=z. } void main() { complex u.0) return 0.modul.14159265358979 class complex { double re. ridicat la putere:". else return (3*PI)/2. inline void complex::afis(){cout<<re<<"+i*"<<"("<<im<<")". u.0)return PI+a. void afis().0)return 2*PI+a. return a.int). } void cp(complex &z.0)return (PI/2).0. cout<<"\n Modulul nr. modul=sqrt(re*re+im*im). cp(u. double r_la_n=pow(r.init(-1.0&&im==0. r=z. u.modul. double arg(). } . if(im==0.} double complex::arg() { if(re==0. double a. if(im<0.-1).(double)n).0)return 0.afis().arg().#include <iostream. friend void cp(complex&.h> #include <conio. u.3). if(re==0. } }. double aa=n*a. cout<<"\n U=". z.double y) { re=x. double im.afis().0) if(im>0. im=y. public: double modul.0) if(re>0.re=r_la_n*cos(aa). cout<<"\n Nr. if(re<0.0.int n) { double r. else return PI.h> #define PI 3. double a=atan(im/re). z.im=r_la_n*sin(aa).U:"<<u. void init(double x.h> #include <math.

CRectangle rt. cout << rtb. height. height. care este prietena cu CRectangle.height*2. Aceasta este necesara pe motivul ca in cadrul functiei CResctangle se face referire la CSquare (ca parametru in functia convert()). int area (void) {return (width * height).h> class CSquare.h> class CRectangle { int width. int b) { width = a. } int main () { CSquare sqr. height = b.Exemplul 4: // functii prietene (friend) #include <iostream. data membru CSquare::side. De observat ca nici in declaratia lui duplicate() nici in utilizarea lui ulterioara din main() nu s-a considerat ca functia duplicate() ar fi membra a clasei CRectangle.height = rtparam. public: void set_values (int.convert(sqr). rtres. height = a. }. public: void set_side (int a) {side=a.area(). in acest caz. } int main () { CRectangle rt. } // clasa prietena (friend) #include <iostream. } CRectangle duplicate (CRectangle rtparam) { CRectangle rtres. . int).side.} void convert (CSquare a). return 0.width = rtparam.width*2. nu este posibil accesul asupra membrilor width si height ale diverselor obiecte de tip CRectangle. rt. cout << rt. class CSquare { private: int side. mai concret. void CRectangle::set_values (int a. S-a declarat clasa CRectangle ca prietena a clasei CSquare astfel incat CRectangle poate accesa mebrii protected si private ai clasei CSquare.3). public: int area (void) {return (width * height).set_side(4).} friend CRectangle duplicate (CRectangle). clasa CSquare nu ar fi fost vizibila din cadrul definitiei lui CRectangle compilatorul ar fi sesizat o eroare. }. Prima instructiune reprezinta un prototip vid al clasei CSquare. void CRectangle::convert (CSquare a) { width = a. Definitia lui CSquare este inclusa mai tarziu. class CRectangle { int width. } Din cadrul functiei duplicate. sqr.area(). rtres. rtb. care defineste latura patratului. iar daca nu ar fi fost instructiunea prototipului vid.side. rt.set_values (2. rtb = duplicate (rt). }.} friend class CRectangle. return (rtres).

unsigned Y) { return X * Y / 2. aceasata nu inseama ca A este prietena cu C. unsigned Y). De asemenea. Daca o clasa A este prietena cu o clasa B. unsigned long Calcul(unsigned X. }. } unsigned long AltaClasa::Calcul(unsigned X..Exemplul 5: // functii prietene (friend) class Point { friend unsigned long Calcul(unsigned X. .. unsigned Y). } Obs: Relatia de prietenie nu este tranzitiva. unsigned Y). iar clasa B este prietena cu o clasa C.. public: fiend unsigned long AltaClasa::Calcul(unsigned X.. { . proprietatea de prietenie nu se mosteneste in clasele derivate ! .

dacă obiectele din clasa D conţin toate atributele clasei A şi au acces la toate metodele acestei clase. fără să fie necesară reprogramarea sau recompilarea acestora. Derivarea claselor este legată de implementarea conceptului de moştenire. numită derivată (sau fiu) pornind de la clase existente.metodele noi şi cele redefinite au acces la toate atributele dobândite sau nou definite. Se spune că o clasă D moşteneşte o clasă A. Relaţia permite constituirea unei noi clase.D poate redefini metode ale clasei de bază. O clasă derivată moşteneşte de la una sau mai multe clase de bază toate caracteristicile acestora. . O clasă care asigură proprietăţi comune mai multor clase se defineşte ca o clasă de bază. Moştenirea simplă .D poate defini noi atribute şi metode.Clase derivate.în procesul de construire participă o singură clasă de bază multiplă .în procesul de construire participă mai multe clase de bază Derivarea permite definirea într-un mod simplu. Dacă D moşteneşte A. dar în plus: . Moşteniri Moştenirea reprezintă o relaţie între clase elementul definitoriu al OOP. . eficient şi flexibil a unor clase noi prin adăugarea unor funcţionalităţi claselor deja existente. cărora le adaugă alte caracteristici noi. atunci obiectele din D vor avea toate atributele şi acces la toate metodele lui A. specifice ei. denumite de bază (sau părinte). .

se consideră modificatorul public. În limbajul C++ este permisă moştenirea multiplă.Deci prin derivare sunt construite clasele din aproape în aproape. Aceasta are atributele x şi y. Prin derivare se construiesc ierarhii de clase. eventual. Pentru a defini o clasă fiu ca fiind derivată dintr-o clasă părinte (sau mai multe clase părinte). care desenează punctul pe ecran. care reprezintă coordonatele punctului în plan. . precedate de modificatori de acces – se pot folosi modificatorii public sau private. care intră prin derivare în componenţa lor. Obs: 1) Pentru fiecare clasă părinte se poate specifica un modificator de acces. se procedează astfel: class nume_clasa_fiu : lista_clase_părinte { descriere membri noi ai clasei fiu}. se preferă ca la derivare să se folosească modificatorul de acces public. Este inclusă o singură metodă. În lista claselor părinte se specifică numele claselor părinte. Clasele agregate de pe nivelul k preiau operanzii şi funcţiile membre ale claselor de pe nivelul k-1. implicit. De aceea acesta este modificatorul explicit. organizânduse pe niveluri de agregare. precum şi proprietăţile acestora. ci oferă posibilitatea extinderii prin adăugarea de noi clase derivate. 2) O clasă derivată are acces la membrii clasei părinte care au fost definiţi ca fiind publici sau protejaţi şi nu are acces la membrii privaţi. 3) Întrucât ierarhiile de clase nu sunt definitive. separate prin virgulă şi. Dacă nu se specifică niciun modificator de acces atunci. Exemplul 1: Fie clasa Punct care implementează entitatea punct geometric. deci din clasa fiu se pot deriva alte clase noi.

primit ca parametru. class cerc: punct { float raza. Funcţia Arie calculează aria din interiorul cercului. fiind nou definită. dat ca parametru. deci un obiect de tipul Cerc va avea ca membri coordonatele x. care va fi folosită pentru desenarea cercului). definită pentru calculul distanţei dintre punctul curent şi punctul p. Ca urmare clasa Cerc poate fi derivată din clasa Punct. { //corpul nu este descris in acest exemplu } Fie clasa Cerc care implementează entitatea geometrică cerc. lucru impus de codul diferit pe care trebuie să-l aibă pentru desenarea obiectelor din această clasă (cerc sau altă figură). }.class punct { int x. void punct::deseneaza(). Funcţia Desenează este redeclarată de clasa Cerc. pentrud esenarea cercului. este accesibilă şi pentru obiectele Cerc şi va calcula distanţa dintre centrul cercului şi un alt punct. }. public: void deseneaza(). void cerc::deseneaza() { //corpul nu este descris in acest exemplu } Clasa cerc o să aibă ca membri atributele x.y. Clasa Cerc moşteneşte clasa Punct. care va fi folosită pentru desenarea centrului cercului) şi deseneazã (nou definită. . Funcţia Distanţa. y şi raza şi metodele deseneazã (moştenită de la clasa Punct. Aceasta este descrisă prin coordonatele centrului cercului şi raza sa.y moştenite şi ca atribut propriu Raza. adăugând un nou atribut (raza) şi o nouă metodă. public: void deseneaza().

public: Angajat(). un şef de secţie (administator) este un angajat (deci sunt necesare toate datele care descriu această calitate) dar mai sunt necesare şi alte informaţii. Angajat(Angajat& r). la care adaugă alte date: . } Diferite categorii de angajaţi necesită date suplimentare faţă de cele definite în clasa Angajat. De aceea. float sal). corespunzătoare postului pe care îl deţin. Angajat::display(){ cout << nume << “ ” << salariu << endl. de exemplu precizare secţiei pe care o conduce. clasa Administator trebuie să includă un obiect de tipul Angajat.Moştenirea simplă Exemplul 2: Se consideră un program care descrie organizarea personalului unei instituţii fără folosirea claselor derivate. De exemplu. void display(). float salariu. O clasă numită Angajat deţine date şi funcţii referitoare la un angajat al instituţiei: class Angajat{ char *nume. Angajat(char *n. }.

De exemplu: class X : public Y. specificatorii de acces pot să difere (pot fi oricare din public. O clasă de bază se numeşte bază directă dacă ea este menţionată în lista de clase de bază. public: void display(). int sectie.public W { // corpul clasei }. Un administrator este un angajat. Moştenirea multiplă: Este posibilă moştenirea din mai multe clase de bază. …. de aceea clasa Administrator se poate construi prin derivare din clasa Angajat astfel: class Administrator : public Angajat { int sectie. public Z. . Într-o clasă derivată se moştenesc atât membrii bazelor directe cât şi membrii bazelor indirecte. protected). Evident. public: void display(). }. O clasă de bază se numeşte bază indirectă dacă nu este bază directă dar este clasă de bază pentru una din clasele menţionate în lista claselor de bază. private. } Această operaţie care se poate reprezenta schematic prin indicarea derivării cu o săgeată (arc direcţionat) de la clasa derivată la clasa de bază.class Administrator{ Angajat ang.

din această clasă de bază. consultant. public Administrator { /* … */ }. direct sau indirect. class Consultant : public Temporar. prin adăugarea câte unei clase pentru fiecare categorie de personal.De exemplu. Modul în care aceste clase se derivează din anumite clase de bază evidenţiază relaţiile de ierarhie între categoriile pe care le reprezintă: class Personal { /* … */ }. iar toate celelalte moştenesc. class Angajat : public Personal{ /* …*/ }. Această organizare a claselor se poate descrie printr-un graf aciclic direcţionat astfel: Clasa (nodul) din vârful ierarhiei reprezintă categoria cea mai generală a ierarhiei. . class Administrator : public Angajat { /* … */ }. fiind clase mai specializate. secretară. se completează programul de descriere a organizării personalului unei instituţii şi pentru alte categorii de personal (personal temporar. class Secretara : public Angajat { /* … */ }. director). class Director : public Administrator { /* … */ }. class Temporar : public Personal { /* … */}.

cout << "setc din baza\n". a este private } void setb(int y) { . class Derived : public Base { int d. • Datele de tip protected în clasa de bază sunt moştenite protected în clasa derivată. Moştenirea de tip public a clasei de bază Dacă specificatorul de acces din declaraţia unei clase derivate este public. // eroare. nu se modifică tipul datelor în clasa de bază. private) din declaraţia clasei derivate. O regulă generală este că.} }. protected: int b. deci pot fi accesate numai de funcţiile membre şi friend ale clasei derivate. public: void seta(int x) { a = x. cout << "setb din baza\n". indiferent de specificatorul de acces declarat la derivare.} void setc(int z){c = z. void seta(int x){a = x.} void setb(int y){b = y.Controlul accesului la membrii clasei de bază Accesul la membrii clasei de bază moşteniţi în clasa derivată este controlat de specificatorul de acces (public. protected. atunci: • Datele de tip public ale clasei de bază sunt moştenite ca date de tip public în clasa derivată şi deci pot fi accesate din orice punct al domeniului de definiţie al clasei derivate. prin derivare. cout << "seta din baza\n". datele de tip private în clasa de bază nu pot fi accesate dintr-o clasă derivată. public: int c. Exemplu: Diverse situaţii de acces la membrii clasei de bază din clasa derivată atunci când specificatorul de acces este public: class Base { int a. O altă regulă generală este că.

dacă acestea sunt specificate în declaraţia clasei. cout << "setb din derivata\n". Bineînţeles. Moştenirea multiplă: O clasă poate avea mai multe clase de bază directe. În exemplul următor este prezentată clasa Derived care moşteneşte două clase de bază. membrii de tip private în clasa de bază nu pot fi accesaţi din clasa derivată. Moştenirea de tip private a clasei de bază Dacă specificatorul de acces din declaraţia clasei derivate este private. membrii de tip private în clasa de bază nu pot fi accesaţi din clasa derivată. } Moştenirea de tip protected a clasei de bază Dacă specificatorul de acces din declaraţia clasei derivate este protected. atunci toţi membrii de tip public şi protected din clasa de bază devin membri protected în clasa derivată.b = y. atunci toţi membrii de tip public şi protected din clasa de bază devin membri de tip private în clasa derivată şi pot fi accesaţi numai din funcţiile membre şi friend ale clasei derivate. } }. Base1 şi Base2. Bineînţeles. . } void setc(int z) { c = z.

class Base2{ protected: int y. } }. } În funcţia fbm() este creat obiectul Derived. y = j. Derived::Derived(int i. } }. public: Derived (int i.getx() << " "<< obd. Base2(j){ cout << "Constructor derivata\n". care iniţializează datele membre x şi y.} ~Base2() { cout <<"Destructor baza 2\n". public: Base1(int i) { cout << "Constructor baza 1\n".class Base1 { protected: int x. int j): Base1(i). int j).gety() << endl. public Base2 { int d. La execuţia funcţiei fbm() se afişează următoarele mesaje. } void fbm(){ Derived obd(3. ~Derived(){ cout << "Destructor derivata\n". public: Base2(int j){ cout << "Constructor baza 2\n". } int gety() { return y. x = i. class Derived : public Base1. } ~Base1() { cout <<"Destructor baza 1\n".} }. cout << obd. obd de clasă Constructorul clasei Derived transferă câte un argument constructorilor fiecărei clase de bază.4). care indică ordinea în care sunt apelaţi constructorii şi destructorii clasei derivate şi ai claselor de bază: Constructor baza 1 Constructor baza 2 Constructor derivata 3 4 Destructor derivata Destructor baza 2 Destructor baza 1 . } int getx(){return x.

Deşi moştenirea prezintă foarte multe avantaje. există şi o serie de costuri de care trebuie să se ţină seama în momentul proiectării ierarhiilor de clase: • viteza de execuţie – este influenţată prin prisma faptului că metodele moştenite. • consistenţa interfeţei – când două sau mai multe clase sunt derivate din aceeaşi clasă părinte. efortul de dezvoltare al unui produs nou este diminuat prin utilizarea de librării cu funcţionalitate deja implementată. sunt de regulă mai lente decât codul specializat. în acest fel. se asigură faptul că comportamentul moştenit este acelaşi pentru toate clasele. • complexitatea programelor – o ierarhie de clase introduce un anumit grad de complexitate în sistem. utilizatorii îl folosesc. el trebuie doar apelat în noul context. timpul de dezvoltare este concentrat pe înţelegerea elementelor specifice ale sistemului. codul respectiv nu trebuie rescris. dar librăria trebuie adăugată în totalitatea sa. o anumită funcţionalitate este scrisă doar la nivelul unei clase şi apoi moştenită şi utilizată în toate clasele derivate. . • dezvoltarea rapidă de prototipuri – atunci când sistemul software este construit folosindu-se componente reutilizabile. cu atât sistemul care foloseşte această ierarhie este mai complex. iar a doua versiune a sistemului este realizată pe baza experienţei acumulată cu prima şi a feedbackului de la utilizatori. nivelurile de abstractizare mai multe. însă afectarea vitezei de execuţie este compensată cu creşterea vitezei de dezvoltare. astfel se construiesc versiuni de sistem. care au un caracter mai general. numite prototipuri. care pun accent pe aspectele critice ale sistemului. Un prototip este dezvoltat. decât programele care folosesc cod specializat. deoarece nu toate componentele dintr-o librărie sunt folosite în proiect. o altă implicaţie este legată de fiabilitatea codului. • componentele software – moştenirea dă posibilitatea programatorilor să construiască componente software reutilizabile şi gruparea lor în biblioteci.Avantajele folosirii moştenirii: • reutilizabilitatea – când o funcţionalitate este moştenită din altă clasă. deoarece prin moştenire. • dimensiunea programelor – este influenţată în sensul că devine mai mare în cazul programelor care folosesc librării de componente. cu cât ierarhia este mai mare.