Sunteți pe pagina 1din 18

L3 CREAREA, INIIALIZAREA I DISTRUGEREA OBIECTELOR UNEI CLASE

Reamintim, c odat cu implementarea unui tip de date, este posibil definirea unor variabile de acest tip. O variabil, avnd ca tip o clas definit de utilizator, este un obiect. Datele membre ale unei clase pot fi arbitrare, dar nu obiecte ale clasei respective. n particular, datele membre pot fi obiecte ale unei alte clase. Obiectele trebuie s fie iniializate. Iniializarea lor se poate realiza:

a) cu ajutorul listelor de iniializare Exemplu: struct fiier { FILE *fp; char *nume; int lung_art; };

fiier fd={stdin,"file.bin",80}; //iniializare corect fiier hd={stdout}; //iniializare corect,restul membrilor lund n mod implicit valoarea 0 n lista de iniializare nu se accept mai multe valori dect numrul cmpurilor prezente n struct. Exemplu: class fiier { public: FILE *fp; char *nume; int lung_art; }; fiier fd={stdin,"file.bin",80}; //iniializare corect S observm c, n class, iniializarea variabilei fd se poate realiza, numai dac membrii clasei sunt declarai n seciunea public. n plus, pentru astfel de obiecte, nu vor exista constructori, funcii virtuale, nu pot proveni din clase derivate [aceste concepte vor fi precizate pe parcurs].

b) cu ajutorul constructorilor. Obiectele se pot iniializa n momentul instanierii (creerii) lor. n acest sens se utilizeaz unele funcii membre speciale ale clasei numite constructori.

Constructorii sunt utili n: -iniializarea obiectelor; -alocarea dinamic de memorie; -deschideri de fiiere etc. 24

Constructorul este o funcie membr a unei clase care se apeleaz automat la crearea fiecrui obiect, inclusiv pentru obiecte temporare, i se identific cu acelai nume ca i clasa din care face parte. Funcia constructor nu conine tip de valoare returnat (nu retuneaz nici o valoare) i nu poate fi funcie virtual [vezi L9]. Ca orice funcie membr, un constructor admite mai multe prototipuri. Listele de parametri ale diferitelor variante de implementare trebuie s fie diferite pentru a putea s fie identificate corect de ctre compilator.

Parametrii unui constructor pot fi de orice tip, cu excepia tipului definit de clasa ce conine acest constructor. Unii dintre aceti parametri pot fi pointeri sau referine la o instan a clasei respective. Exemplu: class A { .. A(A a); // incorect .. A(A*a); // corect, parametrul este un pointer la tipul definit prin clasa A . A(A& a); // corect, parametrul este o referin la un obiect al clasei A } Un constructor poate fi: a) implicit - fr parametri (definit de utilizator) care poate conine n corpul su orice fel de instruciuni valide. Un constructor inline nu poate conine instruciuni cu caracter repetitiv. - generat de compilator (cnd nu exist constructori definii de utilizator n clasa respectiv). Corpul unui astfel de constructor nu conine nici o instruciune. b) cu parametri care iau valori n mod implicit c) cu parametri care nu iau valori implicite d) cu conversie de tip e) de copiere - definit de utilizator - generat implicit de compilator La iniializare, dac n clasa respectiv exist mai muli constructori, se va ine cont, la apelul lor, de lista de parametri (numrul i tipul lor) solicitat de instanierea respectiv. Dac exist un singur constructor, atunci se aplic aceleai reguli ca la apelul funciilor din limbajul C. Ca atare, tipurile parametrilor efectivi (expresiile care vor produce iniializarea) se vor converti la tipurile parametrilor formali corespunztori ai constructorului.

Transmiterea parametrilor actuali ctre funcia constructor, utili pentru iniializarea unui obiect, se realizeaz n momentul creerii acestuia. ident_clas ident_obiect (list); Lista conine una sau mai multe expresii separate prin virgul. Dac nu se produce o iniializare a obiectului, lista i parantezele lipsesc. Exemplu: class data_c{ int zi,luna,an; public: 25

// iniializare, constructor fr parametri (implicit) data_c( ) { zi=15; luna=1; an=1999; } //iniializare, constructor cu parametri tip int, ultimul cu valoare implicit data_c(int z, int l, int a=1999) { zi=z; luna=l; an=a; } //iniializare, constructor care conine ca param. denumirea lunii data_c(int z, char*den_l, int a); .. }; Exemple de instanieri ale clasei data_c. 1) data_c d1; se apeleaz constructorul fr parametri i se obine pentru obiectul d1: zi=15,luna=1,an=1999. 2) data_c d2(12,4,1999); se apeleaz constructorul cu toi parametri de tip int i se obine pentru obiectul d2: zi=12, luna=4, an=1999. 3) data_c d3(12,4); se apeleaz acelai constructor ca la d2 obinnd acelai rezultat. 4) data_c d4(18, "ianuarie", 1999); se apeleaz ultimul constructor i se obine pentru obiectul d4: zi=18, luna=ianuarie, an=1999. 5) data_c *pd=new data_c(18,1); se apeleaz constructorul la fel ca pentru obiectele d2 i d3 n vederea iniializrii, n plus, se va realiza i o alocare de memorie heap pentru obiectul creat, iar pd are ca valoare adresa de nceput a zonei alocate obiectului respectiv. Datele membre ale unui obiect (de durat local sau dinamic) dac nu au fost iniializate, vor primi valori necontrolabile (imprevizibile). Dac ntr-o clas este prezent un constructor implicit, atunci nu se mai poate defini pentru clasa respectiv un constructor cu toi parametrii implicii, pentru c s-ar produce ambiguiti la instanierea obiectelor. Prezena constructorilor ntr-o clas nu este obligatorie, deoarece compilatorul C++ genereaz n mod automat un constructor fr parametri (constructor implicit), care are rolul doar de alocare pentru instana respectiv. Spre deosebire de alte funcii membre ale unei clase, adresa unui constructor nu se poate determina.

Constructorii definii de programator sunt necesari atunci cnd se dorete iniializarea obiectelor odat cu crearea lor. Dac o clas are cel puin un constructor i nici unul dintre ei nu este implicit, atunci nu se pot instania obiecte neiniializate, deoarece compilatorul nu genereaz constructor implicit ntr-o astfel de situaie. Exemplu: class complex { double real; double imag; 26

public: complex(double x, double y) //iniializarea se realizeaz prin asignri { real=x; imag=y; } }; Se pot instania numai obiecte de tip complex cu ambii membri iniializai. Exemplu: complex z(4,-2); complex u; // este incorect pentru c nu exist constructor implicit. ntr-un constructor pot fi apelate i alte funcii care pot fi, sau nu, funcii membre ale clasei respective. Exemplu: class Pers{ private: char nume[25]; long salariu; public: Pers(char *,long); // constructor cu parametri neimplicii long Sal( ) {return salariu;} //funcie membr }; Pers::Pers(char*n,long l){ strcpy(nume,n); // funcie ne-membr a clasei Pers salariu=l; } Constructorul garanteaz iniializarea corect a obiectului nainte de utilizarea efectiv a sa. El ofer o garanie, n plus, asupra faptului, c iniializarea unui obiect se va efectua o singur dat. Apelul constructorului se realizeaz n momentul declarrii unui obiect.

Exemplu: Pers p=Pers("Goga",1987345); sau Pers p("Goga",1987345); Dac un constructor are un singur parametru, iniializarea se realizeaz n mod obinuit. # include <iostream.h> class U { int k; public: U(int c){k=c;} //constructor cu un singur parametru int afis() {return k;} }; main( ) 27

Exemplu:

{ U ob=23; // se transmite valoarea 23 lui c cout <<ob.afis( ); // se afieaz 23 return 0; } Un constructor poate avea toi parametrii cu valori implicite. Exemplu: class Pers{ private: char nume[23]; long salariu; public: //constructor care are parametrii cu valori implicite Pers(char*p="Nume", long s=1987345); . }; Pers::Pers(char*n,long s) { strcpy(nume,n); salariu=s; }

Valorile parametrilor implicii sunt prezente n declaraia constructorului i nu n cadrul implementrii sale. Apelul unui astfel de constructor se poate realiza sub una din urmtoarele forme: Pers p1; Pers p2("Goga"); Pers p3("Goga",1987345); Pers p4=Pers( ); Varianta constructorului cu parametrii ce iau valori implicite se poate folosi atunci cnd constructorul nu necesit argumente (parametri efectivi) i el se va identifica cu un constructor implicit. Apelul su se va face sub forma unei simple declaraii. n mod normal, constructorii se afl n zona public, mai rar n seciunea "private". Dac un constructor este declarat n seciunea private, el poate fi apelat doar de o alt funcie membr sau funcie friend. Prin urmare, numai funciile membre i friend pot crea obiecte avnd constructori privai. O astfel de clas se mai numete i clas privat.

Declaraia obiectelor a cror list de iniializare se reduce la un parametru se poate scrie ntrun format asemntor cu cel de la iniializarea variabilelor simple. ident_clas ident_obiect = expresie; n aceast situaie se apeleaz un constructor n care primul parametru ia ca valoare, valoarea "expresie" iar ceilali parametri, dac exist, au valori implicite. Exemplu: # include <iostream.h> class complex { double real; double imag; public: complex(double x, double y=10) 28

{ real=x; imag=y; } void afis( ); }; void complex::afis( ) { if (imag<0) cout<<real<<imag<<"*i\n"; else cout<<real<<"+"<<imag<<"*i\n"; } void main ( ) { double r=3.2; complex z1(2.56); z1.afis(); complex z2=3*r-1;// se atribuie o expresie obiectului z2 z2.afis( ); complex z3(18.5,-9); z3.afis( ); } Dac se dorete a se instania obiecte att iniializate ct i neiniializate atunci se va folosi un constructor implicit (fr parametri), care va fi apelat la instanierea obiectelor neiniializate. Pentru a nu se ajunge la obiecte, care nu se pot identifica i deci iniializa, nu trebuie apelat un constructor n interiorul unui alt constructor. n cazul n care o clas are obiecte membre, care au constructori, acetia se apeleaz nainte de a se apela constructorul clasei respective.

Constructorii cu conversie de tip au primul parametru de tip diferit de cel al clasei apelante. Dac mai exist i ali parametri, ei trebuie s aib valori implicite. Exemplu: class pers { ........................ public: pers(char*nume, long s=1987345) ............................ } main( ) { .................... pers t=Horia; // se convertete char* la pers ................... } Constructorul de copiere este un constructor special care permite copierea de obiecte. Prin prezena lui se renun la copierea pe bii. El are ca prim parametru o referin ctre clasa din care face parte ca funcie membr, iar dac mai exist i ali parametri, acetia iau valori n mod implicit. Sintaxa: id_clas (const id_clas &); El este util doar pentru iniializri. 29

Un constructor poate fi apelat explicit n situaii de genul urmtor: id_clas ob= id_clas (list par_actuali);

n acest caz este apelat constructorul clasei id_clas pentru a creea un obiect anonoim care va fi copiat n ob. Exemplu: # include <iostream.h> class complex { double real; double imag; public: complex(double x=10, double y=10) { real=x; imag=y; } complex(const complex &u) // constructor de copiere { real=u.real; imag=u.imag; } void afis( ); }; void complex::afis( ) { if (imag<0) cout<<real<<imag<<"*i\n"; else cout<<real<<"+"<<imag<<"*i\n"; } void main ( ) { complex z1(2.56,101.99); z1.afis(); complex z2=z1;// se aplica constructorul de copiere z2.afis( ); complex z3(z1); // idem z3.afis( ); //crearea unui obiect anonim care este copiat in z complex z=complex(1.2,3.5); z.afis( ); } Exemplu: class tablou_2D{ int*p; int dim; public: tablou_2D(int d){ p=new int [d]; if(!p)exit(1); 30

dim=d; } ~tablou_2D(){delete[]p;} //constructor de copiere tablou_2D(const tablou_2D &t); void asign(int i,int j) { if(i>=0&&i<dim)p[i]=j; } int ret(int i){return p[i]; } }; tablou_2D::tablou_2D(const tablou_2D &t) { int i; p=new int [t.dim]; if(!p)exit(1); for(i=0;i<t.dim;i++) p[i]=t.p[i]; } main() { tablou_2D X(8); int i; for(i=0;i<8;i++) X.asign(i,i); for(i=7;i>=0;i--) cout<<X.ret(i); cout<<"\n"; tablou_2D y=X; //se apeleaza constructorul de copiere for(i=0;i<8;i++) cout<<X.ret(i); return 0; }

Dac se solicit un constructor de copiere i nu a fost definit de utilizator, atunci compilatorul va genera unul implicit. Elementele membre sunt copiate de la obiectul surs la obiectul destinaie, n ordinea declarrii lor n clas.

Iniializarea elementelor membre ale unui obiect se poate face printr-un constructor care folosete o list de iniializare. Exemplu: class complex{ double re,im; public: complex(double x, double y); // constructor - prototip }; //list de iniializare a membrilor complex::complex(double x, double y):re(x),im(y) {return;} Ordinea instanierilor este dat de cea indus prin declararea membrilor n clas i nu de ordinea din lista de instaniere. 31

Exemplu: class punct{ public: int re,im; punct(int x, int y){re=x; im=y;} }; class vector { punct vrf, orig; vector (int xv, int yv, int xo, int yo); }; vector::vector(int xv, int yv, int xo, int yo):vrf(xv,yv), orig(xo,yo) {return;} Ce putei afirma despre "construciile" de forma dat mai jos i eventual, realizai secvene de program C++ n care s le utilizai. romb::romb(ind dl, float da) tnod::tnod( ) arena::arena(double *x=5.0) clasa::clasa(int u) alfa::alfa(alfa&s) Dac un membru al unei clase este un pointer la o clas, atunci nu se poate apela constructorul acelei clase n cadrul listei de instaniere.Constructorii nu pot fi apelai pentru pointeri la obiecte, ci numai pentru obiecte.

Se accept ca un membru al unei clase s fie o referin la un obiect. n acest caz referina trebuie s fie iniializat n momentul n care este creat obiectul care conine referina. Exemplu: class B { public: int obs; int &noutate; B(int k); // constructor }; B::B(int k):obs(k),noutate(obs); // lista de iniializare {return;}

Membrii de tip referin sau constani trebuie iniializai ntr-o list de instaniere. Ca atare clasele care vor avea astfel de membri, obligatoriu vor conine un constructor. Am vzut c un constructor este necesar att la iniializarea unui obiect al unei clase ct i la alocarea dinamic de memorie pentru el. O alt funcie membr special, care are rolul de a dezactiva toate funciile unui obiect creat n mod dinamic, este destructorul. Se poate spune c destructorul distruge un obiect creat. Ca atare, destructorul este folosit n cadrul obiectului al crui constructor realizeaz o alocare dinamic de memorie. Ca i constructorii, destructorii pot efectua diverse alte operaii. Un destructor se identific prin acelai identificator ca i clasa al crei membu este, precedat de simbolul "~". Este vorba de negarea unui constructor. ~iden_clas( ); Un destructor nu are parametri i nu conine tip de valoare returnat.

32

Reamintim c un constructor de obiecte este executat cnd se ntlnete instruciunea de declarare a obiectului. Dac se declar dou sau mai multe obiecte n aceeai instruciune, constructorii sunt apelai n ordinea declarrii de la stnga la dreapta. Funciile destructor, pentru obiecte locale, se execut n ordine invers fa de constructorii care s-au folosit la instaniere. Funciile constructor pentru obiectele globale se execut naintea funciei main(). Destructorii globali se execut n ordine invers i dup ce s-a ncheiat funcia main( ). Apelul destructorului se produce,n mod automat, n timpul execuiei programului, cnd se ncheie domeniul de valabilitate (existen) al obiectului respectiv. Nu este necesar un apel explicit al unui destructor. Destructorii obiectelor membre sunt apelai dup ce destructorul obiectului principal a fost executat. Dac obiectul membru este compus din alte obiecte, atunci se va proceda la execuia destructorilor obiectelor incluse. Dac obiectul este local unei funcii, apelul destructorului se face nainte de a se iei din funcia respectiv, iar dac obiectul este static, apelul destructorului corespunztor se va face nainte de a se termina execuia programului principal. O clas nu poate avea mai mult de un destructor, pentru c el nu poate fi redefinit. #n general, un destructor se declar n seciunea public. La o declarare privat a unui destructor, accesul la el se poate face doar cu funcii membre ale obiectului respectiv sau funcii prieten (friend). Antetul destructorului n exteriorul clasei: iden_clas ::~iden_clas( )

Adresa unui destructor nu poate fi determinat. Exemplu: # include <iostream.h> class EXE{ public: int beta; EXE(int par); //constructor ~EXE( ); //destructor }ob_1(1),ob_2(2); EXE::EXE(int par){ cout<<"AFIEAZ_INIIALIZAREA"<<par<<"\n"; beta=par; } EXE::~EXE( ){ cout<<"DISTRUGEREA"<<beta<<"\n"; } main( ){ EXE ob1_local(3); cout<<"SCRIE:\n"; EXE ob2_local(4); return 0;} Ce se va afia? 33

Aplicaie: S se realizeze un program prin care s se afieze un anumit numr de salariai ai unei ntreprinderi mpreun cu salariile lor. Se vor utiliza constructori i destructori pentru creare, respectiv distrugere de obiecte,operatorii new i delete pentru alocare, respectiv eliberare a unei zone de memorie alocat dinamic. #include <iostream.h> #include<conio.h> #include<string.h> class persoana{ private: char *nume; long sal; void init(char *s=NULL,long u=0){ nume=new char[strlen(s)+1]; strcpy(nume,s); sal=u; } public: persoana( ) {init( );} persoana(char*s,long u) {init(s,u);} ~persoana( ) {delete nume;} void afis( ) {cout<<nume<<"...are salariul:"<<sal<<"n";} void introd(char*s,long u) {init(s,u);} }; class serviciu{ private: persoana*pers; int npers; public: serviciu(int n=0){ npers=n; pers=new persoana[npers]; } ~serviciu( ){delete pers;} void intr(int n); void afis_1( ); }; void serviciu::intr(int n){ char s[30]; long u; int i; npers=n; for(i=0;i<npers;i++){ cin>>s>>u; pers[i].introd(s,u); }} void serviciu::afis_1( ){ int i; 34

for(i=0;i<npers;i++){ pers[i].afis( ); } } void main( ){ serviciu serv; clrscr( ); serv.intr(5); serv.afis_1( ); getch( ); } Aplicaie: S se implementeze o clas Stiva n care elementele s fie de tip ntreg. S se implementeze funciile membre care s adauge cte un element n stiv, respectiv, s extrag cte un element din stiv. O alt funcie membr s poat afia stiva n orice moment care se dorete. #include <iostream.h> #include <conio.h> #include <stdlib.h> class nod{ public: int cheie; nod* next;}; class Stiva{ nod*cap; public: Stiva( ) {cap=NULL;} ~Stiva( ); int pop( ); void push(int); void afis( ); }; void Stiva::push(int x){ nod *p; if ((p=new nod)==NULL) {cout<<"Memorie insuficienta!"; getch( ); exit(1); } p->next=cap; p->cheie=x; cap=p; } int Stiva::pop( ){ if(!cap) {cout<<"Stiva vida"; getch( ); exit(1); } nod*p=cap; int t; t=cap->cheie; cap=cap->next; delete p; 35

return t; } void Stiva:: afis( ){ nod*p=cap; while (p!=NULL) {cout<<p->cheie; p=p->next; } } Stiva::~Stiva( ){ nod*p; while(cap!=NULL){ p=cap; cap=cap->next; delete p; } } void ecran( ){ clrscr( ); gotoxy(9,9); cout<<"1:Pune"; gotoxy(9,10); cout<<"2:Sterge"; gotoxy(9,11); cout<<"3:Afisare"; gotoxy(9,12); cout<<"4:Exit"; gotoxy(9,13); } void main ( ){ Stiva V; int cheie; char c; do{ ecran( ); c=getch( ); clrscr( ); switch(c){ case'+':cout<<"Se da cheia datei care se introduce:"; cin>>cheie; V.push(cheie); break; case'-':cout<<"Am extras din stiva:"<<V.pop( ); getch( ); break; case'a':V.afis( ); getch( ); cout<<""; break; case'e':exit(1); } }while(1) ; }

36

Aplicaie S se defineasc un tip abstract Lista. S se implementeze o list simplu nlnuit i cu ajutorul a doi pointeri s se realizeze operaiile de inserare, respectiv, tergere de noduri din list. #include <iostream.h> #include <stdlib.h> #include <conio.h> class nod{ public: int k; nod*next; }; class Lista{ nod*prim,*ultim; public: Lista(); ~Lista(); void insert(nod*,int); void elimin(int); void afis(); }; Lista::Lista(){ char c; nod*p_1; prim=ultim=NULL; do{ cout<<"n Continuati:Y/N"; cin>>c; if(c!='Y'&&c!='y') break; p_1=new nod; if(!(p_1)){ cout<<"Memorie insuficienta!"; getch(); exit(1); } cout<<"Informatia atasata nodului:"; cin>>p_1->k; p_1->next=NULL; if(prim==NULL) prim=ultim=p_1; else { ultim->next=p_1; ultim=p_1; } }while (1); } Lista::~Lista(){ nod*p=prim,*p_1; clrscr(); cout<<"Distrugerea listei"; 37

while(p!=NULL){ p_1=p; p=p->next; delete p_1; } } void Lista::insert(nod*p,int cheie) { nod*p_1; p_1=prim; int gasit=0; while(p_1!=NULL&&!gasit) { if(p_1->k==cheie)gasit=1; else p_1=p_1->next; } if(!gasit){ cout<<"\n Nu s-a identificat un element din lista cu cheia:"<< cheie; getch(); return; } else{ p->next=p_1->next; p_1->next=p; if(p_1==ultim) p=ultim; } } void Lista::elimin(int cheie) { nod*p_1,*p_2; p_1=prim; if(prim==NULL){ cout<<"Lista vida"; return; } if(p_1->next==NULL){ if(p_1->k==cheie){ prim=ultim=NULL; return; } else { cout<<"Nu se afla un element cu cheia"; cout<< cheie; return; } } else{ if(p_1->k==cheie){ prim=prim->next; delete p_1; return; } else{ while(p_1->next!=NULL) { 38

p_2=p_1->next; if(p_2->k==cheie){ p_1->next=p_2->next; delete p_2; cout<<"\n Am eliminat un nod din lista"; return; } p_1=p_2; } cout<<"\n Nu se afla nod cu codul dat"; } } } void Lista::afis() { nod*p=prim; int lin=0,col=60; while(p!=NULL) { if(lin>=21) { gotoxy(10,22); cout<<"\n Actionati o tasta pentru a continua"; getch(); lin=1; col=1; } if(col>50) { lin+=1; col=1; } else col+=25; gotoxy(col,lin); cout<<p->k; p=p->next; } } void ecran() { clrscr(); gotoxy(10,10); cout<<"Afisare:"; gotoxy(10,11); cout<<"Inserare:"; gotoxy(10,12); cout<<"Stergere:"; gotoxy(10,13); cout<<"Exit:"; } void main(void) { 39

nod*p; char c; int cheie; Lista L; do { ecran(); c=getch(); clrscr(); switch(c) { case'A': L.afis(); break; case'I': cout<<"Inserez un element in lista \n"; p=new nod; if(!(p)) { cout<<"Memorie insuficienta"; getch(); exit(1); } cout<<"Se da informatia atasata nodului"; cin>>p->k; cout<<"\n Se da codul nodului dupa care se va insera nodul citit"; cin>>cheie; L.insert(p,cheie); break; case'S': cout<<"\n Se da codul nodului care se va sterge:"; cin>>cheie; L.elimin(cheie); break; case'E': exit(1); } getch(); }while(1); } Tematica propus 1. S se implementeze n limbajul C++ o list dublu nlnuit i una circular.
2. 3.

S se defineasc clasa complex util n realizarea unor operaii cu numere complexe. S se implementeze clasa arbore i s se efectueze parcurgerea unui arbore binar.

4. Ce putei afirma despre urmtoarele situaii: a) class vector{ int x,y; void vector(int x0, int y0) {x=x0; y=y0;} 40

}; b) class vector{ int x,y; vector(int x0, int y0) {x=x0; y=y0;} }; void main( ){ vector(56,80); } 5. S se realizeze un program C++ prin care, dintr-un ir de caractere cunoscut,s se determine cea mai lung expresie aritmetic corect din punct de vedere sintactic, fr paranteze, care s se obin prin operaia de tergere de caractere din irul dat. Operanzii sunt litere mici ale alfabetului latin. 6. n Romnia exist n ruri. S se realizeze un program C++ prin care s se citeasc perechi de forma (i,j) care semnific faptul, c rul i este afluent al rului j. Se cere s se afieze rurile, care nu sunt aflueni ai altor ruri i rurile, care nu au aflueni.

41