Sunteți pe pagina 1din 14

Curs 7

(continuare Curs04_Clase_constructori_destructori)
Clase utilizând pointeri

Revenim la exemplul dat la sfârșitul cursului 4. Am menționat atunci că folosirea destructorilor este
în mod special utilă atunci când un obiect asignează dinamic memoria pe parcursul ciclului său de
viață, iar la momentul distrugerii sale dorim să eliberăm memoria care a fost ocupată de obiect.

// exemplu cu constructori si destructori


1 #include <iostream>
2 using namespace std;
3
4 class CRectangle {
5 int width, height;
6 public:
7 CRectangle (int,int);
8 ~CRectangle ();
9 int area () {return (width * height);}
10 };
11
12 CRectangle::CRectangle (int a, int b) {
13 width = a;
14 height = b;
15 cout<<"Object is being created\n";
16 }
17
18 CRectangle::~CRectangle () {
19 cout<<"Object is being deleted\n";
20 }
21
22 int main () {
23 CRectangle obiect1 (3,4), obiect2 (5,6);
24 cout << "aria obiect1: " << obiect1.area() << endl;
25 cout << "aria obiect2: " << obiect2.area() << endl;
26 return 0;
27 }
28

În exemplul de mai sus destructorul obiectului este apelat în mod automat de către compilator.
În exemplul de mai jos folosim pointeri pentru dimensiunile private width și height ale
dreptunghiului:

1
1 // example on constructors and destructors
2 #include <iostream>
3 using namespace std;
4
5 class CRectangle {
6 int *width, *height;
7 public:
8 CRectangle (int,int);
9 ~CRectangle ();
10
11 int area ()
12 {return (*width * *height);
13 }
14 };
15
16 CRectangle::CRectangle (int a, int b) {
17 //pointerii initializati pt a accesa locatia spre care indica
18 width = new int;
19 height = new int;
20 *width = a;
21 *height = b;
22 }
23
24 CRectangle::~CRectangle () {
25 delete width;
26 delete height;
27 }
28
29 int main () {
30 CRectangle object1 (3,4), object2 (5,6);
31 cout << "aria obiect1: " << obiect1.area() << endl;
32 cout << "aria obiect2: " << obiect2.area() << endl;
33 return 0;
}

În cazul obiectelor create dinamic destructorul obiectului este apelat implicit prin intermediul
operatorului delete.

Supraîncărcarea constructorilor
Ca orice altă funcție, și constructorul poate fi supraîncărcat cu mai multe funcții care au
același nume dar tipuri diferite de parametri sau număr diferit de parametri. Se știe că pentru funcții
supraîncărcate compilatorul va apela acea funcție ai cărei parametri corespund cu argumentele
utilizate în apelul funcției.

2
În cazul constructorilor, care sunt apelați automat atunci când se creează un obiect, se va
executa acel constructor care se potrivește cu argumentele transmise în declararea obiectului:

1 // overloading class constructors


2 #include <iostream>
3 using namespace std;
4
class CRectangle {
5 int width, height;
6 public:
7 CRectangle ();
8 CRectangle (int,int);
9 int area (void) {return(width*height);}
10 };
11
CRectangle::CRectangle () {
12 width = 5;
13 height = 5;
14 }
15
16 CRectangle::CRectangle (int a, int b) {
17 width = a;
height = b;
18
}
19
20 int main () {
21 CRectangle obiect1 (3,4);
22 CRectangle obiect2;
23 cout << "aria obiect1: " << obiect1.area() << endl;
24 cout << "aria obiect2: " << obiect2.area() << endl;
return 0;
25 }
26
27
28
29

În acest caz obiect2 a fost declarat fără niciun argument, astfel că a fost inițializat cu
constructorul care nu are niciun parametru și care inițializează atât width cât și height cu valoarea 5.

Important: Observați că atunci când declarăm un obiect nou și dorim să folosim constructorul
implicit (cel fără parametri) nu avem voie să folosim parantezele ():

1 CRectangle obiectb; // corect


2 CRectangle obiectb(); // gresit!

3
Constructorul implicit, explicit și de copiere
Dacă nu se declară niciun constructor în definiția clasei, compilatorul presupune că există un
constructor implicit al clasei fără niciun argument.

1 class CExample {
2 int a,b,c;
3 public:
4 void multiply (int n, int m) { a=n; b=m; c=a*b; }
5 void afis()
6 {cout<<a<<" "<<b<<" "<<c<<endl;}
7 };

De aceea, după declararea unei clase compilatorul presupune că respectiva clasă are un constructor
implicit, astfel încât pot fi declarate obiecte de această clasă prin simpla declarare a numelui lor, fără
niciun argument.

  CExample ex;

Dacă însă ați declarat propriul constructor pentru o clasă, compilatorul nu va mai furniza un
constructor implicit. Astfel vor trebui declarate toate obiectele clasei în concordanță cu prototipurile
constructorilor definiți pentru respectiva clasă:

1 class Cexemplu {
2 int a,b,c;
3 public:
4 Cexemplu (int n, int m) {
5 a=n; b=m; };
6 void multiplicare () {
7 c=a*b;
8 }
9 void afisare()
{cout<<a<<" "<<b<<" "<<c<<endl;
}
};

Aici s-a declarat un constructor care are doi parametri de tipul int

următoarea declarație a obiectului ex va fi corectă:

  Cexemplu ex (2,3);

următoarea declarație a obiectului ex NU va fi corectă:

4
  Cexemplu ex;

nu va fi o declarație corectă, deoarece noi am declarat un constructor explicit al clasei care l-a înlocuit
pe cel implicit.

Compilatorul însă nu doar creează un constructor implicit în caz că nu se declară unul explicit,
ci furnizează și trei funcții-membru speciale care sunt declarate implicit dacă nu le declarați explicit.
Acestea sunt constructorul de copiere (copy constructor), operatorul de asignare a copierii (copy
assignment operator) și destructorul implicit.

Constructorul de copiere și operatorul de asignare a copierii copiază toate datele


conținute într-un alt obiect în membrii de date ai obiectului curent. Pentru Cexemplu,
constructorul de copiere declarat implicit de compilator ar fi ceva similar cu codul de mai jos:

1 Cexemplu::Cexemplu (Cexemplu & X)


2 { a=X.a; b=X.b; c=X.c;
3 }

Cexemplu::Cexemplu (Cexemplu &t)


{a=t.a;b=t.b;c=t.c;}
De aceea următoarele două declarații de obiecte vor fi corecte:

1 Cexemplu ex (2,3);
2 Cexemplu ex2 (ex); // constructorul de copiere (datele sunt copiate din ex)

Tema
Realizați exemplul complet cu clasa Cexamplu, declarand un obiect ex de clasa Cexemplu,
aplicând acestuia funcția multiplicare și utilizand constructorul implicit de copiere pentru a
crea un al doilea obiect ex2 de clasa Cexemplu care să copieze, ca date în elementele sale, datele
obiectului ex setate anterior. Apoi să se afișeze, utilizand funcția publică afisare, datele din
obiectele ex și ex2.

5
Rezolvare:

Pointeri spre clase


Se pot crea pointeri care să indice spre clase. Trebuie pur și simplu să considerăm că, odată declarată,
o clasă devine un tip valid, așa încât putem folosi numele clasei ca și tip al pointerului. De exemplu: 

  CRectangle * pointer;

este un pointer spre un obiect de clasă CRectangle.

Așa cum am făcut și la structurile de date, pentru a referi direct un membru al unui obiect
indicat de un pointer putem folosi operatorul săgeată (->) pentru indirectare. Mai jos este un exemplu
cu unele combinații posibile:

1 // Exemplu pentru pointer spre clase a area: 2


2 #include <iostream> *b area: 12
3 using namespace std; *c area: 2
4 d[0] area: 30
5 class CRectangle { d[1] area: 56
6 int width, height;
7 public:
8 void set_values (int, int);
9 int area (void) {return (width * height);}
10 };
11
12 void CRectangle::set_values (int a, int b) {
13 width = a;

6
14 height = b;
15 }
16
17 int main() {
18 CRectangle a, *b, *c;
19 CRectangle * d = new CRectangle[2];
20 b= new CRectangle;
21 //init point cu adresa de mem
22 c= &a;
23 a.set_values (1,2);
24 b->set_values (3,4);
25 d->set_values (5,6);
26 d[1].set_values (7,8);
27 cout << "a area: " << a.area() << endl;
28 cout << "*b area: " << b->area() << endl;
29 cout << "*c area: " << c->area() << endl;
30 cout << "d[0] area: " << d[0].area() << endl;
31 cout << "d[1] area: " << d[1].area() << endl;
32 delete[] d;
33 delete b;
34 return 0;
35 }
36
37
38
39
40

Operatorii new, delete și delete[]


Pentru a cere memorie dinamică (alocată în timpul execuției programului) se utilizează
operatorul new urmat de un specificator de tip de date. Va fi returnat un pointer către începutul
noului bloc de memorie alocat. Sintaxa este: 
pointer = new type sau

pointer = new type [number_of_elements]

Care este diferența între a declara un array normal și a asigna dinamic memorie printr-un pointer, așa
cum am făcut mai sus? Cea mai importantă diferență este faptul că dimensiunea array-ului trebuie să
fie o valoare constantă, ceea ce limitează această dimensiune la ceea ce noi decidem în momentul
proiectării programului, înainte de execuția acestuia, în timp ce alocarea dinamică a memoriei ne
permite să asignăm memorie în timpul execuției programului făcând uz de de orice valoare variabilă
sau constantă ca și dimensiune pentru array.
Exemple cu aplicațiile din enunțurile următoare.

7
Enunț 1 Declararea unui array de dimensiune constanta pentru care se aloca memoria la
compilare

#include <iostream>
#include <conio.h>
using namespace std;
int main()
{const int nMaxArr=5;
int Arr[nMaxArr];//se declara un array cu 5 elemente pentru care se aloca memoria la
compilare
//vom umple array-ul cu valorile 42, 43, 44, 45 si 46
for(int i=0;i<nMaxArr;i++)
Arr[i]=42+i;
// afisam valorile din array
for(int i=0;i<nMaxArr;i++)
cout<<"Arr["<<i<<"]="<<Arr[i]<<endl;
_getch();
return 0;
}

Enunț 2 Declararea unui array de dimensiune constanta pentru care se aloca memoria la executie
#include <iostream>
#include <conio.h>
using namespace std;
int main()
{const int nMaxArr=5;
int *Arr=new int[nMaxArr];//se declara un array cu 5 elemente pentru care se aloca memoria la
exec.
//vom umple array-ul cu valorile 42, 43, 44, 45 si 46
for(int i=0;i<nMaxArr;i++)
Arr[i]=42+i;
// afisam valorile din array
for(int i=0;i<nMaxArr;i++)
cout<<"Arr["<<i<<"]="<<Arr[i]<<endl;
delete[] Arr; // se elibereaza memoria ocupata de array
_getch();
return 0;
}

Enunț 3 Declararea unui array de dimensiune variabila (care va fi cunoscuta doar la executie)

#include <iostream>
#include <conio.h>
using namespace std;
int main()
{int n;
cout<<"Dati numarul de elemente al array-ului:";cin>>n;
int *Arr=new int[n];//se declara un array cu n elemente pentru care se aloca memoria la
executie
//vom umple array-ul cu valorile 42, 43, 44, 45 si 46
for(int i=0;i<n;i++)

8
Arr[i]=42+i;
// afisam valorile din array
for(int i=0;i<n; i++)
cout<<"Arr["<<i<<"]="<<Arr[i]<<endl;
delete[] Arr; // se elibereaza memoria ocupata de array
_getch();
return 0;
}

Deoarece în general necesarul de memorie dinamică este limitat la anumite momente de timp din
cadrul programului, atunci când nu mai este nevoie respectiva memorie ar trebui eliberată, astfel
încât memoria să devină din nou disponibilă pentru alte cereri de memorie dinamică. Acesta este
scopul operatorului delete, al cărui format este:

1 delete pointer;
2 delete [] pointer;

Observați cum valoarea dintre paranteze drepte în instrucțiunea new este o valoare variabilă
introdusă de utilizator (n), și nu o valoare constantă:

  p= new int[n];

Utilizatorul însă ar fi putut introduce pentru n o valoare așa de mare încât să depășească
puterea de alocare de memorie a sistemului (de exemplu n=1000000000). În acest caz este util a se
folosi următoarea variantă de program, cu includerea headerului new:
#include <iostream> How many numbers would you like to
#include <new> type? 5
using namespace std; Enter number : 75
Enter number : 436
int main() Enter number : 1067
{ Enter number : 8
int i, n; Enter number : 32
int* p; You have entered: 75, 436, 1067, 8, 32,
cout << "How many numbers would you like
to type? ";
cin >> n;
p = new (nothrow) int[n];
if (p == nullptr)
cout << "error:memory not
allocated";
else
{
for (i = 0; i < n; i++)
{
cout << "Enter number: ";
cin >> p[i];
}
cout << "You have entered: ";
for (i = 0; i < n; i++)
cout << p[i] << ", ";
delete[] p;
}
return 0;
}

9
După cum se vede, în expresia new trebuie specificat un parametru nothrow și dacă pointerul
returnat este pointerul null înseamnă că nu s-a putut aloca memoria necesară și se generează o
excepție pe care am tratat-o printr-un simplu mesaj de eroare. În caz că pointerul returnat nu este
pointerul null înseamnă că s-a alocat memoria necesară și programul continuă. Fără parametrul
nothrow eșecul alocării memoriei ar fi generat erori.

Enunț
Aplicație cu constructori supraincarcati, metode set-get, array de obiecte, declarare dinamică prin
pointeri a unor obiecte și array de obiecte

a) Să se implementeze o clasa Persoana cu membri privați pentru nume, numărul de cont și suma
existentă; un constructor standard și doi constructori expliciți (unul care seteaza toate cele 3
câmpuri private de mai sus cu cele 3 valori primite ca parametru și altul care seteaza numele și
suma existenta cu cele doua valori primite ca parametru și seteaza numarul de cont pe 0).

Clasa mai are metode publice de set și get pentru fiecare câmp din cele 3 campuri private (metodele
set seteaza câmpul respectiv cu valoarea primita ca parametru iar metodele get returneaza valoarea
respectivului câmp).
Clasa mai are o metoda publică pentru depunerea unei anumite sume primita ca parametru, valoare
ce se aduna la suma existenta, și o metodă publică pentru extragerea unei anumite sume, primită de
asemenea ca parametru, valoare ce se scade din suma existenta daca acest fapt e posibil, sau se
afișează mesajul „Sold insuficient !” în caz că rezultatul ar fi un numar negativ. Clasa mai are o
ultima metodă publica care tiparește la consola numele obiectului de tip Persoana, numarul de cont si
suma existenta.

b) În programul principal se declară 3 obiecte, un obiect p cu constructorul implicit (cel fara


parametri), un obiect p2 cu constructorul cu 2 parametri, dând valori arbitrare pentru acesti 2
parametri, si un obiect p3 cu constructorul cu 3 parametri, dand alte valori la alegere pentru
acesti 3 parametri. Pentru obiectul p se setează cu ajutorul metodelor de setare valoarea
numelui ca fiind „maria”, a numarului de cont ca fiind 43534 și suma existenta ca fiind 100. Apoi
se folosesc metodele get pentru a afișa cele 3 câmpuri private setate anterior. Se rulează! Se
afișează și datele din obiectele p2 și p3, dar de data aceasta utilizând funcția publică de tiparire
din clasa. Se rulează!
c) În continuare pentru obiectul p se cere de la tastatura suma de depus și se depune această suma cu
ajutorul metodei de depunere. Analog se cere de la tastatura suma de retras și se extrage aceasta
suma cu ajutorul metodei de extragere. În final se tiparesc datele pentru persoana p în urma
operațiilor de depunere și extragere utilizand de aceasta data metoda publică de tiparire a clasei.

d) Se declara un array de 3 obiecte de tipul Persoana și primul element al array-ului se atribuie cu


obiectul p, al doilea element din array se atribuie cu obiectul p2 și al treilea cu p3. Într-un for care
ciclează de 3 ori se apelează metoda de tiparire pentru fiecare din cele 3 elemente ale array-ului.

10
e) Să se modifice funcția main astfel încât obiectele să se declare dinamic prin pointeri și la fel si
array-ul de obiecte de la punctul d)

f) Să se modifice codul adaugat la punctul d) astfel încît să se creeze o funcție void afiseaza cu un parametru
de tip array cu elemente de tipul Persoana (în funcție facându-se ciclarea în for pentru tiparirea tuturor
celor 3 elemete din array), funcție care să se apeleze din programul principal.

Rezolvare

a) Să se implementeze o clasa Persoana cu membri privați pentru nume, numărul de cont și suma
existentă; un constructor standard și doi constructori expliciți (unul care seteaza toate cele 3
câmpuri private de mai sus cu cele 3 valori primite ca parametru și altul care seteaza numele și
suma existenta cu cele doua valori primite ca parametru și seteaza numarul de cont pe 0).

…………………………….

Clasa mai are metode publice de set și get pentru fiecare câmp din cele 3 campuri private
(metodele set seteaza câmpul respectiv cu valoarea primita ca parametru iar metodele get
returneaza valoarea respectivului câmp).

……………………………………………..
Clasa mai are o metoda publică pentru depunerea unei anumite sume primita ca parametru,
valoare ce se aduna la suma existenta, și o metodă publică pentru extragerea unei anumite sume,
primită de asemenea ca parametru, valoare ce se scade din suma existenta daca acest fapt e
posibil, sau se afișează mesajul „Sold insuficient !” în caz că rezultatul ar fi un numar negativ.
Clasa mai are o ultima metodă publica care tiparește la consola numele obiectului de tip
Persoana, numarul de cont si suma existenta.
…………………………………………

b) În programul principal se declară 3 obiecte, un obiect p cu constructorul implicit (cel fara


parametri), un obiect p2 cu constructorul cu 2 parametri, dând valori arbitrare pentru acesti
2 parametri, si un obiect p3 cu constructorul cu 3 parametri, dand alte valori la alegere
pentru acesti 3 parametri. Pentru obiectul p se setează cu ajutorul metodelor de setare
valoarea numelui ca fiind „maria”, a numarului de cont ca fiind 43534 și suma existenta ca
fiind 100. Apoi se folosesc metodele get pentru a afișa cele 3 câmpuri private setate anterior.
Se rulează! Se afișează și datele din obiectele p2 și p3, dar de data aceasta utilizând funcția
publică de tiparire din clasa. Se rulează!

11
c) În continuare pentru obiectul p se cere de la tastatura suma de depus și se depune această
suma cu ajutorul metodei de depunere. Analog se cere de la tastatura suma de retras și se
extrage aceasta suma cu ajutorul metodei de extragere. În final se tiparesc datele pentru
persoana p în urma operațiilor de depunere și extragere utilizand de aceasta data metoda
publică de tiparire a clasei.
…………………………………
d) Se declara un array de 3 obiecte de tipul Persoana și primul element al array-ului se atribuie
cu obiectul p, al doilea element din array se atribuie cu obiectul p2 și al treilea cu p3. Într-un
for care ciclează de 3 ori se apelează metoda de tiparire pentru fiecare din cele 3 elemente ale
array-ului.
…………………………………….
e) Să se modifice funcția main astfel încât obiectele să se declare dinamic prin pointeri și la fel
si array-ul de obiecte de la punctul d)

f)
………………………………

REZOLVARE

public: void setNume(string n)


{
nume = n;}
string getNume()
{
return nume;}
void setnrCont( int n)
{
nrCont= n;}
int getnrCont()
{
return nrCont;}

void setsumaExistenta ( float n)


{
sumaExistenta= n;}
float getsumaExistenta()
{
return sumaExistenta;}
void depunere(float n)
{
12
sumaExistenta= sumaExistenta + n;}
void retragere(float n)
{
if(sumaExistenta < n) cout << "Sold insuficient!\n";
else sumaExistenta= sumaExistenta - n;}
void tipar()
{
cout<<"Nume:"<< nume<<" Numar cont:"<< nrCont<<" Suma existent:"<<
sumaExistenta<<"\n";}
};

int main(void)
{
float suma;
Persoana p;
Persoana p2("ana", 35.5);
Persoana p3("ion",123, 35);
p.setNume("maria");
p.setnrCont(43534);
p.setsumaExistenta(100);
cout << p.getNume()<<" Numar cont:"<< p.getnrCont()<<" Suma:"<<
p.getsumaExistenta()<<"\n";
p2.tipar();
p3.tipar();
cout<<"Ce suma depuneti?";
cin>>suma;
p.depunere(suma);
cout<<"Ce suma retrageti?";
cin>>suma;
p.retragere(suma);
cout<<"Dupa depunere si retragere situatia este:\n";
p.tipar();
_getch();
Persoana listaPersoane[3];
listaPersoane[0] = p;
listaPersoane[1] = p2;
listaPersoane[2] = p3;
system ("cls");
for(int i=0; i<=2; i++)
{
listaPersoane[i].tipar();
}
_getch();
return 0;
}

Pentru punctul e) int main se modifica astfel:


int main(void)
{
float suma;
Persoana * p=new Persoana;
Persoana * p2=new Persoana("ana", 35.5);
Persoana * p3=new Persoana("ion",123, 35);
p->setNume("maria");
p->setnrCont(43534);
p->setsumaExistenta(100);
cout << p->getNume()<<" Numar cont:"<< p->getnrCont()<<" Suma:"<<p->
getsumaExistenta()<<"\n";
cout<<"Ce suma depuneti?";
cin>>suma;
p->depunere(suma);
cout<<"Ce suma retrageti?";
cin>>suma;
p->retragere(suma);

13
cout<<"Dupa depunere si retragere situatia este:\n";
p->tipar();
_getch();
Persoana * listaPersoane=new Persoana[3];
listaPersoane[0] = *p;
listaPersoane[1] = *p2;
listaPersoane[2] = *p3;
system ("cls");
for(int i=0; i<=2; i++)
{
listaPersoane[i].tipar();
}
_getch();
return 0;
}

Pentru punctul f), aplicația se modifică astfel (modificarile sunt aceleași și dacă se lucrează cu pointeri ca la
punctul e), deoarece un array este de fapt un pointer către primul element din array):
void afiseaza(Persoana lista[])
{
for(int i=0; i<=2; i++)
{
lista[i].tipar();
}
}
int main(void)
{
float suma;
Persoana p;
Persoana p2("ana", 35.5);
Persoana p3("ion",123, 35);
p.setNume("maria");
p.setnrCont(43534);
p.setsumaExistenta(100);
cout << p.getNume()<<" Numar cont:"<< p.getnrCont()<<" Suma:"<<p.getsumaExistenta()<<"\
n";
cout<<"Ce suma depuneti?";
cin>>suma;
p.depunere(suma);
cout<<"Ce suma retrageti?";
cin>>suma;
p.retragere(suma);
cout<<"Dupa depunere si retragere situatia este:\n";
p.tipar();
_getch();
Persoana listaPersoane[3];
listaPersoane[0] = p;
listaPersoane[1] = p2;
listaPersoane[2] = p3;
system ("cls");
afiseaza(listaPersoane);
// for(int i=0; i<=2; i++)
//{
// listaPersoane[i].tipar();
//}
_getch();
return 0;
}

14

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