Sunteți pe pagina 1din 19

3.

Clase (III)
n acest capitol vom continua discuia despre clase i abstractizarea datelor
prezentnd modul n care obiectele pot fi create i sterse dinamic. De asemenea,
vom vedea cum se lucreaz cu obiectele constante i funciile membre const, ce
sunt prietenii unei clase, cum se folosesc datele i funciile membre statice i ce
este pointerul this.

- Obiecte constante i funcii membre const


Principiul restrngerii privilegiilor (principle of least privilege) este unul
fundamental n ingineria software. S vedem cum se aplic el obiectelor.
Unele obiecte trebuie s fie modificabile, altele nu. Programatorul poate folosi
cuvntul cheie const pentru a specifica faptul c un obiect nu poate fi modificat, ca
n exemplul de mai jos n care obiectul noon este constant.
Exemplu
const Time noon(12, 0, 0);
Compilatoarele C++ permit funciilor membre s apeleze obiecte const doar
dac ele nsele sunt declarate de asemenea const. Aceast regul este valabil
inclusiv pentru funcii get care nu modific obiectul. Funciile membre declarate
const nu pot modifica obiectul.
O funcie const trebuie declarat n acest fel att n prototip ct i n definiia sa.
Cuvntul cheie const trebuie plasat dup lista de parametri ai funciei i, atunci cnd
este vorba despre o definiie, inainte de corpul funciei. De exemplu, funcia
int Time::getHour() const { return hour};
ntoarce valoarea datei membre hour a unui obiect de tip Time i, pentru c nu
modific valoarea obiectului este declarat const.
O problem special apare n cazul constructorilor i al destructorilor. Acestora
trebuie s li se permit s modifice obiectele pentru ca s poat face iniializri sau
operaii de tergere.
Declararea obiectelor const accentueaz pricipiul restrngerii privilegiilor, iar
modificrile incorecte ale obiectelor sunt detectate chiar din faza de compilare.
Folosirea obiectelor, a funciilor membre i a datelor membre const este crucial
pentru o proiectare corect a claselor i a programelor, dar i pentru codare.
Declararea variabilelor i obiectelor constante nu este util doar din punct de vedere
al ingineriei software. Compilatoarele care se folosesc azi fac optimizri sofisticate
asupra constantelor care nu pot
fi fcute asupra variabilelor, mbuntind
performanele generale ale aplicaiilor.
Programul de mai jos este o extensie a clasei Time prezentat n capitolele
precedente i exemplific folosirea ei pentru obiecte constante, neconstante i a
funciilor membre const. Compilnd programul, vei observa c n funcia main sunt
dou erori care apar din apelul unor funcii non-const pentru obiecte const.
Exemplu
time5.h
#ifndef TIME5_H
#define TIME5_H
class Time
{
public:

Programarea calculatoarelor i limbaje de programare II

Time(int = 0, int = 0, int = 0); //constructor


//functii set
void setTime(int, int, int);
void setHour(int);
//seteaza hour
void setMinute(int); //seteaza minute
void setSecond(int); //seteaza second
//functii get declarate const
int getHour() const;
//intoarce hour
int getMinute() const; //intoarce minute
int getSecond() const; //intoarce second
//functii print - ambele ar trebui declarate const
void printShort() const; //tiparire in format scurt
void printLong ();
//tiparire in format lung
private:
int hour; //0-23
int minute; //0-59
int second; //0-59
};
#endif
time5.cpp
#include <iostream>
using std::cout;
#include "time5.h"
Time::Time(int hr, int min, int sec)
{
setTime(hr, min, sec);
}
void Time::setTime(int h, int m, int s)
{
setHour(h);
setMinute(m);
setSecond(s);
}
void Time::setHour(int h)
{
hour = (h >= 0 && h < 24) ? h : 0;
}
void Time::setMinute(int m)
{
minute = (m >= 0 && m < 60) ? m : 0;
}

Programarea calculatoarelor i limbaje de programare II

void Time::setSecond(int s)
{
second = (s >= 0 && s < 60) ? s : 0;
}
int Time::getHour() const { return hour; }
int Time::getMinute() const { return minute; }
int Time::getSecond() const { return second; }
void Time::printShort() const
{
cout << (hour < 10 ? "0" : "") << hour << ":"
<< (minute < 10 ? "0" : "") << minute;
}
void Time::printLong()
{
cout << ((hour == 0 ||
<< ":" << (minute
<< ":" << (second
<< (hour < 12 ? "
}
test_time5.cpp
#include "time5.h"

hour == 12) ? 12 : hour % 12)


< 10 ? "0" : "") << minute
< 10 ? "0" : "") << second
AM" : " PM");

int main()
{
Time wakeUp(6, 45, 0);
//obiect non-const
const Time noon(12, 0, 0); //obiect constant
//FUNCTIE MEMBRA
wakeUp.setHour(18); //non-const

OBIECT
non-const

//EROARE:
noon.setHour(12);

//non-const

const

wakeUp.getHour();
noon.getMinute();
noon.printShort();

//const
//const
//const

non-const
const
const

//EROARE:
noon.printLong();

//non-const

const

return 0;
}
Obiectul wakeUp este neconstant, iar noon este constant. Programul apeleaz
funciile membre neconstante noon.setHour(18) i noon.printLong() pentru
obiectul noon care este constant, iar acest lucrul este semnalat de compilator care
genereaz eroare.
Aa cum am mai spus, constructorul nu poate fi const, ns el poate fi apelat
pentru obiecte const, aa cum se ntmpl n cazul obiectului noon. S remarcm
3

Programarea calculatoarelor i limbaje de programare II

faptul c obiectele const nu pot fi modificate prin asignare, de aceea ele trebuie
iniializate. Atunci cnd o dat membr este declarat const, definiia constructorului
clasei trebuie modificat prin adugarea unei liste de iniializare a membrilor. Vom
ilustra iniializarea datelor membre const prin exemplul urmtor n care declarm
clasa Increment cu dou date membre dintre care una este constant.
Exemplu
test_const_data_member.cpp
#include <iostream>
using std::cout;
using std::endl;
class Increment
{
public:
Increment(int c = 0, int i = 1);
void addIncrement(){ count += increment; }
void print();
private:
int count;
const int increment; //data membra const
};
//constructorul clasei Increment
Increment::Increment(int c, int i)
: increment(i)
//initializarea datei membre const increment
{
count =c;
}
void Increment::print()
{
cout << "count = " << count
<< ", increment = " << increment << endl;
}
int main()
{
Increment value(10, 5);
cout << "Inainte de incrementare: ";
value.print();
for(int j = 0; j < 3; j++)
{
value.addIncrement();
cout << "Dupa increment " << j+1 << ": ";
value.print();
}
return 0;
}
4

Programarea calculatoarelor i limbaje de programare II

Rulnd programul obinem urmtorul rezultat:


Inainte de incrementare: count = 10, increment = 5
Dupa increment 1: count = 15, increment = 5
Dupa increment 2: count = 20, increment = 5
Dupa increment 3: count = 25, increment = 5
Prin notaia : increment(i) precizm c data membr increment va fi
iniializat cu valoarea i. Dac sunt necesare mai multe iniializri, acestea sunt
separate prin virgul i atunci avem o list de iniializare.
Toate datele membre ale unei clase pot fi iniializate prin lista de iniializare a
membrilor, ns datele membre const trebuie iniializate n felul acesta. Vom vedea
tot n acest capitol c obiectele membre ale unei clase trebuie iniializate prin aceast
metod. Este vorba despre clasele care conin ca date membre obiecte din alte
clase. n capitolul urmtor vom vedea c, n cazul claselor derivate, partea care
provine din clasa de baz trebuie iniializat tot prin lista de iniializare.
O variant greit a definiiei constructorului clasei Increment, prin care data
membr increment nu este iniializat prin lista de iniializare ci prin asignare, este
urmtoarea:
//EROARE
Increment::Increment(int c, int i)
{
count =c;
increment = i; //nu se poate modifica valoarea unei date
//membre const
}

- Compunerea claselor: Obiecte ca membri ai claselor


Dac am scrie un program pentru un ceas cu alarm, un obiect al clasei
AlarmClock ar trebui s tie cnd trebuie s declaneze alarma. O idee de
proiectare a acestei clase ar fi includerea unui obiect Time ca membru al su. Acesta
este un exemplu de compunere a claselor. O clas poate conine ca membri obiecte
ale altor clase.
Vom descrie conceptul de compunere a claselor printr-un exemplu n care clasa
Employee conine dou obiecte tip Date. Obiectele clasei Employee pstreaz
urmtoarele informaii despre angajaii unei firme: numele, prenumele, data naterii
i data angajrii. Datele membre birthDate i hireDate sunt obiecte const ale
clasei Date i conin, la rndul lor, datele membre month, day i year. Vom
impementa o variant extins a clasei Date din capitolul anterior.
Exemplu
date1.h
#ifndef DATE1_H
#define DATE1_H
class Date
{
public:
//constructor implicit
Date(int = 1, int = 1, int = 1990);
void print() const;
~Date();
private:
int day; //1-12
5

Programarea calculatoarelor i limbaje de programare II

int month;//1-31
int year;
//functie utilitara de testare a
//corectitudinii zilei pentru month si year
int checkDay(int);
};
#endif
date1.cpp
#include <iostream>
using std::cout;
using std::endl;
#include "date1.h"
//constructor fara verificarea valorilor
Date::Date(int d, int m, int y)
{
if(m > 0 && m <= 12)
month = m;
else {
month = 1;
cout << "Luna " << m << " incorecta. "
<< "Valoarea implicita este 1.\n";
}
year = y;
day = checkDay(d); //valideaza ziua
cout << "Constructorul obiectului de tip Date pentru ";
print();
cout << endl;
}
//Tipareste data in forma zi-luna-an
void Date::print() const
{ cout << day << '-' << month << '-' << year; }
//Destructorul
folosit
pentru
confirmarea
stergerii
obiectului
Date::~Date()
{
cout << "Destructorul obiectului de tip Date pentru ";
print();
cout << endl;
}
int Date::checkDay( int testDay )
{
static const int daysPerMonth[13] =
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if( testDay > 0 && testDay <= daysPerMonth[month])
return testDay;
//Februarie: test pentru an bisect
if(month == 2 && testDay == 29 &&
6

Programarea calculatoarelor i limbaje de programare II

(year % 400 == 0 ||
(year % 4 == 0 && year % 100 != 0)))
return testDay;
cout << "Ziua " << testDay << " incorecta. "
<< "Valoarea implicita este 1.\n";
return 1;
}
employee1.h
#ifndef EMPLOYEE1_H
#define EMPLOYEE1_H
#include"date1.h"
class Employee
{
public:
Employee(char*, char*, int, int, int, int, int, int);
void print() const;
~Employee();//destructor folosit la confirmarea ordinii
//in care sunt sterse obiectele
private:
char firstName[25];
char lastName[25];
const Date birthDate;
const Date hireDate;
};
#endif
employee1.cpp
#include <iostream>
using std::cout;
using std::endl;
#include <cstring>
#include "employee1.h"
#include "date1.h"
Employee::Employee(char* fname, char*lname,
int bday, int bmonth, int byear,
int hday, int hmonth, int hyear)
: birthDate(bday, bmonth, byear),
hireDate(hday, hmonth, hyear){
//copiaza fname in firstName
//verificand daca lungimea corespunde
int length = strlen(fname);
length = (length < 25 ? length : 24);
strncpy( firstName, fname, length);
firstName[length] = '\0';
//copiaza lname in lastName
//verificand daca lungimea corespunde
length = strlen(lname);
length = (length < 25 ? length : 24);
7

Programarea calculatoarelor i limbaje de programare II

strncpy( lastName, lname, length);


lastName[length] = '\0';
cout << "Constructorul obiectului Employee: "
<< firstName << ' ' << lastName << endl;
}
void Employee::print() const
{
cout << lastName << ", " << firstName << "\nAngajat: ";
hireDate.print();
cout << " Data nasterii: ";
birthDate.print();
cout << endl;
}
//Destructorul folosit pentru
//confirmarea stergerii obiectului
Employee::~Employee()
{
cout << "Destructorul obiectului de tip Employee: "
<< lastName << ", " << firstName << endl;
}
test_composition.cpp
#include <iostream>
using std::cout;
using std::endl;
#include "employee1.h"
int main()
{
Employee e("Bob", "Jones", 24, 7, 1949, 12, 3, 1988);
cout << '\n';
e.print();
cout << "\nTesteaza constructorul Date "
<< "pentru valori incorecte:\n";
Date d(35, 14, 1994);
cout << endl;
return 0;
}
Rulnd acest program obinem urmtorul rezultat:
Constructorul obiectului de tip Date pentru 24-7-1949
Constructorul obiectului de tip Date pentru 12-3-1988
Constructorul obiectului Employee: Bob Jones
Jones, Bob
8

Programarea calculatoarelor i limbaje de programare II

Angajat: 12-3-1988

Data nasterii: 24-7-1949

Testeaza constructorul Date


Luna 14 incorecta. Valoarea
Ziua 35 incorecta. Valoarea
Constructorul obiectului de

pentru valori incorecte:


implicita este 1.
implicita este 1.
tip Date pentru 1-1-1994

Destructorul obiectului de tip Date pentru 1-1-1994


Destructorul obiectului de tip Employee: Jones, Bob
Destructorul obiectului de tip Date pentru 12-3-1988
Destructorul obiectului de tip Date pentru 24-7-1949
Headerul constructorului clasei Employee din fiierul employee1.cpp conine
i lista de iniializare a obiectelor membre birthDate i hireDate de tip Date:
Employee::Employee(char* fname, char*lname,
int bday, int bmonth, int byear,
int hday, int hmonth, int hyear)
: birthDate(bday, bmonth, byear),
hireDate(hday, hmonth, hyear)
Constructorul are opt parametri. Lista de parametri este urmat de semnul : i de
lista de iniializare a membrilor clasei. Iniializarea membrilor const din numele
acestora, n cazul nostru birthDate i hireDate, i se argumentele care sunt
transmise constructorilor celor dou obiecte de tip Date. Argumentele bday,
bmonth i byear sunt transmise constructorului obiectului birthDate, iar
Argumentele hday, hmonth i hyear sunt transmise constructorului obiectului
hireDate.
Un obiect membru al unei clase nu trebuie iniializat explicit prin lista de
iniializare. Dac nu se face o astfel de iniializare pentru unul dintre obiectele
membre, pentru acesta de apeleaz automat contructorul implicit al clasei sale care il
va iniializa cu valori implicite. Lipsa constructorului implicit al clasei obiectului
membru este semnalat de compilator ca eroare. Dac n exemplul de mai sus nu
am fi iniialitat obiectul birthDate prin lista de iniializare a constructorului clasei
Employee, pentru iniializarea sa ar fi fost apelat automat constructorul cu valori
implicite al clasei Date.
Obiectele membre ale unei clase sunt construite naintea obiectului n care sunt
incluse.
UML permite modelarea asocierii i compunerii claselor. O metod prin care
clasele pot fi relaionate este cea de asociere. Pentru simplificarea diagramelor, UML
permite truncherea simbolul de reprezentare a unei clase pstrnd doar seciunea
care conine numele clasei. Diagrama de mai jos reprezint compunerea claselor
Date i Employee.
1

Employee

Date

Compunerea este o relaie tip parte/ntreg. n exemplul nostru, clasa Employee


conine obiecte ale clasei Date. Clasa Employee este ntregul, iar obiectele clasei
Date sunt pri ale acesteia.
Relaia de asociere dintre cele dou clase este reprezentat printr-o linie.
Rombul negrul arat c asocierea este o relaie de compunere. El este ataat clasei

Programarea calculatoarelor i limbaje de programare II

n compunerea creia intr obiecte din clasa aflat la cealalt extremitate a liniei.
Valorile de multiplicitate arat cte obiecte ale claselor particip la relaie. Conform
diagramei de mai sus, dou obiecte ale clasei Date fac parte din clasa Employee.
Aceasta este o relaie unu-la-doi. n diagramele UML se pot folosi urmtoarele valori
de multiplicitate:
Simbol Semnificaie
0
Niciunul
1
Unu
m
O valoare ntreag
0..1
Zero sau unu
m..n
Cel puin m, dar nu mai multe de n
*
Orice ntreg nenegativ
0..*
Zero sau mai multe
1..*
Unu sau mai multe
Un alt exemplu este cel al claselor Building, Floor i Elevator care fac
parte dintr-o aplicaie care dirijeaz funcionarea unui lift.
1
1
Building
2
Floor

1
2

Services 1

Elevator

n aceast diagram, dou obiecte Floor particip n compunere cu un obiect din


clasa Building. Pe de alt parte, un obiect din clasa Elevator face parte din
clasa Building. Clasele Floor i Elevator sunt ntr-o relaie de asociere. Unei
relaii i se poate da un nume. De exemplu, cuvntul Services de deasupra liniei
care conecteaz clasele Floor i Elevator indic numele asocierii. Sgeata arat
direcia asocierii. Un obiect al clasei Elevator deservete dou obiecte ale clasei
Floor, fiind o asociere unu-la-doi.
Diferena dintre asocierea simpl i compunere este dat de perioada de
existen a obiectelor implicate. n cazul compunerii, obiectele fac parte integrant
din clasa care reprezint ntregul i perioadele lor de existen coincid. ntregul este
responsabil de crearea i distrugerea obiectelor care intr n compunerea lui. Atunci
cnd avem de a face cu o simpl asociere, obiectele parte i ntreg pot avea
perioade de existen diferite. n C++, aceast asociere poate fi modelat prin
includerea n clasa ntreg a unor referine la obiecte din alte clase. O alt metod de
modelare a asocierii simple este folosirea de ctre funciile membre ale clasei ntreg
de parametri de tipul claselor parte.

- Funcii friend i clase friend


O funcie friend a unei clase se definete n afara domeniului clasei, dar are
drept de acces la membrii private (i protected, aa cum vom vedea atunci
cnd vom prezenta motenirea) ai clasei. O funcie sau o clas ntreag pot fi
declarate friend pentru o alta clas.
n aceast seciune vom prezenta un exemplu mecanic, prin care ilustrm strict
sintaxa i modul de acces la membri clasei. ntr-un alt capitol al cursului vom folosi
funciile friend la suprancrcarea unor operatori pentru a putea fi folosii de obiectele
10

Programarea calculatoarelor i limbaje de programare II

unei clase sau la crearea claselor iterator. Obiectele unei clase iterator se folosesc
pentru a selecta elemente succesive sau pentru a efectua operaii asupra
elementelor dintr-un obiect container. Obiectele claselor container pstreaz colecii
de elemente. Funciile friend se folosesc n mod obinuit atunci cnd anumite
operaii nu pot fi implementate prin funcii membre, ca n cazul suprancrcrii
operatorilor.
Declararea unei funcii ca prieten a unei clase se face prin inserarea
prototipului su precedat de cuvntul cheie friend n clasa respectiv. Relaia de
prietenie ntre dou clase, de exemplu ClassOne i ClassTwo, se exprim prin
plasarea declaraiei
friend class ClassTwo;
n definiia clasei ClassOne.
Programul urmtor demonstreaz modul n care se declar i se folosete
funcia global setX care este friend pentru clasa Count. Aceast funcie seteaz
data membr privat x din clasa Count.
Exemplu
test_friend.h
#include <iostream>
using std::cout;
using std::endl;
class Count
{
friend void setX(Count&, int);//declaratie friend
public:
Count(){ x = 0; } // constructor
void print() const { cout << x << endl; }//iesire
private:
int x; //data membra
};
//Poate sa modifice data membra privata a clasei Count
//pentru ca setX este declarata functie friend a clasei Count
void setX( Count& c, int val )
{
c.x = val;
}
int main()
{
Count counter;
cout << "counter.x dupa instantiere: ";
counter.print();
cout << "counter.x dupa apelul functiei friend setX: ";
setX(counter, 8);
counter.print();
return 0;
}

11

Programarea calculatoarelor i limbaje de programare II

Rezultatul rulrii acestui program este:


counter.x dupa instantiere: 0
counter.x dupa apelul functiei friend setX: 8
n acest exemplu, funcia setX nu este membr a clasei Count. Acesta este
motivul pentru care atunci cnd invocm setX pentru obiectul counter folosim
instruciunea
setX(counter, 8);
care l ia pe counter ca argument, neputnd s folosim un apel ca n
counter.setX(8);
Dac definiia clasei Count nu ar conine declaraia friend a funciei setX, astfel:
//Clasa Count modificata: nu contine declaratia friend
//a functiei setX
class Count
{
public:
Count(){ x = 0; } // constructor
void print() const { cout << x << endl; }//iesire
private:
int x; //data membra
};
funcia setX nu ar avea acces la data membr privat x i compilatorul ar genera
eroare, apelul setX(counter, 8); fiind considerat incorect.
Cu toate c specificatorii de acces public, protected i private nu au
nicio influen asupra declaraiilor friend, se prefer ca aceste declaraii s fie
plasate imediat dup headerul clasei.
Exist prerea n comunitatea dezvoltatorilor de aplicaii orientate pe obiecte c
declaraiile friend sunt n contradicie cu ideea de ascundere a informaiei i c
slbete valoarea abordrii orientrii pe obiecte.

- Pointerul this
Fiecare obiect are acces la propria adres de memorie prin intermediul
pointerului numit this. Pointerul this al unui obiect nu este parte a obiectului, el
nefiind reflectat de rezultatul unei operaii sizeof asupra sa. Pe de alt parte, this
este introdus de compilator ca prim argument n fiecare apel al funciilor nestatice
realizat de obiect.
Pointerul this este folosit implicit pentru a referi datele membre i funciile
membre ale unui obiect. El poate fi folosit i explicit. Tipul pointerului this depinde de
tipul obiectului i de caracterul const sau non-const al funciei membre apelate de
obiect. ntr-o funcie membr non-const a clasei Employee, pointerul this are tipul
de dat Employee* const (pointer constant la un obiect Employee). ntr-o funcie
membr constant a clasei Employee, pointerul this are tipul de dat const
Employee* const (pointer constant la un obiect Employee constant).
Exemplul urmtor prezint modul n care se poate folosi pointerul this n mod
explicit. Fiecare funcie membr non-static are acces la pointerul this al
obiectului pentru care este invocat funcia membr. Funcia print din clasa Test
tiprete valoarea datei membre private x prin intermediul pointerului this.
Exemplu
test_this.h
#include <iostream>
12

Programarea calculatoarelor i limbaje de programare II

using std::cout;
using std::endl;
class Test
{
public:
Test( int = 0); // constructor implicit
void print() const;
private:
int x;
};
Test::Test( int a ) { x = a; }
void Test::print() const
{
cout << "
x = " << x
<< "\n this->x = " << this->x
<< "\n(*this).x = " << (*this).x
<< endl;
}
int main()
{
Test testObject(12);
testObject.print();
return 0;
}
La rularea acestui program se obine urmtorul rezultat:
x = 12
this->x = 12
(*this).x = 12
Valoarea datei membre x din obiectul testObject este tiprit n trei moduri.
Mai nti, aceasta este afiat direct. Apoi funcia print folosete dou notaii
diferite pentru a accesa valoarea lui x prin pointerul this: operatorul sgeat ->
pentru pointerul this la obiect i operatorul punct . pentru pointerul this
derefereniat.
Folosirea parantezelor care ncadreaz pointerul derefereniat *this este
obligatorie pentru c operatorul . are preceden mai mare dect *. Fr paranteze,
expresia *this.x ar fi evaluat ca i cum parantezele ar fi plasate dup operatorul
de derefereniere, *(this.x) care este o eroare de sintax pentru c operatorul .
nu poate fi folosit pentru pointeri.

- Alocarea dinamic a memoriei cu operatorii new i


delete
Alocarea dinamic a memoriei nseamn posibilitatea ca programul s obin
mai mult spaiu de memorie n timpul execuiei.

13

Programarea calculatoarelor i limbaje de programare II

Operatorii new i delete sunt folosii n limbajul C++ pentru alocarea i


dealocarea dinamic a memoriei. Ei nlocuiesc apelurile de funcii malloc i free
care erau folosite n limbajul C.
Considerm declaraia
TypeName *typeNamePtr;
Alocarea dinamic a memoriei se poate face n limbajul C prin instruciunea
typeNamePtr = malloc(sizeof(TypeName));
ns funcia malloc nu ofer nicio metod de iniializare a blocului de memorie
alocat. n C++, putem scrie mai simplu:
typeNamePtr = new TypeName;
Prin acest instruciune operatorul new creeaz automat n memorie un obiect cu
dimensiunea dat de tipul de dat invocat, apeleaz constructorul pentru noul obiect
i returneaz un pointer de tipul obiectului. Dac new nu gsete suficient spaiu
pentru obiect, returneaz valoarea 0 dac folosim un compilator anterior standardului
ANSI/ISO C++ sau, pentru compilatoarele care respect acest standard, o excepie
care poate fi tratat n codul client.
Pentru a terge din memorie obiectele alocate dinamic i a elibera memoria se
folosete operatorul delete dac alocarea a fost fcut prin operatorul new sau
funcia free dac am folosit malloc i putem scrie:
delete typeNamePtr;
Operatorul delete apeleaz destructorul clasei din care face parte obiectul a crul
zon de memorie urmeaz s fie eliberat, dac n clas acesta este declarat.
Combinarea alocrii memoriei printr-o metod cu eliberarea acesteia prin cealalt
metod este o eroare de logic a programului. Memoria alocat cu malloc nu este
eliberat de operatorul delete, iar memoria alocata cu new nu este eliberat de
funcia free.
Limbajul C++ permite transmiterea unor valori pentru iniializarea obiectelor
create dinamic:
double *thingPtr = new double(3.1415927);
care iniializeaz noul obiect de tip double cu valoarea 3.1415927.
Tablourile pot fi alocate dinamic folosind modelul urmtor:
int *arrayPtr = new int[10];
iar eliberarea memoriei se face prin instruciunea
delete [] arrayPtr;
Este recomandat folosirea parantezelor drepte pentru eliberarea spaiului ocupat de
tablouri pentru a evita erorile care pot aprea la rularea programului.

- Membrii static ai claselor


Fiecare obiect al unei clase are propria copie a tuturor datelor membre ale
clasei. n anumite cazuri, doar o copie a unei variabile trebuie partajat de toate
obiectele clasei. O dat membr static poate fi folosit n acest scop. Ea
pstreaz o informaie la nivelul clasei i nu specific unui obiect. Declaraia unui
membru static ncepe cu cuvntul cheie static.
Chiar dac datele membre static par s fie similare variabilelor globale, ele
aparin domeniului clasei. Datele membre static pot fi public, private sau
protected. Ele trebuie iniializate o singur dat n domeniul fiier. Un membru
public static al unei clase poate fi accesat printr-un obiect al clasei sau direct
prin numele clasei urmat de operatorul domeniu. Un membru static al unei clase

14

Programarea calculatoarelor i limbaje de programare II

exist chiar dac nu exist n memorie niciun obiect al clasei. Datele membre
static pot fi accesate prin funcii membre doar dac sunt i ele tot static.
Programul de mai jos ilustreaz folosirea datelor membre private static i
a funciilor membre public static. Data membr count din clasa Employee
este declarat static i este iniializat cu 0 n domeniul fiier prin instruciunea
int Employee::count = 0;
Data membr count numr cte obiecte Employee au fost instaniate n program.
Aceast valoare este incrementat de constructorul clasei i este decrementat de
destructor.
Exemplu
employee2.h
#ifndef EMPLOYEE2_H
#define EMPLOYEE2_H
class Employee
{
public:
Employee(const char*, const char*);
~Employee();
const char *getFirstName() const;
const char *getLastName() const;
//functie membra static
//intoarce nr. de obiecte instantiate
static int getCount();
private:
char *firstName;
char *lastName;
//data membra static
//numarul de obiecte instantiate
static int count;
};
#endif
employee2.cpp
#include <iostream>
using std::cout;
using std::endl;
#include <cstring>
#include <cassert>
#include "employee2.h"
//Initializarea datei membre static
int Employee::count = 0;
int Employee::getCount() { return count; }
//Constructorul aloca dinamic spatiu pentru
//nume si prenume si foloseste strcpy pentru
//a copia numele si prenumele in obiect
Employee::Employee(const char *first, const char *last)
{

15

Programarea calculatoarelor i limbaje de programare II

firstName = new char[strlen(first) + 1];


//verifica daca a fost alocata memoria
assert(firstName != 0);
strcpy(firstName, first);
lastName = new char[strlen(last) + 1];
assert(lastName != 0);
strcpy(lastName, last);
++count; //incrementeaza variabila statica
cout << "Constructorul pentru obiectul Employee "
<< firstName << ' ' << lastName << endl;
}
Employee::~Employee()
{
cout << "~Employee() apelat pentru "
<< firstName << ' ' << lastName << endl;
delete [] firstName;
delete [] lastName;
--count; //decrementeaza variabila statica
}
const char *Employee::getFirstName() const
{
//Clientul trebuie sa copieze stringul returnat inainte
//ca destructorul obiectului sa elibereze memoria
return firstName;
}
const char *Employee::getLastName() const
{
//Clientul trebuie sa copieze stringul returnat inainte
//ca destructorul obiectului sa elibereze memoria
return lastName;
}
test_static.cpp
#include <iostream>
using std::cout;
using std::endl;
#include "employee2.h"
int main()
{
//se foloseste numele clasei
cout << "Numarul de angajati inainte de instantiere este "
<< Employee::getCount() << endl;
Employee *e1Ptr = new Employee("Susan", "Baker");
Employee *e2Ptr = new Employee("Robert", "Jones");
16

Programarea calculatoarelor i limbaje de programare II

cout << "Numarul de angajati dupa instantiere este "


<< e1Ptr->getCount();
cout <<
<<
<<
<<
<<
<<
<<

"\n\nAngajatul 1: "
e1Ptr->getFirstName()
" " << e1Ptr->getLastName()
"\nAngajatul 2: "
e2Ptr->getFirstName()
" " << e2Ptr->getLastName()
endl << endl;

delete e1Ptr;
e1Ptr = 0;
delete e2Ptr;
e2Ptr = 0;
cout << "Numarul de angajati dupa stergere este "
<< Employee::getCount() << endl;
return 0;
}
Rezulzatul rulrii programului este:
Numarul de angajati inainte de instantiere este 0
Constructorul pentru obiectul Employee Susan Baker
Constructorul pentru obiectul Employee Robert Jones
Numarul de angajati dupa instantiere este 2
Angajatul 1: Susan Baker
Angajatul 2: Robert Jones
~Employee() apelat pentru Susan Baker
~Employee() apelat pentru Robert Jones
Numarul de angajati dupa stergere este 0
Atunci cnd nc nu a fost instaniat niciun obiect, valoarea datei membre
count nu poate fi referit dect prin apelul funciei membre statice getCount
Employee::getCount();
Dup instanierea obiectului e1Ptr, funcia getCount poate fi apelat i prin
intermediul su. Apelurile e1Ptr->getCount() i Employee::getcount()
produc acelai rezultat.
Spre deosebire de funciile membre non-static, o funcie membr static nu
are pointer this pentru ca datele membre static i funciile membre static
exist independent de obiectele clasei.
Obiectele e1Ptr i e2Ptr sunt alocate dinamic prin operatorul new. Acesta
apeleaza constructorul clasei Employee pentru fiecare dintre ele. Prin folosirea
operatorului delete se dealoc cele dou obiecte i se apeleaz destructorii lor.
Macro-ul assert definit n fiierul header cassert testeaz valoarea unei
condiii. Dac valoarea este fals, se genereaz un mesaj de eroare i programul se
termin prin apelul funciei abort care nu mai apeleaz destructorii. Atunci cnd

17

Programarea calculatoarelor i limbaje de programare II

contiia este adevrat, programul continu. n exemplul de mai sus, dac assert
detecteaz faptul c operatorul new nu a putut aloca memoria, programul se ncheie.

- Clase proxy
Este de dorit ca detaliile de implementare s rmn ascunse clienilor clasei
pentru a preveni accesul la datele private i la logica programului din clas. Oferirea
unei clase proxy care cunoate doar interfaa public a clasei permite clienilor s
foloseasc serviciile clasei fr a avea acces la detaliile de implementare.
Implementarea unei clase proxy presupune mai muli pai, primul fiind definirea
i implementarea clasei ale crei date private trebuie ascunse. Clasa din exemplul de
mai jos se numete Implementation, iar clasa proxy se numete Interface.
Clasa Implementation are o dat membr privat numit value iniializat printrun constructor i dou funcii numite setValue i getValue. Clasa proxy are o
interfa public identic cu cea a clasei Implementation i o singur dat
membr privat numit ptr care este un pointer la clasa Implementation. Folsind
acest pointer, i se ascund clientului detaliile de implementare.
Exemplu
implementation.h
class Implementation
{
public:
Implementation(int v) { value = v; }
void setValue(int v) { value = v; }
int getValue() const { return value; }
private:
int value;
};
interface.h
class Implementation;//declaratie forward
class Interface
{
public:
Interface(int);
void setValue(int); //Aceeasi interfata
int getValue() const;//ca si Implementation
~Interface();
private:
Implementation *ptr; //necesita declaratia forward
};
interface.cpp
#include "interface.h"
#include "implementation.h"
Interface::Interface(int v)
: ptr( new Implementation(v))
{}
void Interface::setValue(int v) {ptr->setValue(v);}

18

Programarea calculatoarelor i limbaje de programare II

int Interface::getValue() const {return ptr->getValue();}


Interface::~Interface() {delete ptr;}
test_interface.cpp
#include <iostream>
using std::cout;
using std::endl;
#include "interface.h"
int main()
{
Interface i(5);
cout << "Interfata contine: " << i.getValue()
<< " inainte de setValue" << endl;
i.setValue(10);
cout << "Interfata contine: " << i.getValue()
<< " dupa de setValue" << endl;
return 0;
}
Clasa Interface este clasa proxy pentru clasa Implementation. Singura
meniune referitoare la clasa Implementation este pointerul ptr din clasa
Interface. Atunci cnd definiia unei clase folosete doar un pointer la o alt clas,
fiierul header care conine clasa referit nu trebuie inclus. Este nevoie doar de o
declaraie forward a clasei.
Deoarece fiierul interface.cpp este transmis clientului doar sub forma
codului obiect compilat, acesta nu poate vedea interaciunile dintre clasa proxy i
clasa proprietar.

19

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

  • Subcircuite Spice
    Subcircuite Spice
    Document4 pagini
    Subcircuite Spice
    AlexandruDîrlău
    Încă nu există evaluări
  • PCLP2 Capitolul7
    PCLP2 Capitolul7
    Document11 pagini
    PCLP2 Capitolul7
    AlexandruDîrlău
    Încă nu există evaluări
  • 11 Autocorelatie
    11 Autocorelatie
    Document24 pagini
    11 Autocorelatie
    Ştefan Pruteanu
    Încă nu există evaluări
  • Dioda Zener
    Dioda Zener
    Document5 pagini
    Dioda Zener
    Marian Baciu
    Încă nu există evaluări
  • LASD1v8 PDF
    LASD1v8 PDF
    Document403 pagini
    LASD1v8 PDF
    AlexandruDîrlău
    Încă nu există evaluări
  • OrCAD Capture
    OrCAD Capture
    Document8 pagini
    OrCAD Capture
    AlexandruDîrlău
    Încă nu există evaluări
  • PCLP2 Capitolul9
    PCLP2 Capitolul9
    Document13 pagini
    PCLP2 Capitolul9
    AlexandruDîrlău
    Încă nu există evaluări
  • PCLP2 Capitolul6
    PCLP2 Capitolul6
    Document12 pagini
    PCLP2 Capitolul6
    AlexandruDîrlău
    Încă nu există evaluări
  • Programare 8
    Programare 8
    Document12 pagini
    Programare 8
    AlexandruDîrlău
    Încă nu există evaluări
  • PCLP2 Capitolul4
    PCLP2 Capitolul4
    Document14 pagini
    PCLP2 Capitolul4
    AlexandruDîrlău
    Încă nu există evaluări
  • PCLP2 Capitolul2
    PCLP2 Capitolul2
    Document11 pagini
    PCLP2 Capitolul2
    Ady Sin
    Încă nu există evaluări
  • Matrice C++
    Matrice C++
    Document2 pagini
    Matrice C++
    AlexandruDîrlău
    Încă nu există evaluări
  • PCLP2 Capitolul3
    PCLP2 Capitolul3
    Document19 pagini
    PCLP2 Capitolul3
    AlexandruDîrlău
    Încă nu există evaluări
  • PCLP2 Capitolul4
    PCLP2 Capitolul4
    Document14 pagini
    PCLP2 Capitolul4
    AlexandruDîrlău
    Încă nu există evaluări
  • PCLP2 Capitolul2
    PCLP2 Capitolul2
    Document11 pagini
    PCLP2 Capitolul2
    Ady Sin
    Încă nu există evaluări
  • PCLP2 Capitolul5
    PCLP2 Capitolul5
    Document12 pagini
    PCLP2 Capitolul5
    AlexandruDîrlău
    Încă nu există evaluări
  • PCLP2 Capitolul5
    PCLP2 Capitolul5
    Document12 pagini
    PCLP2 Capitolul5
    AlexandruDîrlău
    Încă nu există evaluări
  • Laborator 4
    Laborator 4
    Document11 pagini
    Laborator 4
    Cosmin Telescu
    Încă nu există evaluări
  • PCLP2 Capitolul1
    PCLP2 Capitolul1
    Document13 pagini
    PCLP2 Capitolul1
    Emanuel Emy Gavrilă
    Încă nu există evaluări
  • Memoria Interna
    Memoria Interna
    Document12 pagini
    Memoria Interna
    mirelairimia
    Încă nu există evaluări
  • Cap6 AOC Memoria
    Cap6 AOC Memoria
    Document40 pagini
    Cap6 AOC Memoria
    Florescu Gabriel
    Încă nu există evaluări
  • Laborator 4
    Laborator 4
    Document11 pagini
    Laborator 4
    Cosmin Telescu
    Încă nu există evaluări
  • Cap6 AOC Memoria
    Cap6 AOC Memoria
    Document40 pagini
    Cap6 AOC Memoria
    Florescu Gabriel
    Încă nu există evaluări
  • 2014 2015 PCLP1 - Tema2 PDF
    2014 2015 PCLP1 - Tema2 PDF
    Document2 pagini
    2014 2015 PCLP1 - Tema2 PDF
    AlexandruDîrlău
    Încă nu există evaluări
  • T2 DeCo Dec 2014 PDF
    T2 DeCo Dec 2014 PDF
    Document11 pagini
    T2 DeCo Dec 2014 PDF
    AlexandruDîrlău
    Încă nu există evaluări
  • Matrice C++
    Matrice C++
    Document2 pagini
    Matrice C++
    AlexandruDîrlău
    Încă nu există evaluări
  • Cap6 AOC Memoria
    Cap6 AOC Memoria
    Document40 pagini
    Cap6 AOC Memoria
    Florescu Gabriel
    Încă nu există evaluări
  • Matrice C++
    Matrice C++
    Document2 pagini
    Matrice C++
    AlexandruDîrlău
    Încă nu există evaluări
  • T6 DeCo Dec 2014
    T6 DeCo Dec 2014
    Document9 pagini
    T6 DeCo Dec 2014
    AlexandruDîrlău
    Încă nu există evaluări