Sunteți pe pagina 1din 13

Programare Obiect-Orientat

Laborator II
Adrian Li / Ovidiu Grigore
2015

Scopul laboratorului

Laboratorul II al materiei Programare Obiect-Orientat are ca scop aprofundarea claselor i definirea constructorilor, destructorului i a operatorului de
copiere.
n acest laborator se parcurg urmtoarele puncte:
namespace-ul standard (std)
pointerul this
constructorul implicit
constructorul cu variabile
destructorul
constructorul de copiere
operatorul de copiere

Desfurarea lucrrii

n C++ apare conceptul de namespace. Acesta este o grupare de clase, variabile, funcii, obiecte, etc., folosit n special pentru a evita folosirea numelor
diferite. Nu vom studia foarte tare acest aspect, ns vom folosi librria iostream, care este organizata n astfel de namespace-uri. Cel pe care-l vom folosi
se numete namespace-ul standard.
1

#i n c l u d e <i o s t r e a m >

2
3
4
5
6
7

i n t main ( )
{
s t d : : c o u t << " Afisam un mesaj " << s t d : : e n d l ;
system ( " pause " ) ;
}

n exemplul de mai sus vrem sa folosim obiectele cout i endl, dar acestea
sunt declarate n contextul namespace-ului std. De aceea trebuie s le apelam
ntotdeauna cu std:: nainte.
Acest lucru se poate evita, specificndu-i compilatorului c vrem s folosim
namespace-ul std n mod implicit: using namespace std;, ca n exemplul de
mai jos:
1
2

#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;

3
4
5
6
7
8

i n t main ( )
{
c o u t << " Afisam un mesaj " << e n d l ;
system ( " pause " ) ;
}

Practic, n toate programele pe care le vom crea mpreuna vom include librria
iostream i vom folosi n mod implicit namespace-ul std.

2.1

Pointerul *this

*this este un pointer ce se gsete ntotdeauna n interiorul unei clase. Acesta


nu poate fi declaratat/redeclarat deoarece C++ marcheaz cuvntul this ca pe
un cuvnt cheie.
this este un pointer ctre obiectul curent. Practic, lucrul acesta se poate traduce
n felul urmtor. O clas, de sine stttoare, fr s aib un obiect definit, este o
entitate abstract care descrie modul de organizare a datelor i descrie funcii ce
se pot folosi asupra sa. Dar, clasa nu este folosibil de una singur, nsemnnd
c, fr s declarm un obiect din acea clas, nu putem folosi datele sau funciile
din ea.
Not. Exist totui o excepie, dac anumite funcii membre ale unei clase nu
folosesc membri variabile ale clasei, atunci acele funcii pot fi folosite i fr a
declara obiecte, dar acest lucru nu face scopul acestui laborator.
Presupunnd c avem clasa Exemplu ca n codul de mai jos, obiectul este
declarat abia la linia 24 (obiectul se numete S). Pn n momentul declarrii
obiectului, pointerul *this nu are sens. Abia dup ce a fost declarat obiectul,
this ia valoarea adresei acestuia (se execut automat operaia this = &s n
momentul construirii).
Practic, la linia 26 se apeleaz funcia set_a() asupra obiectului S: S.set_a(3).
n funcia respectiv, avem (intenionat) un parametru numit tot a, care este
tot double (la fel ca membrul private al clasei). Prin rualrea funciei vrem
ca membrul clasei s ia valoarea dat prin parametrul funciei - a. n funcie
avem doar o singur linie: this->a = a;. Aici avem o copiere a valorii prin
operatorul =. n stnga acestuia se gsete this->a, care reprezint membrul
clasei (declarat la linia 6), iar n dreapta acestuia se gsete parametrul funciei
set_a(double a).
1

#i n c l u d e <i o s t r e a m >

u s i n g namespace s t d ;

3
4
5
6
7
8

c l a s s Exemplu
{
int a ;
public :
int b;

v o i d set_a ( i n t a )
{
t h i s >a = a ;
}

10
11
12
13
14

void a f i s ( )
{
c o u t << " a = " << a << e n d l ;
c o u t << "b = " << b << e n d l ;
}

15
16
17
18
19
20

};

21
22
23
24
25
26
27
28
29

i n t main ( )
{
Exemplu S ;
S . set_a ( 3 ) ;
S . b = 5;
S . a f i s () ;
system ( " pause " ) ;
}

Practic, pointerul this este folosit pentru a putea accesa membri ai clasei care
au acelai nume cu membri ai funciilor. Atenie, this este un pointer. Asta
nseamn c trebuie apelat fie prin operatorul ->, fie prin valoare i operatorul
.(punct): this->membru fie (*this).membru.
Not. De acum nainte vom folosi pointerul this de fiecare dat cnd accesm
membri ai clasei, ca s nu i ncurcm cu alte variabile care au acelai nume.

2.2

Constructorul implicit

Constructorii reprezint o categorie de funcii speciale pe care le poate avea


o clas pentru a creea un obiect. Constructorul este funcia ce se apeleaz n
mod implicit, dac acesta exist, la crearea obiectului. n momentul declarrii
unui obiect, constructorul este primul lucru care se apeleaz. Constructorii sunt
folosii pentru a iniializa membrii clasei, pentru a face alocri de memorie dac
acestea sunt necesare i pentru a face orice fel de pregtire necesar obiectului
nainte ca acesta s fie funcional. Constructorul este funcia care are
exact acelai nume ca i clasa, i constructorii nu returneaz niciun tip
de date. Constructorul implicit nu are niciun parametru.
1
2

#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;

3
4
5

c l a s s Persoana
{

6
7
8

public :
c h a r nume [ 5 0 ] ;
int varsta ;

Persoana ( )
{
s t r c p y ( t h i s >nume , " n e d e f i n i t " ) ;
t h i s >v a r s t a = 0 ;
}

10
11
12
13
14
15

void a f i s ( )
{
c o u t << t h i s >nume << " a r e " << t h i s >v a r s t a << " a n i " << e n d l
;
}

16
17
18

19
20

};

21
22
23
24
25

i n t main ( )
{
Persoana P ;
P . a f i s ( ) ; // n e d e f i n i t a r e 0 a n i

26

s t r c p y (P . nume , " I o n " ) ;


P. varsta = 20;
P . a f i s ( ) ; // I o n a r e 20 a n i

27
28
29
30

n exemplul de mai sus, avem clasa Persoana ce are dou variabile - nume i
varsta - un constructor implicit i o funcie de afiare. Obiectul P, declarat la
linia 24, va apela constructorul n momentul declarrii. Pn s fie apelat constructorul, variabilele nume i varsta aveau valori aleatoare, dar constructorul
le iniializeaz. Afind obiectul, vom confirma acest lucru.

2.3

Constructorul cu parametri

Constructorul cu parametri este similar constructorului implicit, dar are i parametri de iniializare. Exemplul urmtor conine un constructor cu parametri.
1
2

#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;

3
4
5
6
7
8

c l a s s Persoana
{
public :
c h a r nume [ 5 0 ] ;
int varsta ;

9
10
11
12
13
14

Persoana ( c h a r nume , i n t v a r s t a )
{
s t r c p y ( t h i s >nume , nume ) ;
t h i s >v a r s t a = v a r s t a ;
}

15
16
17

void a f i s ( )
{

c o u t << t h i s >nume << " a r e " << t h i s >v a r s t a << " a n i " << e n d l
;

18

19
20

};

21
22
23
24
25

i n t main ( )
{
Persoana P( " D a n i e l a " , 2 5 ) ;
P. a f i s () ;

26

s t r c p y (P . nume , " I o n " ) ;


P. varsta = 20;
P. a f i s () ;

27
28
29
30

Totui, exemplul de mai sus nu este cel mai bun pentru uz general, deoarece clasa
Persoana nu are definit un constructor implicit. Din aceast cauza nu se poate
declara un obiect prin sintaxa: Persoana D;. Mesajul afiat de compilator este
c nu avem declarat constructor implicit. Acest lucru poate fi util n momentul
n care vrem neaprat s nu se poat crea obiecte neiniializate sau iniializate
implicit.
Not. O clas poate avea mai muli constructori, inclusiv mai muli constructori cu parametri, dac aceti parametri difer. C++ ofer posibilitatea suprancrcrii funciilor (i implicit a constructorilor) dac programatorul are
nevoie. Un exemplu de suprancrcare este dac creem o funcie de afiare numit afis(int a) care afieaz numere ntregi, mai putem crea i alte funcii
numite tot afis(...) dar care au ali parametri. Ex: afis(double x) pe care o
facem s afieze un double, afis(int a, double x) care poate afia un int i un
double, etc.
Exemplul de mai jos este unul complex, avnd un constructor implicit i mai
muli constructori cu parametri. ncepnd cu linia 10 este declarat constructorul
implicit, la linia 16 este declarat primul constructor cu parametri i la linia 22
este declarat al 2lea constructor cu parametri.
1
2

#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;

3
4
5
6
7
8

c l a s s Persoana
{
public :
c h a r nume [ 5 0 ] ;
int varsta ;

9
10
11
12
13
14

Persoana ( )
{
s t r c p y ( t h i s >nume , " n e d e f i n i t " ) ;
t h i s >v a r s t a = 0 ;
}

15
16
17
18

Persoana ( c h a r nume )
{
s t r c p y ( t h i s >nume , nume ) ;

t h i s >v a r s t a = 0 ;

19

20
21

Persoana ( c h a r nume , i n t v a r s t a )
{
s t r c p y ( t h i s >nume , nume ) ;
t h i s >v a r s t a = v a r s t a ;
}

22
23
24
25
26
27

void a f i s ( )
{
c o u t << t h i s >nume << " a r e " << t h i s >v a r s t a << " a n i " << e n d l
;
}

28
29
30

31
32

};

33
34
35
36
37

i n t main ( )
{
Persoana P ;
P. a f i s () ;

38

Persoana D( " D a n i e l a " ) ;


D. a f i s ( ) ;

39
40
41

Persoana C( " C o r n e l " , 3 2 ) ;


C. a f i s ( ) ;

42
43
44

n exemplul de mai jos nuanm una dintre cele mai importante funcii ale
constructorului, i anume alocarea dinamic de memorie. Acesta este un bun
exemplu de cum trebuie scris o clas care folosete memorie dinamic, i din
punct de vedere al constructorilor i din punct de vedere al afirii (se verific
daca numele este NULL sau nu).
1
2

#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;

3
4
5
6
7
8

c l a s s Persoana
{
public :
c h a r nume ;
int varsta ;

9
10
11
12
13
14

Persoana ( )
{
t h i s >nume = NULL;
t h i s >v a r s t a = 0 ;
}

15
16
17
18
19
20
21
22
23

Persoana ( c h a r nume , i n t v a r s t a )
{
t h i s >nume = new c h a r [ s t r l e n ( nume ) + 1 ] ;
i f ( t h i s >nume == NULL)
{
c o u t << " E r o a r e l a a l o c a r e " << e n d l ;
exit (0) ;
}

s t r c p y ( t h i s >nume , nume ) ;
t h i s >v a r s t a = v a r s t a ;

24
25

26
27

void a f i s ( )
{
i f ( t h i s >nume != NULL)
c o u t << t h i s >nume << " a r e " << t h i s >v a r s t a << " a n i " <<
endl ;
else
c o u t << " O b i e c t u l e s t e n e i n i t i a l i z a t " << e n d l ;
}

28
29
30
31

32
33
34
35

};

36
37
38
39
40

i n t main ( )
{
Persoana P ;
P. a f i s () ;

41

Persoana C( " C o r n e l " , 3 2 ) ;


C. a f i s ( ) ;

42
43
44

system ( " pause " ) ;

45
46

2.4

Destructorul

Asemntor constructorului, destructorul este o funcie special care se apeleaz


automat n momentul n care obiectul este "distrus". Un obiect este distrus fie n
momentul n care ii pierde contextul (se ncheie funcia din care a fost declarat)
sau dac obiectul a fost creat dinamic (prin alocare dinamic), n momentul n
care se cere tergerea acestuia.
Funcia destructor are ca nume numele clasei precedat de i nu are niciun
parametru. Spre deosebire de constructori, care pot fi mai muli i de mai multe
feluri, destructorul este unul singur, i nu este neaprat necesar dac clasa nu
folosete memorie dinamic. Continund i mbuntind exemplul de mai sus,
i adugm i destructor:
1
2

#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;

3
4
5
6
7
8

c l a s s Persoana
{
public :
c h a r nume ;
int varsta ;

9
10
11
12
13
14

Persoana ( )
{
t h i s >nume = NULL;
t h i s >v a r s t a = 0 ;
}

15
16

Persoana ( c h a r nume , i n t v a r s t a )

17

t h i s >nume = new c h a r [ s t r l e n ( nume ) + 1 ] ;


i f ( t h i s >nume == NULL)
{
c o u t << " E r o a r e l a a l o c a r e " << e n d l ;
exit (0) ;
}
s t r c p y ( t h i s >nume , nume ) ;
t h i s >v a r s t a = v a r s t a ;

18
19
20
21
22
23
24
25

26
27

~Persoana ( ) // d e s t r u c t o r
{
i f ( t h i s >nume != NULL)
d e l e t e [ ] t h i s >nume ;
}

28
29
30
31
32
33

void a f i s ( )
{
i f ( t h i s >nume != NULL)
c o u t << t h i s >nume << " a r e " << t h i s >v a r s t a << " a n i " <<
endl ;
else
c o u t << " O b i e c t u l e s t e n e i n i t i a l i z a t " << e n d l ;
}

34
35
36
37

38
39
40
41

};

42
43
44
45
46

i n t main ( )
{
Persoana P ;
P. a f i s () ;

47

Persoana C( " C o r n e l " , 3 2 ) ;


C. a f i s ( ) ;

48
49
50

n exemplul de mai sus, destructorul este apelat la sfritul funciei main().


Not. Constructorii sunt apelai exact n ordinea declarrii variabielor. Pentru
exemplul de mai sus, nti se apeleaz constructorul obiectului P, apoi constructorul obiectului C, exact n aceast ordine. Destructorii n schimb, se apeleaz
n ordinea invers creeri. nti va fi apelat destructorul lui C, dup care destructorul lui P.

2.5

Constructorul de copiere

Constructorul de copiere este un constructor ce se apeleaz dac n momentul


declarrii unui obiect acesta este i iniializat cu un altul.
Not. Atentie: a nu se confunda constructorul de copiere cu operatorul
de copiere (explicat n capitolul urmtor).
n exemplul de mai jos este artat diferna ntre constructorul de copiere
i operatorul de copiere. Practic, dac semnul = apare n momentul declarrii

obiectului, atunci avem de-a face cu constructorul de copiere. Dac obiectul


este deja creat atunci operatorul de copiere face treaba.
1
2
3
4

c l a s s Persoana
{
...
};

5
6
7
8
9
10
11
12

i n t main ( )
{
Persoana P ;
// c o n s t r u c t o r i m p l i c i t
Persoana C = P ; // c o n s t r u c t o r de c o p i e r e
Persoana D;
// c o n s t r u c t o r i m p l i c i t
D = P;
// o p e r a t o r de c o p i e r e
}

Constructorul de copiere are ca rol simplificarea duplicrii unui obiect. n loc s


se apeleze constructorul implicit i apoi operatorul de copiere, care execut mai
multe instruciuni, se apeleaz constructorul de copiere. Sintaxa constructorului
de copiere, precum i modul de funcionare ale acestuia sunt demonstrate n
codul urmtor.
1
2

#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;

3
4
5
6
7
8

c l a s s Persoana
{
public :
c h a r nume ;
int varsta ;

9
10
11
12
13
14

Persoana ( )
{
t h i s >nume = NULL;
t h i s >v a r s t a = 0 ;
}

15
16
17
18
19
20
21
22
23
24
25
26

Persoana ( c h a r nume , i n t v a r s t a )
{
t h i s >nume = new c h a r [ s t r l e n ( nume ) + 1 ] ;
i f ( t h i s >nume == NULL)
{
c o u t << " E r o a r e l a a l o c a r e " << e n d l ;
exit (0) ;
}
s t r c p y ( t h i s >nume , nume ) ;
t h i s >v a r s t a = v a r s t a ;
}

27
28
29
30
31
32

~Persoana ( )
{
i f ( t h i s >nume != NULL)
d e l e t e [ ] t h i s >nume ;
}

33
34
35

Persoana ( c o n s t Persoana &X) // c o n s t r u c t o r u l de c o p i e r e


{

t h i s >nume = new c h a r [ s t r l e n (X. nume ) + 1 ] ;


i f ( t h i s >nume == NULL)
{
c o u t << " E r o a r e l a a l o c a r e " << e n d l ;
exit (0) ;
}

36
37
38
39
40
41
42

s t r c p y ( t h i s >nume , X. nume ) ;
t h i s >v a r s t a = X. v a r s t a ;

43
44

45
46

void a f i s ( )
{
i f ( t h i s >nume != NULL)
c o u t << t h i s >nume << " a r e " << t h i s >v a r s t a << " a n i " <<
endl ;
else
c o u t << " O b i e c t u l e s t e n e i n i t i a l i z a t " << e n d l ;
}

47
48
49
50

51
52
53
54

};

55
56
57
58
59

i n t main ( )
{
Persoana P ;
P. a f i s () ;

60

Persoana C( " C o r n e l " , 3 2 ) ;


C. a f i s ( ) ;

61
62
63

Persoana D = C ;
D. a f i s ( ) ;

64
65
66

La linia 64 se apeleaz constructorul de copiere, care copiaz obiectul C n


obiectul ce urmeaz a se crea D. De observat este faptul c att cele dou
obiecte, precum i spaiile de memorie ocupate de ele i de membrii fiecruia
sunt complet diferite. Doar valorile membrilor sunt aceleai (de fapt, pointerul
*nume al lui D are o valoare diferit de cel al lui C, pentru c denot o alt
zon de memorie, dar valorile din cele dou zone de memorie sunt identice).

2.6

Operatorul de copiere

Dei operatorul de copiere nu este un constructor, dar este menionat i fcut


neles dup ce se studiaz constructorul de copiere i destructorul. Operatorul
de copiere presupune existena a dou obiecte deja construite, comparativ cu
constructorul de copiere, n care obiectul stng nu era creat. n momentul
apelrii operatorului de copiere, termenul stng (cel n care se va copia noua
valoare) este deja iniializat cu anumite valori, poate ocupa memorie alocat
dinamic, etc. Acest lucru nseamn c operatorul de copiere e responsabil s
gestioneze aceast memorie s nu se piard. Operatorul de copiere primete un
parametru, termenul drept - cel din care se copiaz, i returneaz un tip de
date identic cu obiectul - termenul stng. Operatorul este ntotdeauna aplicat
termenului stng (mai bine zis, rezultatul copierii se gsete n termenul stng,
10

cum este i logic).


1
2

#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;

3
4
5
6
7
8

c l a s s Persoana
{
public :
c h a r nume ;
int varsta ;

9
10
11
12
13
14

Persoana ( )
{
t h i s >nume = NULL;
t h i s >v a r s t a = 0 ;
}

15
16
17
18
19
20
21
22
23
24
25
26

Persoana ( c h a r nume , i n t v a r s t a )
{
t h i s >nume = new c h a r [ s t r l e n ( nume ) + 1 ] ;
i f ( t h i s >nume == NULL)
{
c o u t << " E r o a r e l a a l o c a r e " << e n d l ;
exit (0) ;
}
s t r c p y ( t h i s >nume , nume ) ;
t h i s >v a r s t a = v a r s t a ;
}

27
28
29
30
31
32

~Persoana ( )
{
i f ( t h i s >nume != NULL)
d e l e t e [ ] t h i s >nume ;
}

33
34
35
36
37
38
39
40
41

Persoana ( c o n s t Persoana &X) // c o n s t r u c t o r u l de c o p i e r e


{
t h i s >nume = new c h a r [ s t r l e n (X. nume ) + 1 ] ;
i f ( t h i s >nume == NULL)
{
c o u t << " E r o a r e l a a l o c a r e " << e n d l ;
exit (0) ;
}

42

s t r c p y ( t h i s >nume , X. nume ) ;
t h i s >v a r s t a = X. v a r s t a ;

43
44
45

46
47
48
49
50
51
52

Persoana& o p e r a t o r =( c o n s t Persoana& X)
{
i f ( t h i s != &X)
{
i f ( t h i s >nume != NULL)
d e l e t e [ ] t h i s >nume ;

// o p e r a t o r u l de c o p i e r e

53
54
55
56

t h i s >nume = new c h a r [ s t r l e n (X. nume ) + 1 ] ;


i f ( t h i s >nume == NULL)
{

11

c o u t << " E r o a r e l a a l o c a r e " << e n d l ;


exit (0) ;

57
58

}
s t r c p y ( t h i s >nume , X. nume ) ;
t h i s >v a r s t a = X. v a r s t a ;

59
60
61

62
63

return this ;

64

65
66

void a f i s ( )
{
i f ( t h i s >nume != NULL)
c o u t << t h i s >nume << " a r e " << t h i s >v a r s t a << " a n i " <<
endl ;
else
c o u t << " O b i e c t u l e s t e n e i n i t i a l i z a t " << e n d l ;
}

67
68
69
70

71
72
73
74

};

75
76
77
78
79

i n t main ( )
{
Persoana P ;
P. a f i s () ;

80

Persoana C( " C o r n e l " , 3 2 ) ;


C. a f i s ( ) ;

81
82
83

Persoana D = C ;
D. a f i s ( ) ;

84
85
86

P = D;
P. a f i s () ;

87
88
89

Prima linie de cod din corpul operatorului de copiere reprezint o verificare a


cazului n care se ncearc copierea unui obiect n el nsi (exemplu cod: D =
D;). n acest caz nu este nevoie de nicio operaie propriu-zis, pentru c practic
nu se ntmpl nimic dac o executm.
ncepnd cu linia 51 din codul de mai sus se execut copierea propriu-zis: nti
trebuie dezalocat spaiul de memorie ocupat n prezent de ctre obiectul stng
(care este obiectul curent - this). Dup ce a fost fcut aceast dezalocare, se
trece la copierea efectiv a datelor, ca n cazul constructorului de copiere.
La sfritul copierii, operatorul trebuie, prin sintax, s returneze noua valoare.
Aceast valoare este chiar termenul stng, dat prin this. La linia 87 se exemplific apelarea operatorului de copiere (a nu se confunda cu linia 84, unde este
apelat constructorul de copiere).

Probleme
1. Creai clasa Persoana care s conin *nume i varsta. n main()
creai un vector de pointeri-obiecte de tip Persoana, dup care citii
de la tastatur un numr N de astfel de obiecte alocate dinamic.
12

2. Creai clasa de la exerciiul de mai sus, dup care creai cteva obiecte cu
acest clas, alocate unele static altele dinamic. Distrugei apoi o parte din
obiectele alocate dinamic. Gsii o metod pentru a numra n program
cte obiecte Persoana au fost create n total, i cte obiecte Persoana
mai sunt existente la un moment dat.
3. Creai o clas numit Persoana care s conin urmtorii membri:
*nume
*prenume
anul, luna i ziua naterii
serie i numr carte de identitate
Construii apoi pentru aceast clas urmtoarele metode (funcii):
constructor implicit
constructor cu parametri (cel puin 2 constructori)
constructor de copiere
destructor
operator de copiere
funcie de afiare ( afis() )
funcie ce calculeaz vrsta: int varsta();
funcie de afiare cu parametru dac s afieze i vrsta sau nu (
afis(int afis_varsta) )
Creai astfel obiecte statice i dinamice de tip Persoana pentru a exemplifica i a demonstra c programul funcioneaz corect.

13

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