Documente Academic
Documente Profesional
Documente Cultură
(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.
Î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:
Î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 ():
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
Cexemplu ex (2,3);
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.
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:
CRectangle * pointer;
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:
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
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.
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.
…………………………………………
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
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;
}
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