Sunteți pe pagina 1din 8

Cap. 3.

Clase
3.1. Declararea claselor

O sintaxa simplificata a declararii unei clase este urmatoarea:

class NumeClasa
{
...
declaratii variabile membre
...
declaratii functii membre
...
}

Dupa cum se poate observa din aceasta alcatuire a declaratiei, clasa este
asemanatoare cu o structura din limbajul C, dar care poate avea in componenta
sa membri atat de tip variabila cat si de tip functie. Asa cum spuneam si in
capitolul precedent, pentru datele din interiorul clasei se utilizeaza de obicei
termenul de date membre, iar pentru functii denumirea de functii
membre sau metode. O clasa permite incapsularea in interiorul sau a datelor si
a codului.
Intocmai ca in limbajul C, pentru a putea utiliza efectiv un tip de date (in cazul
de fata o clasa), trebuie sa definim o variabila de acel tip. Intr-un mod similar
declaratiei

int i;

putem scrie:

NumeClasa variabila

Vom considera de acum incolo ca variabila poarta numele de obiect.


Exprimarea uzuala este ca un obiect este instantierea unei clase.
3.2. Membrii unei clase

Accesarea membrilor unei clase se face ca in cazul structurilor din limbajul C:

obiect.VariabilaMembra = valoare

pentru accesul la o variabila membra, si

obiect.FunctieMembra()
pentru apelarea unei functii membre.
Pentru exemplificare sa consideram o implementare a notiunii de punct. Ca
variabile membre avem nevoie doar de coordonatele x si y care definesc pozitia
in spatiu a unui punct. Am mai declarat o functie care calculeaza aria
dreptunghiului avand colturile (0, 0) si (x, y).

class Point
{
unsigned x, y;
unsigned long Arie()
{
return x * y;
};
unsigned GetX();
unsigned GetY();
void SetX(unsigned X);
void SetY(unsigned Y);
};

unsigned Point::GetX()
{
return x;
}

unsigned Point::GetY()
{
return y;
}

void Point::SetX(unsigned X)
{
x = X;
}

void Point::SetY(unsigned Y)
{
y = Y;
}

Am folosit un operator nou, specific C++, ::, numit operator de rezolutie,


numit si operator de acces sau de domeniu. El permite accesul la un
identificator, dintr-un bloc in care acesta nu este vizibil datorita unei alte
declaratii locale. Un exemplu de folosire este urmatorul:
char *sir = "variabila globala";

void functie()
{
char *sir = "variabila locala";
printf("%s\n", ::sir); // afiseaza variabila globala
printf("%s\n", sir); // afiseaza variabila locala
}

Pentru definitiile functiilor membre aflate in afara declaratiei clasei este


necesara specificarea numelui clasei urmat de acest operator, indicand faptul ca
functia are acelasi domeniu cu declaratia clasei respective si este membra a ei,
desi este definita in afara declaratiei.
3.3. Crearea si distrugerea obiectelor

Sa consideram urmatorul program C++:

void main()
{
Point p;
}

In momentul definirii variabilei p, va fi alocat automat spatiul de memorie


necesar, acesta fiind eliberat la terminarea programului. In exemplul de mai
sus, variabila p este de tip static. In continuare vom modifica acest program
pentru a folosi o variabila dinamica (pointer).

void main()
{
Point *p;

p = new Point;
p->x = 5;
p->y = 10;
printf("Aria = %d\n", p->Aria());
delete p;
}

Ati observat utilizarea unor operatori pe care nu ii cunoasteti din limbajul


C: new si delete. C++ introduce o metoda noua pentru gestiunea dinamica a
memoriei, similara celei utilizate in C (malloc() si free()), dar superioara si
special construita pentru programarea orientata pe obiecte. Operatorul new este
folosit pentru alocarea memoriei, iar sintaxa acestuia este:

variabila = new tip;
variabila = new tip(valoare_initiala);
variabila = new tip[n];
Este usor de intuit ca prima varianta aloca spatiu pentru variabila dar nu o
initializeaza, a doua varianta ii aloca spatiu si o initializeaza cu valoarea
specificata, iar a treia aloca un tablou de dimensiune n. Acest operator
furnizeaza ca rezultat un pointer continand adresa zonei de memorie alocate, in
caz de succes, sau un pointer cu valoarea NULL (practic 0) atunci cand alocarea
nu a reusit.
Eliminarea unei variabile dinamice si eliberarea zonei de memorie aferente se
realizeaza cu ajutorul operatorului delete. Sintaxa acestuia este:

delete variabila;

Desi acesti doi operatori ofera metode flexibile de gestionare a obiectelor,


exista situatii in care aceasta nu rezolva toate problemele (de exemplu obiectele
care necesita alocarea unor variabile dinamice in momentul crearii lor). De
aceea pentru crearea si distrugerea obiectelor in C++ se folosesc niste functii
membre speciale, numite constructori si destructori.
Constructorul este apelat automat la instantierea unei clase, fie ea statica sau
dinamica.
Destructorul este apelat automat la eliminarea unui obiect, la incheierea
timpului de viata in cazul static, sau la apelul unui delete in cazul dinamic.
Din punct de vedere cronologic, constructorul este apelat dupa alocarea
memoriei necesare, deci in faza finala a crearii obiectului, iar destructorul
inaintea eliberarii memoriei aferente, deci in faza initiala a distrugerii sale.
Constructorii si destructorii se declara si se definesc similar cu celelalte functii
membre, dar prezinta o serie de caracteristici specifice:
 numele lor coincide cu numele clasei careia ii apartin; destructorii se disting
de constructori prin faptul ca numele lor este precedat de caracterul ~
 nu pot returna nici un rezultat
 nu se pot utiliza pointeri catre constructori sau destructori
 constructorii pot avea parametri, destructorii insa nu. Un constructor fara
parametri poarta denumirea de constructor implicit.
De remarcat este faptul ca in cazul in care o clasa nu dispune de constructori
sau destructori, compilatorul de C++ genereaza automat un constructor
respectiv destructor implicit.
Sa completam in continuare clasa Point cu un constructor si un destructor:

Point::Point()
// constructor implicit
{
x = 0;
y = 0;
}

Point::Point(unsigned X, unsigned Y)
{
x = X;
y = Y;
}

Point::~Point()
{
}

Ati remarcat cu aceasta ocazie modul de marcare a comentariilor in C++: tot ce


se afla dupa caracterul // este considerat comentariu.
De notat este faptul ca definitii de forma
Point p;

sau

Point *p = new Point();

duc la apelarea constructorului implicit.


O intrebare care poate apare este motivul pentru care am realizat
functiile GetX(), GetY(), SetX(), SetY(), cand puteam utiliza direct variabilele
membru x si y. Deoarece una din regulile programarii C++, adoptata in general
de catre specialisti, este de a proteja variabilele membru (veti afla in capitolul
urmator cum), acestea neputand fi accesate decat prin intermediul unor functii,
care au rolul demetode de prelucrare a datelor incapsulate in interiorul clasei.
3.4. Conceptul de mostenire

Daca intrebam un zoolog ce este un caine, ne va raspunde ca este un


reprezentant al speciei canine domesticus. Un caine este un tip de carnivor, un
carnivor este un tip de mamifer, si asa mai departe. Zoologul imparte animalele
in regn, clasa, ordin, familie, gen si specie. Aceasta ierarhie stabileste o relatie
de genul "este un/este o". Putem remarca acest tip de relatie oriunde in aceasta
lume: Mercedeseste un tip de masina, care la randul sau este un tip de
autovehicul, si exemplele pot continua. Astfel, cand spunem ca ceva este un tip
de altceva diferit, spunem ca este o specializare a acelui lucru, asa cum o
masina este un tip mai special de autovehicul.
Conceptul de caine mosteneste, deci primeste in mod automat, toate
caracteristicile unui mamifer. Deoarece este un mamifer, cunoastem faptul ca
se misca si respira aer - toate mamiferele se misca si respira aer prin definitie.
Conceptul de caine aduce in plus ideea de a latra, de a misca coada, si asa mai
departe. Putem clasifica mai departe cainii in caini de paza si caine de
vanatoare, iar cainii de vanatoare in cocker spanioli si dobermani, etc.
Asa cum am vazut mai sus, conceptul de mostenire este o notiune foarte
naturala si pe care o intalnim in viata de zi cu zi. In C++ intalnim notiunea
de derivare, care este in fapt o abstractizare a notiunii de mostenire. O clasa
care adauga proprietati noi la o clasa deja existenta vom spune ca
este derivata din clasa originala. Clasa originala poarta denumirea de clasa de
baza.
Clasa derivata mosteneste toate datele si functiile membre ale clasei de baza; ea
poate adauga noi date la cele existente si poate suprascrie sau adauga functii
membre. Clasa de baza nu este afectata in nici un fel in urma acestui proces de
derivare si ca urmare nu trebuie recompilata. Declaratia si codul obiect sunt
suficiente pentru crearea clasei derivate, ceea ce permite reutilizarea si
adaptarea usoara a codului deja existent, chiar daca fisierul sursa nu este
disponibil. Astfel, nu este necesar ca programatorul unei clase derivate sa
cunoasca modul de implementare a functiilor membre din componenta clasei
de baza.
O notiune noua legata de derivare este cea
de supraincarcare sau suprascriere a functiilor membre. Aceasta se refera, in
mod evident, la redefinirea unor functii a clasei de baza in clasa derivata. De
notat este faptul ca functiile originale din clasa parinte sunt in continuare
accesibile in clasa derivata, deci caracteristicile clasei de baza nu sunt pierdute.
Dintr-o clasa de baza pot fi derivate mai multe clase si fiecare clasa derivata
poate servi mai departe ca baza pentru alte clase derivate. Se poate astfel realiza
o ierarhie de clase, care sa modeleze adecvat sisteme complexe. Pornind de la
clase simple si generale, fiecare nivel al ierarhiei acumuleaza caracteristicile
claselor "parinte" si le adauga un anumit grad de specializare. Mai mult decat
atat, in C++ este posibil ca o clasa sa mosteneasca simultan proprietatile mai
multor clase, procedura numita mostenire multipla. Construirea ierarhiei de
clase reprezinta activitatea fundamentala de realizare a unei aplicatii orientate
obiect, reprezentand in fapt faza de proiectare a respectivului sistem.
Sintaxa simplificata a derivarii este:

class NumeClasaDerivata : NumeClasaDeBaza

In continuare vom deriva din clasa Point o clasa specializata, GraphicPoint,


care va "sti" sa deseneze efectiv punctul pe ecran:

class GraphicPoint : Point


{
unsigned color;

GraphicPoint(unsigned X, unsigned Y, unsigned Color);


~GraphicPoint();
void Draw();
void SetX(unsigned X);
void SetY(unsigned Y);
};

GraphicPoint::GraphicPoint(unsigned X, unsigned Y, unsigned Color) :


Point(X, Y)
{
color = Color;
}

GraphicPoint::~GraphicPoint()
{
}

GraphicPoint::Draw()
{
// ...
// apelare primitive grafice pentru desenarea efectiva a punctului
}

GraphicPoint::SetX(unsigned X)
{
Point::SetX(); // apelul functiei SetX() apartinand clasei de baza
Draw();
}

GraphicPoint::SetY(unsigned Y)
{
Point::SetY();
Draw();
}

Se observa din exemplul de mai sus ca am adaugat o variabila noua fata de


clasa Point, color, pentru a putea memora culoarea cu care se face desenarea
punctului. De asemenea, am suprascris constructorul si destructorul clasei
parinte. In constructorul derivat, am apelat constructorul original folosind
constructia:

ClasaDerivata::ClasaDerivata() : ClasaDeBaza()

In clasa GraphicPoint am adaugat o functie membra noua, Draw(), care


deseneaza efectiv punctul pe ecran. Am suprascris functiile SetX() si SetY(),
apeland in ambele functiile originale, utilizand sintaxa:

ClasaDeBaza::FunctieMembra()

apeland apoi functia de desenare, Draw().


Regulile de functionare ale constructorilor si destructorilor, descrise in
paragraful precedent, raman valabile si in cazul claselor derivate, cu doua
observatii privind ordinea de apelare a acestora:
 la instantierea clasei derivate, se apeleaza mai intai constructorul clasei de
baza, apoi se apeleaza propriul constructor.
 la distrugerea unui obiect al unei clase derivate, este apelat mai intai
propriul constructor, si apoi destructorul clasei de baza (deci in ordine inversa
crearii obiectului).

In capitolul urmator vor fi prezentate notiuni de programare avansata in C++,


cum ar fi controlul accesului la clase, redefinirea operatorilor, mostenirea
multipla si altele.

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