OBIECTE I FUNCII
Atunci, cnd o clas se definete n cadrul unei alte clase, se spune c sunt clase imbricate.
O clas imbricat este valid doar n interiorul clasei ce o conine. Avnd n vedere posibilitile oferite prin mecanismul de motenire, astfel de clase se utilizeaz mai rar. Exemplu: #include <iostream.h> class vector { private: class punct { public: float x,y; void afis(); void in_punct(float x1=0,float y1=0); }o,v; public: void in_vect(float x1,float y1,float x2,float y2) { o.in_punct(x1,y1); v.in_punct(x2,y2); } void afis(); }; void vector::punct::in_punct(float x1,float y1) {x=x1;y=y1;} void vector::punct::afis() {cout<<"\n x="<<x<<" y="<<y;} void vector::afis() {o.afis();v.afis();} void main() { vector vect; vect.in_vect(35,45,55,65); vect.afis(); }
O clas poate conine ca membri, instanieri ale altor clase (obiecte imbricate) cu o remarc asupra faptului, c nu se permit iniializri n cadrul declarrii acestor clase. Exist o convenie asupra apelului constructorilor obiectelor membre. S considerm urmtoarea situaie: A este o clas care conine un obiect imbricat de tip "TB" care, la rndul su, conine un membru M de tip pointer. S presupunem c prin constructorul clasei TB se aloc dinamic o zon de memorie la care va pointa M. Dac constructorul TB nu a fost apelat, nu se accept ca prin constructorul A s se iniializeze zona la care pointeaz M, pentru c:
a) pointerul M va lua o valoare aleatoare; b) se pot distruge informaii din memorie. n C++ exist un mecanism convenabil n acest sens.
67
S considerm clasa A care conine ca membri, obiecte ale altor clase A1,A2,..,An. Pot fi ntlnite urmtoarele situaii: 1) clasele Ak nu au constructori (k=1,...,n) (n acest caz, nu vor exista deosebiri ntre constructorii clasei A i un constructor al unei clase ce nu conine obiecte membre imbricate); 2) clasele Ak (k=1,...,n) nu au un constructor implicit i nici un constructor cu parametri cu valori implicite (ntr-o astfel de situaie este obligatoriu a se specifica explicit numele fiecrui obiect urmat de lista parametrilor actuali): iden_obiect(list_param_actuali) 3) clasele Ak (k=1,...,n) au un constructor implicit sau unul cu toi parametrii cu valori implicite (se poate proceda ca n primul caz, apelndu-se implicit constructorii respectivi, sau ca n a doua situaie, specificndu-se explicit doar parametrii solicitai). ntotdeauna apelul constructorilor obiectelor imbricate se va efectua nainte de executarea constructorilor clasei A. Pentru a preciza ordinea de apelare a constructorilor obiectelor imbricate este necesar a se meniona aceasta n definiia constructorului clasei A i nu n declaraia sa. Unele aspecte cu privire la utilizarea destructorilor n clasele imbricate: S presupunem c avem urmtoarea situaie: class U { class imb_1 ob1; class imb_2 ob2; . public: U(int); . ~U( ); }ob; #n acest caz, apelul destructorilor pentru obiectele ob1 i ob2 se va face dup execuia "~U( )". Dac presupunem c avem o adresare indirect de forma "pointer la obiect imbricat", n loc de obiecte imbricate, atunci va fi necesar att alocarea dinamic ct i eliberarea explicit a zonei de memorie necesar pentru obiectele spre care vor pointa membrii respectivi. Aceste operaii se execut prin constructorul / destructorul clasei care conine obiectele imbricate.
O atenie sporit trebuie acordat mecanismului de copiere bit cu bit a obiectelor. Pot aprea erori nu la copiere, ci la apelul destructorului care este obligat s elibereze de mai multe ori aceeai zon de memorie. Exemplu: class punct { int u,v; public: punct(int a=0, int b=0) {u=a;v=b;} punct(int a, int b) {u=a; v=b;} ~punct( ); }; class vector { punct og,vf;
68
public: vector(int,int); vector(int,int,int,int); ~vector( ); }; class triunghi { punct *p1, *p2, *p3; public: triunghi(int,int,int,int,int,int); ~triunghi( ); delete p1; delete p2; delete p3; }; vector::vector(int a, int b):vf(a,b){ } vector::vector(int a1,int b1,int a2,int b2):og(a1,b1),vf(a2,b2){ } triunghi:: triunghi(int a1, int b1, int a2, int b2, int a3, int b3) { p1=new punct(a1,b1); p2=new punct(a2,b2); p3=new punct(a3,b3); } Exist deosebiri sintactice / semantice ntre acest exemplu i ultimul program din L5 ? Cnd o clas este declarat ntr-o funcie, ea este cunoscut doar n interiorul acelei funcii i nu n exteriorul ei. O astfel de clas se numete clas local. Dac se utilizeaz o astfel de clas, trebuie s inem cont de urmtoarele: a) toate funciile membre trebuie definite n interiorul declaraiei clasei; b) clasa local nu are acces la variabilele locale ale funciei n care ea este declarat (cu excepia variabilelor locale declarate static n interiorul funciei); c) ntr-o clas local nu se poate declara nici o variabil de tip static, de aceea astfel de clase sunt mai puin uzuale n C++. Exemplu: # include <iostream.h> void gama( ); main( ) { // n aceast funcie nu este cunoscut clasa LOC gama( ); return 0; }
void gama( ) { // clasa LOC definit n interiorul funciei gama n interiorul creia este cunoscut. class LOC { int k; public: void scrie(int m){k=m;} int af( ){return k;}
69
}ob; ob.scrie(30); cout<<ob.af( ); } Transmiterea obiectelor ctre funcii se realizeaz asemntor cu transmiterea oricrui alt tip de parametru. Se utilizeaz mecanismul "apel prin valoare", adic prin copiere. Efectuarea unei copii nseamn crearea unui obiect nou. Se pune ntrebarea, dac efectuarea unei copii, solicit apelul unui constructor al obiectului la crearea copiei sale i dac se execut destructorul la distrugerea copiei respective.
S urmrim urmtoarea secven: class A { int k; public: A(int p); ~A( ); void init1(int p){k=p;} int revin( ){return k;} }; A::A(int p) { k=p; cout<<k<< \n"; // creere } A::~A( ) { cout<<k<<"\n";} // distrugere void g(A ob); void main( ) { A ob_1(13); g(ob_1); cout<<ob_1.revin( )<<"\n"; // ce reprezint? } void g(A ob) { ob.init1(12); cout<<ob.revin( ); // ce reprezint? } Analiznd ieirea acestui program, se observ c se apeleaz de dou ori funcia destructor i o singur dat funcia constructor. Conform ieirii, funcia constructor nu este apelat, cnd copia lui "ob_1" din main() este transmis lui "ob" din g( ). Cnd se transmite un obiect ctre o funcie, se vrea starea curent a obiectului. Dac ar fi apelat constructorul pentru a crea copia obiectului respectiv, s-ar produce iniializarea noului obiect creat i ar putea fi modificat. De aceea, constructorul nu va fi apelat la crearea copiei. Copia, la fel ca o variabil local obinuit, se poate distruge la finele execuiei funciei. Copia, care se creaz la nivel de bit, poate realiza diverse operaii, ceea ce impune apelul destructorului pentru nlturarea sa. O funcie poate returna un obiect n punctul apelant.
Exemplu:
70
. class A { int k; public: void init(int m){k=m;} int afis( ){return k;} }; A g( ); // returneaz obiect de tipul A void main( ) { A ob; Ob = g( ); cout<<ob.afis( )<<"\n"; } A g( ) { A a; a.init(11); return a; } Cnd un obiect este returnat de o funcie, este creat automat un obiect temporar, care conine valoarea returnat. Acesta este obiectul care este returnat de funcie. Dup returnare, obiectul respectiv este distrus. Distrugerea obiectului temporar poate avea i efecte negative. Dac obiectul, care a fost returnat, are un destructor care elibereaz zona de memorie alocat dinamic, aceea memorie va fi eliberat, chiar dac obiectul, care primete valoarea returnat, nc o mai folosete Prevenirea unei astfel de situaii se face cu ajutorul suprancrcrii vezi L.10 operatorului de atribuire i definirea unui constructor de copiere. Dac dou obiecte sunt de acelai tip, se poate realiza o atribuire ntre ele. Aceasta nseamn c datele obiectului din membru drept al operatorului de atribuire vor fi copiate, n mod corespunztor, n datele obiectului din membru stng. Exemplu; class A { int k; public: void init(int m){k=m;} .. }; void main( ) { A ob1, ob2; ob1.init(66); ob2=ob1; // copiere bit cu bit a obiectului ob1 n obiectul ob2} . }
Aplicaie.
71
S se calculeze aria unor figuri geometrice plane. #include<iostream.h> #include<conio.h> #include<stdlib.h> #include<math.h> #define EROARE -1 #define CERC 0 #define PATRAT 1 #define DREPTUNGHI 2 #define TRIUNGHI 3 #define PI 3.141 class GEOM { private: union { public: double raza,lp,ld[2],lt[3]; }fig; public: int tip; GEOM(); double aria(GEOM*p); int cit_fig(GEOM*p); }; double GEOM::aria(GEOM*p) { double sp,a,b,c; switch(p->tip){ case CERC:return PI*(p->fig.raza)* (p->fig.raza); case PATRAT: return p->fig.lp*p->fig.lp; case DREPTUNGHI: return p->fig.ld[0]* p->fig.ld[1]; case TRIUNGHI: {sp=(p->fig.lt[0]+p->fig.lt[1]+ p->fig.lt[2])/2; cout<<"Semiperimetrul este:"<<sp; if((a=sp-p->fig.lt[0])>0&& (b=sp-p->fig.lt[1])>0&& (c=sp-p->fig.lt[2])>0) return sqrt(sp*a*b*c); else cout<<"Nu se formeaza triunghi!"; return 0;} default:return 0; } } int GEOM:: cit_fig(GEOM*p) { switch(p->tip){ case CERC:for(;;){ cout<<"raza="; cin>>p->fig.raza; if(p->fig.raza<=0)cout<<"Citire raza eronata!"; else return 1; }
72
case PATRAT:for(;;){ cout<<"Latura patratului="; cin>>p->fig.lp; if(p->fig.lp<=0)cout<<"\n citire lp eronata!"; else return 1;} case DREPTUNGHI:for(;;){ cout<<"Dimensiunile dreptunghi:\n"; cin>>p->fig.ld[0]; cin>>p->fig.ld[1]; if(p->fig.ld[0]<=0|| p->fig.ld[1]<=0) cout<<"citire ld eronata!"; else return 1;} case TRIUNGHI:for(;;){ cout<<"Laturile triunghiului:\n"; cin>>p->fig.lt[0]; cin>>p->fig.lt[1]; cin>>p->fig.lt[2]; if(p->fig.lt[0]<=0||p->fig.lt[1]<=0 ||p->fig.lt[2]<=0) cout<<"citire lt eronata!"; else return 1;} default:return 0; } } void main(void) { char c; GEOM f; for(;;){ cout<<"Tastati o tasta: C D P T\n"; cin>>c; switch(c){ case 'C':{f.tip=CERC; break;} case 'D':{f.tip=DREPTUNGHI; break;} case 'P':{f.tip=PATRAT; break;} case 'T':{f.tip=TRIUNGHI; break; } default:cout<<"S-a tastat alt caracter!"; } if(f.tip!=EROARE)break;} if(f.cit_fig(&f)==0)exit (1); if(f.aria(&f)==0)exit(1); cout<<"ARIA FIGURII:\n"<<f.aria(&f); } Ce se ntmpl cu constructorii i destructorii n aceast implementare? Este o eroare? De ce? S se realizeze o nou implementare n C++ pentru o astfel de aplicaie care s conin imbricate. clase
73
Aplicaie: S se selecteze un anumit numr de elemente dintr-un ir de numere ntregi, s se ordoneze subirul extras i s se calculeze norma acestuia din urm. Utilizai o clas local n funcia de afiare a normei. #include<iostream.h> #include<conio.h> #include<math.h> #define MAX 50 void afis(double); void sort(int a[],int m) { int*p,i,r,q=1; while (q) { q=0; p=&a[1]; for(i=1;i<m;i++){ if(*p>*(p+1)){ r=*p; *p=*(p+1); *(p+1)=r; q=1;} p++;} } } void suma_patrat(int a[],int m) { double nv=0; for(int i=1;i<m+1;i++) nv+=(a[i]*a[i]); afis(nv); } void afis(double t) { class N { double u,t; public: void scrie(){cout<<"Norma vectorului extras:";} void af(double t){u=sqrt(t);cout<<u;} }v; v.af(t); } void main(void) { int n,m,i,a[MAX]; clrscr(); cout<<"Dimensiunea vector:"; cin>>n; cout<<"Dati elementele vector:"; for (i=1;i<n+1;i++){
74
cin>>a[i]; cout<<"";} cout<<"Numar elemente sortate:"; cin>>m; sort(a,m); cout<<"Elem.sortate:\n"; for(i=1;i<m+1;i++){ cout<<a[i]; cout<<"\n";}suma_patrat(a,m);} Tema propus 1. S se scrie un program, folosind clase imbricate, pentru conversia numerelor naturale nenule, din baza zece n bazele 2,8,16 (eventual i alte baze). 2. S se scrie programe n care s se pun n eviden modul de transmitere a unor obiecte ctre funcii, fcndu-se o analiz corespunztoare asupra apelului funciilor constructor i destructor. 3. Folosind clase locale, s se scrie un program care s reflecte situaia la o sesiune de examene a unei grupe de studeni.
4.
Realizai un program pentru parcurgerea unui arbore n care nodurile s fie de tip class.
75