Sunteți pe pagina 1din 21

CREAREA, INITIALIZAREA SI DISTRUGEREA OBIECTELOR UNEI CLASE

Odata cu implementarea unui tip de date este posibila definirea unor variabile de acest tip. O variabila, care are ca tip o clasa definita de utilizator, este un obiect. Datele membre ale unei clase pot fi arbitrare, dar nu obiecte ale clasei respective. In schimb, astfel de date membru pot fi obiecte ale unei alte clase. Spre deosebire de tipurile de date predefinite, care stocheaza valori singulare in simple zone de memorie, o clasa definita de utilizator contine mai multe componente care, pe langa setarea unui numar de octeti necesari pentru memorarea lor, solicita si alte prelucrari suplimentare. Obiectele create trebuie sa fie initializate. Initializarea lor se poate realiza: a) cu ajutorul listelor de initializare Exemplu: class fisier { public: FILE *fp; char *nume; int lung_art; }; fisier fd={stdin,"file.bin",80}; //initializare corecta Sa observam ca, in class, o astfel de initializare a obiectului fd se poate realiza numai daca membrii clasei sunt declarati in sectiunea public. In plus, pentru astfel de obiecte, nu vor exista constructori, functii virtuale, nu pot proveni din clase derivate (aceste concepte vor fi precizate pe parcurs). b) cu ajutorul constructorilor. Obiectele se pot initializa in momentul declararii lor. In acest sens se utilizeaza unele functii membru speciale ale clasei numite constructori.
1

Constructorii sunt utili in: -initializarea obiectelor; -alocarea dinamica de memorie; -deschideri de fisiere etc. Constructorul este o functie membru a unei clase care se apeleaza automat la crearea fiecarui obiect, inclusiv pentru obiecte temporare, si se identifica cu acelasi nume ca si clasa din care face parte. Spre deosebire de alte functii membru ale unei clase, adresa unui constructor nu se poate determina. Sintaxa: id_clasa (lista_parametri);//prototip Functia constructor nu retuneaza nici o valoare, deci nu apare tip al valorii returnate (nici chiar void), si nu poate fi functie virtuala. Un constructor admite mai multe prototipuri. Listele de parametri, ale diferitelor variante de implementare, trebuie sa fie diferite pentru a putea sa fie identificate corect de catre compilator. Parametrii unui constructor pot fi de orice tip, cu exceptia tipului definit de clasa ce contine acest constructor. Unii dintre acesti parametri pot fi pointeri sau referinte la o instanta a clasei respective. Exemplu: class A{ .. A(A a); // incorect .. A(A*a); // corect, parametrul este un pointer la tipul definit de clasa A . A(A& a); }// corect, parametrul este o referinta la un obiect al clasei A Un constructor poate fi: a) implicit: - fara parametri (definit de utilizator) care poate contine in corpul sau orice instructiune valida. Un astfel de constructor inline nu poate contine instructiuni cu caracter repetitiv. - generat de compilator (cand nu exista constructori definiti de utilizator in clasa respectiva). Corpul unui astfel de constructor nu contine nici o instructiune. b) cu parametri care, in totalitate sau partial, iau valori in mod implicit;
2

c) cu parametri care nu iau valori in mod implicit; d) cu conversie de tip; e) de copiere: - definit de utilizator; - generat implicit de compilator; Desi se accepta conceptul de constructor implicit trebuie facuta o distinctie clara intre constructorul implicit (fara parametri) generat de utilizator si cel care are toti parametrii cu valori implicite. La initializare, daca in clasa respectiva exista mai multi constructori, se va tine cont, la apelul lor, de lista de parametri (numar si tip) solicitata de instanta respectiva. Daca exista un singur constructor atunci se aplica aceleasi reguli ca la apelul functiilor. Ca atare, tipurile parametrilor efectivi (expresiile care vor produce initializarea) se vor converti la tipurile parametrilor formali din prototipul constructorului. Transmiterea parametrilor actuali catre functia constructor, utili pentru initializarea unui obiect, se realizeaza in momentul creerii acestuia. ident_clasa ident_obiect(lista_param_actuali); Lista contine una sau mai multe expresii separate prin virgula. Daca nu se produce o initializare a obiectului, lista si parantezele lipsesc. Exemplu: class data_c{ int zi,luna,an; public: // initializare, constructor fara parametri (implicit) data_c( ){ zi=28; luna=7; an=2002;} //initializare, constructor cu param. tip int, ultimul cu valoare // implicita data_c(int z, int l, int a=2002){ zi=z; luna=l; an=a; } //initializare, constructor care contine ca param. denumirea lunii data_c(int z, char*den_l, int a) { zi=z;
3

strcpy(luna, den_l ); an=a;}; Exemple de instante ale clasei data_c. 1) data_c d1; se apeleaza constructorul implicit (fara parametri) si se obtine pentru obiectul d1: zi=28, luna=7, an=2002. 2) data_c d2(12,4,2002); se apeleaza constructorul cu toti parametrii de tip int si se obtine pentru obiectul d2: zi=12, luna=4, an=2002. 3) data_c d3(12,4); se apeleaza acelasi constructor ca la d2 obtinand acelasi rezultat. 4) data_c d4(18,"ianuarie", 2002); se apeleaza ultimul constructor si se obtine pentru obiectul d4: zi=18, luna=ianuarie, an=2002. 5) data_c *pd=new data_c(18,1); se apeleaza constructorul la fel ca pentru obiectele d2 si d3 in vederea initializarii, in plus, se va realiza si o alocare de memorie heap pentru obiectul creat, iar pd are ca valoare adresa de inceput a zonei alocate obiectului respectiv. Datele membru ale unui obiect (locale/dinamice) daca nu au fost initializate, vor primi valori necontrolabile (imprevizibile). Daca intr-o clasa este prezent un constructor implicit, atunci nu se mai poate defini pentru clasa respectiva un constructor cu toti parametrii cu valori implicite, pentru ca s-ar produce ambiguitati la instantierea obiectelor. Exemplu: Sa presupunem ca ar exista in clasa data_c urmatorii constructori: data_c( ){ zi=28;luna=7;an=2002;} si data_c(int z=27, int l=6, int a=2002){ zi=z; luna=l;an=a;} Pentru o instanta de forma data_c dd; nu este clar care dintre constructori va fi apelat. Lipsa constructorilor dintr-o clasa obliga compilatorul C++ sa genereze, in mod automat, un constructor implicit care are rolul doar de a aloca memorie pentru instantele clasei respective. Constructorii definiti de programator sunt necesari atunci cand se doreste initializarea obiectelor odata cu crearea lor. Daca nici unul dintre constructorii prezenti intr-o clasa nu este implicit, atunci nu se pot
4

instantia obiecte neinitializate, deoarece compilatorul nu genereaza constructor implicit intr-o astfel de situatie. Exemplu: class complex{ double real; double imag; public: complex(double x, double y){ //initializarea se realizeaza prin asignari real=x; imag=y;} }; Se pot instantia numai obiecte de tip complex cu ambii membri initializati. Exemplu: complex z(4,-2); complex u; // este incorect pentru ca nu exista constructor implicit. Intr-un constructor pot fi apelate si alte functii care pot fi, sau nu, functii membru ale clasei respective. Exemplu: class Pers{ char nume[25]; long salariu; public: Pers(char *,long);//constructor cu param. care nu iau valori //impliciteprototip long Sal( ) {return salariu;} //functie membru inline }; //definirea constructorului in exteriorul clasei Pers::Pers(char*n,long l){ strcpy(nume,n); // functie non-membru a clasei Pers salariu=l;} Constructorul garanteaza initializarea corecta a obiectului inainte de utilizarea efectiva a sa. El ofera o garantie, in plus, asupra faptului ca initializarea unui obiect se va efectua o singura data.
5

Apelul constructorului se realizeaza in momentul declararii unui obiect. Exemplu: Pers p=Pers("Iorga", 1987345); sau Pers p("Iorga",1987345); Exemplu: Folosirea unui constructor care are toti parametrii cu valori implicite: class Pers{ 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 impliciti sunt prezente in declaratia constructorului si nu in implementarea sa. Apelul unui astfel de constructor se poate realiza prin una din urmatoarele instante: Pers p1; Pers p2("Iorga"); Pers p3("Iorga",1987345); Pers p4=Pers( ); Varianta constructorului cu parametrii ce iau valori implicite se poate folosi atunci cand constructorul nu solicita argumente (parametri efectivi) si el se va identifica cu un constructor implicit. Apelul sau se va face sub forma unei simple declaratii. In mod normal, constructorii unei clase se afla in zona public si mai rar in sectiunea "private". Daca un constructor este declarat in sectiunea private el poate fi apelat doar de o alta functie membru a clasei sau de o functie friend. Prin urmare, numai functiile membru si friend pot produce obiecte avand constructori privati. O astfel de clasa se mai numeste si clasa privata.
6

Declaratia obiectelor a caror lista de initializare se reduce la un parametru se poate scrie intr-un format asemanator cu cel de la initializarea variabilelor simple. ident_clasa ident_obiect = expresie; In aceasta situatie se apeleaza un constructor in care primul parametru se actualizeaza cu valoarea "expresie", iar ceilalti parametri, daca exista, iau valori implicit. Exemplu: # include <iostream.h> class complex{ double real; double imag; public: complex(double x, double y=10) { 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( );//se va afisa numarul 2.56+10*i complex z2=3*r-1;// se atribuie o expresie obiectului z2 z2.afis( ); //se va afisa numarul 8.6+10*i complex z3(18.5,-9); z3.afis( );} //se va afisa numarul 18.5-9*i Daca se declara obiecte atat initializate cat si neinitializate atunci se va folosi un constructor implicit (fara parametri) care va fi apelat la instantierea obiectelor neinitializate. Pentru a nu se ajunge la obiecte care nu se pot identifica / initializa nu trebuie apelat un constructor in interiorul unui alt constructor.
7

Constructorii cu conversie de tip au primul parametru de tip diferit de cel indicat de clasa care il contine. Daca mai contin si alti parametri, ei trebuie sa ia valori implicit. Exemplu: class pers { ........................ public: pers(char*nume, long s=1987345) ...........................} void main( ){ .................... pers t=Horia; // se converteste char* la pers ................... } Constructorul de copiere este un constructor special care permite copierea de obiecte. Prin prezenta lui se renunta la copierea pe biti. El are ca prim parametru o referinta catre clasa din care face parte ca functie membru, iar daca mai exista si alti parametri, acestia au valori implicite. Sintaxa: id_clasa (const id_clasa &); El este util doar pentru initializari. Un constructor al unei clase poate fi apelat in mod explicit in situatii de genul urmator: id_clasa ob= id_clasa (lista par_actuali); In acest caz este apelat constructorul clasei id_clasa pentru a creea un obiect anonim care va fi copiat in 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;
8

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);//se creaza obiectul z1 z1.afis( ); complex z2=z1;// se aplica constructorul de copiere z2.afis( ); complex z3(z1); // idem z3.afis( ); complex z=complex(1.2,3.5); //crearea unui obiect anonim care este copiat in z z.afis( ); } Daca se solicita un constructor de copiere si nu a fost definit de utilizator, atunci compilatorul va genera unul implicit. Elementele membre sunt copiate de la obiectul sursa la obiectul destinatie in ordinea declararii lor in clasa. Initializarea elementelor membre ale unui obiect se poate face printr-un constructor care foloseste o lista de initializare. Exemplu: class complex{ double re,im; public: complex(double x, double y); // constructor - prototip }; //lista de initializare a membrilor complex::complex(double x, double y):re(x),im(y) {return;} Ordinea initializarilor este data de cea indusa prin declararea membrilor in clasa si nu de ordinea din lista de initializare. Daca un membru al unei clase este un pointer la o clasa, atunci nu se poate apela constructorul acelei clase in cadrul listei de
9

initializare. Constructorii nu pot fi apelati pentru pointeri la obiecte, ci numai pentru obiecte. Exemplu: class punct{ public: int re,im; punct(int x, int y){re=x; im=y;} }; class vector { punct varf, orig;//obiecte ale clasei punct vector (int xv, int yv, int xo, int yo); }; vector::vector(int xv, int yv, int xo, int yo):varf(xv,yv), orig(xo,yo) {return;} Se accepta ca un membru al unei clase sa fie o referinta. In acest caz referinta trebuie sa fie initializata in momentul in care este creat obiectul care contine referinta. Exemplu: #include<iostream.h> class B{ public: int obs; int &noutate;// membru referinta B(int k);// constructor-prototip }; B::B(int k):obs(k),noutate(obs) // lista de initializare {return;} void main(){ B b(101); cout<<"obs:"<<b.obs<<" "<<"noutate:"<<b.noutate;} Membrii de tip referinta sau constanti trebuie initializati intr-o lista de initializare. Ca atare clasele care vor avea astfel de membri vor contine, in mod obligatoriu, un constructor. Un constructor al unei clase este necesar atat la initializarea obiectelor clasei cat si la alocarea dinamica de memorie pentru acestea.
10

Exemplu: Sa se calculeze o suma de polinoame de o variabila. #include<iostream.h> #include<conio.h> #include<stdlib.h> class polinom{ int grad; double *coef; public: polinom(int n=0); void cit(); polinom* add(polinom*); void afis(); }; int nr; polinom::polinom(int n){ grad=n; coef=new double[grad+1];//alocare de memorie for(int i=0;i<=grad;i++) *(coef+i)=0; } void polinom::cit(){ cout<<"\ngradul = ";cin>>grad; coef=new double[grad+1]; //alocare de memorie for(int i=0;i<=grad;i++){ cout<<"\n coeficientul lui X^"<<i<<" = "; cin>>*(coef+i); } } polinom* polinom::add(polinom* p){ int gr=((grad<p->grad)? p->grad:grad); polinom *rez=new polinom(gr);//alocare de memorie for(int k=0;k<=gr;k++) rez->coef[k]=0; for(int i=0;i<=gr;i++) rez->coef[i]=p->coef[i]+coef[i]; return rez; } void polinom::afis(){ char s; for(int i=grad ;i>=0;i--){ if(coef[i]>=0) s='+';
11

else s='-'; cout<<s<<abs(coef[i])<<" *X^"<<i<<" ";} } void main(){ polinom p[40],*t; clrscr(); cout<<"Nr de polinoame:"; cin>>nr; for(int i=1;i<=nr;i++) p[i].cit(); cout<<"\n Polinomul suma:\n"; for(int j=2;j<=nr;j++){ t=p[j-1].add(&p[j]); p[j]=*t;} t->afis(); getch();
}

Destructorul este o alta functie membru speciala a unei clase care are rolul de a dezactiva toate functiile unui obiect creat in mod dinamic. Se poate spune ca destructorul distruge un obiect creat dinamic. Ca atare, destructorul este folosit in cadrul obiectului al carui constructor realizeaza o alocare dinamica de memorie. Ca si constructorii, destructorii pot efectua si alte operatii. Un destructor se identifica prin simbolul "~" urmat de numele clasei din care face parte. Se zice ca este vorba de o negare a constructorului. ~iden_clasa( ); Un destructor nu are parametri si nu contine tip de valoare returnata. Reamintim ca un constructor de obiecte este executat cand se intalneste instructiunea de declarare a obiectului. Daca se declara doua sau mai multe obiecte in aceeasi instructiune, constructorii sunt apelati in ordinea declararii acestora de la stanga la dreapta. Functiile destructor, pentru obiecte locale (membre ale unei clase), se executa in ordine inversa fata de constructorii care s-au folosit la initializare.

12

Functiile constructor pentru obiectele globale se executa inaintea functiei main( ). Destructorii pentru obiectele globale se executa dupa ce s-a incheiat functia main( ). Apelul destructorului se produce, in mod automat, in timpul executiei programului, cand se incheie domeniul de valabilitate (existenta) al obiectului respectiv. Nu este necesar un apel explicit al unui destructor. Daca obiectul este local unei functii, apelul destructorului se face inainte de a se iesi din functia respectiva, iar daca obiectul este static, apelul destructorului corespunzator se va face inainte de a se termina executia programului principal. O clasa nu poate avea mai mult de un destructor, pentru ca el nu poate fi redefinit. In general, un destructor se declara in sectiunea public. La o declarare privata a unui destructor, accesul la el se poate face doar cu functii membre ale obiectului respectiv sau functii prieten (friend). Definirea destructorului in exteriorul clasei: iden_clasa ::~iden_clasa( ) {...} Adresa unui destructor nu poate fi determinata. Exemplu: #include<iostream.h> class exe{ public: int beta; exe(int par); ~exe(); }o_1(1),o_2(2); exe::exe(int par){ cout<<"Creare:"<<par<<"\n"; beta=par;} exe::~exe(){ cout<<"Distrugere:"<<beta<<"\n";} void main(){ exe o1(3); exe o2(4); }

13

Aplicatie: Sa se implementeze o clasa Stiva cu elemente de tip intreg care sa contina o functie membru pentru adaugare de elemente in stiva, una pentru extragere de elemente din stiva si una pentru afisarea stivei in orice moment. #include<iostream.h> #include<conio.h> #include<stdlib.h> class nod{ public: int cheie; nod*next; }; //declaratia clasei //========================== class stiva{ nod*varf; public: stiva(){varf=NULL;} ~stiva(); void pune(int); int extrage(); void afis(); }; //========================= void stiva::pune(int x){ nod*p; if((p=new nod)==NULL){ cout<<"Memorie insuficienta!"; getch(); exit(1);} p->next=varf; p->cheie=x; varf=p;} //======================== int stiva::extrage(){ if(!varf){cout<<"stiva vida!"; getch();
14

exit(1);} nod*p=varf; int t; t=varf->cheie; varf=varf->next; delete p; return t;} //============================ void stiva::afis(){ nod*p=varf; while(p!=NULL){ cout<<p->cheie<<" "; p=p->next;}} //============================ stiva::~stiva(){ nod*p; while(varf!=NULL){ p=varf; varf=varf->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();
15

c=getch(); clrscr(); switch(c){ case'+':cout<<"Se da elementul de introdus:"; cin>>cheie; v.pune(cheie); break; case'-':cout<<"Am extras din stiva:"<<v.extrage(); getch(); break; case'a':v.afis(); getch(); cout<<""; break; case'e':exit(1);} }while(1);} Aplicatie Sa se defineasca un tip abstract Lista. Sa se implementeze o lista simplu inlantuita si cu ajutorul a doi pointeri sa se realizeze operatia de inserare, respectiv stergere de noduri din lista. #include<iostream.h> #include<conio.h> #include<stdlib.h> class nod{ public: int k; nod*next;}; //========================= class lista{ nod*prim,*ultim; //se adreseaza clasa nod public: lista(); ~lista(); void insert(nod*,int); void elimin(int);
16

void afis(); }; lista::lista(){ char c; nod*p; prim=ultim=NULL; do{ cout<<"\n Continuati:Y/N"; cin>>c; if(c!='Y'&&c!='y')break; p=new nod; if(!p){ cout<<"Memorie insuficienta!"; getch(); exit(1);} cout<<"Informatia atasata nodului:"; cin>>p->k; p->next=NULL; if(prim==NULL) prim=ultim=p; else { ultim->next=p; ultim=p;} }while(1);} //================================== lista::~lista(){ nod*p=prim,*p1; clrscr(); cout<<"\n Se distruge lista!"; while(p!=NULL){ p1=p; p=p->next; delete p1;}} //================================== void lista::insert(nod*p,int cheie){ nod*p1; p1=prim; int id=0; while(p1!=NULL && !id){ if(p1->k==cheie)id=1;
17

else p1=p1->next;} if(!id){ cout<<"\n Nu s-a identificat element in lista cu cheia:"<<cheie; getch(); return;} else{ p->next=p1->next; p1->next=p; if(p1==ultim)p=ultim;}} //================================ void lista::elimin(int cheie){ nod*p1,*p2; p1=prim; if(prim==NULL){ cout<<"Lista vida!!!"; return;} if(p1->next ==NULL){ if(p1->k==cheie){ prim=ultim=NULL; return;} else{ cout<<"\n Nu se afla element cu cheia data:"<<cheie; return;}} else{ if(p1->k==cheie){ prim=prim->next; delete p1; return;} else{ while(p1->next!=NULL){ p2=p1->next; if(p2->k==cheie){ p1->next=p2->next; delete p2; cout<<"\n Am eliminat un nod din lista!"; return;} p1=p2;} cout<<"\n Nu se afla nod cu cheia data!!!";}}}
18

//==================================== 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(){ nod*p; char c; int cheie; lista ll; do{ ecran(); c=getch();
19

clrscr(); switch(c){ case'A': ll.afis(); break; case'I': cout<<"\n Inserare in lista:"; p=new nod; if(!p){ cout<<"\n Memorie insuficienta!!"; getch(); exit(1);} cout<<"\n Informatia nodului:"; cin>>p->k; cout<<"\n Se da cheia nodului dupa care se face inserarea:"; cin>>cheie; ll.insert(p,cheie); break; case'S': cout<<"\n Se da cheia nodului care se va sterge:"; cin>>cheie; ll.elimin(cheie); break; case'E':exit(1); }getch(); }while(1);}

Tematica propusa
Verificati-va cunostintele: 1. Constructorii unei clase sunt utili in: a) identificarea clasei; b) alocare dinamica de memorie; c) identificarea obiectelor; d) initializarea obiectelor; e) distrugerea obiectelor; 2. Apelul constructorului se produce in functie de: a) structura obiectului;
20

b) lista expresiilor ce insotesc identificatorul obiectului; c) identificatorul obiectului; d) functiile prezente in corpul constructorului; 3. Constructorul de copiere permite: a) copierea pe biti; b) prim parametru un pointer; c) parametrii care nu iau valori in mod implicit; d) copierea de obiecte fara a folosi "copierea bit cu bit"; e) prim parametru de forma const id_clasa &...; 4. Raspundeti la urmatoarele intrebari: a) Cum se executa constructorii pentru obiecte globale ? Dar destructorii? b) Cum se executa constructorii/destructorii intr-o clasa care contine ca date membre, obiecte ale unei alte clase? c) Cand se apeleaza destructorul pentru un obiect local unei functii? 5. Sa se implementeze o lista dublu inlantuita si una circulara. 6. Sa se implementeze clasa arbore si sa se efectueze parcurgerea unui arbore binar. 7. Ce puteti afirma despre situatiile urmatoare? a) class vector{ int x,y; public: void vector(int x0, int y0) {x=x0; y=y0;} }; b) class vector{ int x,y; public: vector(int x0, int y0) {x=x0; y=y0;} }; void main( ) {vector v(56,80);...}
21

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