Sunteți pe pagina 1din 26

10 Elemente de programare

orientat obiect

Programarea orientat obiect (Object Oriented Programming - OOP)


reprezint o tehnic ce s-a impus n anii 90, dovedindu-se benefic pentru
realizarea sistemelor software de mare complexitate. Noiunea de obiect dateaz
din anii 60, odat cu apariia limbajului Simula. Exist limbaje ca Smalltalk i
Eiffel care corespund natural cerinelor programrii orientate obiect, fiind
concepute n acest spirit. Recent au fost dezvoltate i alte limbaje orientate obiect,
fie pentru programare general, fie pentru realizarea de scripturi Java, Delphi,
C++, Visual Basic .NET, C#, Python, Ruby. Unele dintre ele ofer n continuare i
posibilitatea programri procedurale (Delphi, C++). Toate limbajele folosite n
prezent ofer i faciliti de programare orientat obiect ADA, Fortran, Cobol,
PHP etc. n prezent, exist n funciune sisteme software de mare anvergur
realizate n tehnica programrii orientat obiect, principiile ei fiind suficient de
bine clarificate, astfel nct s se treac din domeniul cercetrii n cel al produciei
curente de programe.
Acest capitol prezint o introducere n lucrul orientat obiect n limbajul
C++, fr a acoperi toat problematica specific.

10.1 Modelul de date orientat obiect


OOP reprezint o abordare cu totul diferit fa de programarea
procedural, devenit deja clasic. Dac n programarea clasic programatorul
era preocupat s rspund la ntrebarea ce trebuie fcut cu datele?, adic s
defineasc proceduri care s transforme datele n rezultate, n OOP accentul cade
asupra datelor i legturilor ntre acestea, ca elemente prin care se modeleaz
obiectele lumii reale. Se poate afirma, ntr-o prim analiz, c OOP organizeaz un
Elemente de programare orientat obiect

program ca o colecie de obiecte, modelate prin date i legturi specifice, care


interacioneaz dinamic, adic manifest un anumit comportament, producnd
rezultatul scontat. n general, pentru modelul de date orientat pe obiect, se
consider definitorii urmtoarele concepte: abstractizare, obiect, atribut, metod,
clas, spaiu propriu, spaiu extins, ncapsulare, motenire i polimorfism.

Abstractizarea constituie procesul de simplificare a realitii prin reinerea


caracteristicilor i comportamentelor eseniale i constituirea lor ntr-un model
adecvat rezolvrii problemelor.

Obiectul este un model informaional al unei entiti reale, care posed, la


un anumit nivel, o mulime de proprieti i care are, n timp, un anumit
comportament, adic manifest reacii specifice n relaiile cu alte entiti din
mediul su de existen. Ca model, un obiect este o unitate individualizabil prin
nume, care conine o mulime de date i funcii. Datele descriu proprietile i
nivelul acestora, iar funciile definesc comportamentul.
Avnd n vedere proprietile comune i comportamentul similar al
entitilor pe care le modeleaz, obiectele pot fi clasificate n mulimi. O mulime
de obiecte de acelai fel constituie o clas de obiecte, descris prin modelul comun
al obiectelor sale.
De exemplu, n figura 10.1, numerele complexe, ca perechi de numere
reale de forma (parte real, parte imaginar) pot fi descrise printr-un model comun,
denumit ClasaComplex. Modelul arat c orice obiect de acest fel se caracterizeaz
printr-o pereche de numere ntregi i c pe aceast mulime sunt definite operaii
unare i binare care arat cum interacioneaz obiectele n interiorul mulimii: un
numr complex poate da natere modulului i opusului su, dou numere complexe
pot produce un alt numr complex ca sum, produs etc.
Generaliznd, se poate afirma c o clas de obiecte se manifest ca un tip
obiect, iar modelul comun al obiectelor este modelul de definire a tipului obiect.
Astfel, obiectele individuale apar ca manifestri, realizri sau instanieri ale clasei,
adic exemplare particulare generate dup modelul dat de tipul obiect. Altfel spus, o
clas poate fi considerat ca un tip special de dat, iar obiectele sale ca date de acest
tip.

Figura 10.1 Clas i obiecte mulimea numerelor complexe


Programarea calculatoarelor

Acceptarea acestei semnificaii pentru clase de obiecte este de natur s


simplifice descrierea obiectelor i s asigure un tratament al acestora similar
tipurilor structurate de date din limbajele de programare: este suficient o descriere
a tipului obiect i apoi se pot declara constante i variabile de acest tip. Datele care
reprezint proprietile obiectelor se numesc atribute i sunt de un anumit tip (de
exemplu ntregi, reale, caractere etc.). Setul de valori ale atributelor unui obiect la
un moment dat formeaz starea curent a obiectului respectiv. Funciile care
definesc comportamentul obiectelor sunt cunoscute ca metode ale clasei.
mpreun, atributele i metodele sunt membrii clasei, identificabili prin nume.
Pentru a pune n eviden faptul c un membru aparine unui obiect se utilizeaz
calificarea, astfel: nume_obiect.nume_membru. n figura 10.1, a.P_real refer
valoarea 1.0, iar a.Modul refer metoda Modul a obiectului a pentru a produce
obiectul rezultat.
Aa cum sugereaz figura 10.1, fiecare obiect trebuie s conin valorile
atributelor sale, deoarece ele definesc starea obiectului respectiv. Spaiul de
memorie ocupat de atributele unui obiect se numete spaiu propriu al obiectului.
n multe cazuri, ntre atribute se afl pointeri care indic anumite zone de memorie
alocate dinamic pentru obiect (de exemplu, clasa list are ca membru atributul cap
care conine adresa primului nod al unei liste dinamice simplu nlnuite). Acest
spaiu alocat dinamic aparine tot obiectului, dar el se numete spaiu extins al
obiectului. Gestiunea acestui spaiu extins trebuie asigurat de metodele clasei.
Metodele, care descriu aciuni identice pentru toate obiectele clasei, sunt
memorate o singur dat, ntr-o zon comun tuturor obiectelor clasei. ntruct
metodele descriu comportamentele obiectelor, ele nu pot fi apelate independent, ci
numai n legtur cu un anumit obiect. Despre o metod apelat pentru un anumit
obiect se spune c se execut n contextul obiectului respectiv, iar acesta este numit
obiect curent. Apelarea metodei este considerat ca trimitere de mesaj ctre
obiectul curent, iar execuia metodei reprezint rspunsul (reacia) obiectului curent
la mesajul primit.
Faptul c o metod se execut n contextul obiectului curent nseamn c
are, n mod implicit, acces la toate atributele i metodele obiectului. Acestea nu
trebuie s apar ca parametri ai metodei. Pentru a utiliza alte obiecte, din aceeai
clas sau din clase diferite, metoda trebuie s aib parametri corespunztori. De
asemenea, pentru a simplifica scrierea, n interiorul unei metode referirea la
membrii obiectului curent se face fr calificare.
Pe baza acestor convenii, n funciile Conjugat, Suma i Modul, scrise n
pseudocod, s-a specificat cu un parametru mai puin dect numrul de operanzi pe
care i presupune operaia respectiv, deoarece un operand este obiectul curent.
Referirea la atributele obiectului curent se distinge de celelalte prin lipsa calificrii.
Descrierea n pseudocod a metodelor Conjugat, Suma i Modul din clasa
CComplex (figura 10.1) poate fi fcut astfel:
void Conjugat(b);
begin b.p_reala:=p_reala;
b.p_imaginara:=-p_imaginara;
end;
Elemente de programare orientat obiect

void Suma(b,c);
begin
c.p_reala:=p_reala+b.p_reala;
c.p_imaginara:=-p_imaginara+b.p_imaginara;
end;

float Modul();
begin
Modul=sqrt(p_reala*p_reala+p_imaginara*p_imaginara);
end;

Deoarece o clas este un tip de dat, n definirea unei clase B se pot declara
atribute de tip A, unde A este la rndul ei o clas. Mai mult, o clas A poate defini
atribute de tip A. De exemplu clasa Carte, din figura 10.2 are atributul Autor de
tipul Persoana care este, de asemenea, o clas. Mai mult, Persoana are atributul
Sef care este de acelai tip (Persoana).

Figura 10.2 Atribute de tip clas

Definirea atributelor unei clase ca tipuri ale altei clase pune n eviden o
relaie ntre clase i deci ntre obiectele acestora.
Din punct de vedere funcional, metodele unei clase au destinaii diverse.
n multe cazuri i depinznd de limbaj, unei clase i se poate defini o metod (sau
mai multe) constructor i o metod destructor. Un constructor este o metod care
creeaz un obiect, n sensul c i aloc spaiu i/sau iniializeaz atributele acestuia.
Destructorul este o metod care ncheie ciclul de via al unui obiect, elibernd
spaiul pe care acesta l-a ocupat.
ncapsularea exprim proprietatea de opacitate a obiectelor cu privire la
structura lor intern i la modul de implementare a metodelor. Ea este legat de
securitatea programrii, furniznd un mecanism care asigur accesul controlat la
Programarea calculatoarelor

starea i funcionalitatea obiectelor. Se evit astfel modificri ale atributelor


obiectelor i transformri ale acestora care pot s le deterioreze. Potrivit acestui
mecanism, o clas trebuie s aib membrii mprii n dou seciuni: partea public
i partea privat.
Partea public este constituit din membri (atribute i metode) pe care
obiectele le ofer spre utilizare altor obiecte. Ea este interfaa obiectelor clasei
respective cu lumea exterioar i depinde de proiectantul clasei. Modalitatea
extrem de constituire a interfeei este aceea a unei interfee compus numai din
metode. Dac se dorete ca utilizatorii obiectelor clasei s poat prelua i/sau stabili
valorile unor atribute ale acestora, interfaa trebuie s prevad metode speciale,
numite accesorii, care au ca unic rol accesul la atribute.
Partea privat cuprinde membri (atribute i/sau metode) care servesc
exclusiv obiectelor clasei respective. De regul, n aceast parte se includ atribute
i metode care faciliteaz implementarea interfeei i a funcionalitii interne a
obiectului.
De exemplu, o stiv, ca tip de dat poate fi descris de o clas Stiva n care
interfaa este constituit din metodele Push, Pop, Top, Empty, n timp ce pointerul
la capul stivei, Cap i numrtorul de noduri, Contor, ca atribute, sunt ascunse n
partea privat. Ea se servete de obiectele altei clase, denumit Nod, ale crei
obiecte le nlnuiete n stiv (figura 10.3)

Stiva

Cap: Nod
Partea privat
Contor: Integer

Push ( )
Pop ( )
Partea public (Interfaa)
Top ( )
Empty ( )

Figura 10.3 Interfaa obiectelor

Trebuie remarcat c ncapsularea nseamn i faptul c utilizatorul


metodelor nu trebuie s cunoasc codul metodelor i nici nu trebuie s fie
dependent de eventuala schimbare a acestuia, interfaa fiind aceea care i ofer
funcionalitatea obiectelor n condiii neschimbate de apelare.

Motenirea reprezint o relaie ntre clase i este, probabil, elementul


definitoriu al OOP. Relaia permite constituirea unei noi clase, numit derivat
(sau fiu) pornind de la clase existente, denumite de baz (sau printe). Dac n
procesul de construire particip o singur clas de baz, motenirea este simpl,
altfel este multipl.
Elemente de programare orientat obiect

Se spune c o clas D motenete o clas A, dac obiectele din clasa D


conin toate atributele clasei A i au acces la toate metodele acestei clase. Din
aceast definiie, dac D motenete A, atunci obiectele din D vor avea toate
atributele i acces la toate metodele lui A, dar n plus:
D poate defini noi atribute i metode;
D poate redefini metode ale clasei de baz;
metodele noi i cele redefinite au acces la toate atributele dobndite sau
nou definite.
n figura 10.4, clasa Cerc motenete clasa Punct, deci un obiect de tipul
Cerc va avea ca membri coordonatele x,y motenite i ca atribut propriu Raza.
Funcia Distana, definit pentru calculul distanei dintre punctul curent i punctul
p, dat ca parametru, este accesibil i pentru obiectele Cerc i va calcula distana
dintre centrul cercului i un alt punct, primit ca parametru. Funcia Arie calculeaz
aria din interiorul cercului, fiind nou definit. Funcia Deseneaz este redeclarat
de clasa Cerc, lucru impus de codul diferit pe care trebuie s-l aib pentru
desenarea obiectelor din aceast clas (cerc sau alt figur).

Punct
X: int
Y:int X= 100
Deseneaz() Y= 100
Distana(p: punct): float

Cerc
Raza: int
X= 200
Arie():float
Y= 200
Deseneaz()
Raza= 50
Distana(p: punct): float

Figura 10.4 Motenirea simpl.

Dac se au n vedere mulimi de clase, atunci se observ c relaia de


motenire simpl induce un arbore ierarhic de motenire pe aceast mulime. Exist
o singur clas iniial, rdcina arborelui, fiecare clas are un singur ascendent
(printe) i orice clas care nu este frunz poate avea unul sau mai muli
descendeni (fii). n fine, cu privire la motenirea simpl se pot face urmtoarele
observaii:
dac se aduc modificri n clasa de baz, prin adugarea de atribute
i/sau metode, nu este necesar s se modifice i clasa derivat;
motenirea permite specializarea i mbogirea claselor, ceea ce
nseamn c, prin redefinire i adugare de noi membri, clasa derivat are, n parte,
funcionalitatea clasei de baz, la care se adaug elemente funcionale noi;
motenirea este mecanismul prin care se asigur reutilizarea codului
sporind productivitatea muncii de programare.
Programarea calculatoarelor

Cobornd (de obicei, n reprezentrile grafice ale arborilor de clase,


rdcina se afl n partea superioar) n arborele ierarhic al claselor de la rdcin
ctre frunze, se poate spune c ntlnim clase din ce n ce mai specializate. Prin
motenire se realizeaz o specializare a claselor. n sens invers, de la frunz ctre
rdcin, clasele sunt din ce n ce mai generale, avem o relaie de generalizare.
Clasa aflat la baza ierarhiei este cea mai general.
Limbajele de programare orientate obiect au implementate ierarhii standard
extinse de clase, care corespund necesitilor generale ale programrii. Utilizatorii
pot deriva clase noi din cele standard.

Polimorfismul este un concept mai vechi al programrii, cu diferite


implementri n limbajele de programare care se bazeaz pe tipuri de date (limbaje
cu tip). El i-a gsit extensia natural i n modelul orientat pe date, implementat
prin limbaje cu tip, n care clasa reprezint tipul de date obiect.
Polimorfismul n limbajele de programare cu tip. Noiunea de
polimorfism exprim capacitatea unui limbaj de programare cu tip de a exprima
comportamentul unei proceduri independent de natura (tipul) parametrilor si. De
exemplu, o funcie care determin cea mai mare valoare dintr-un ir de valori este
polimorfic dac poate fi scris independent de tipul acestor valori. n funcie de
modul de implementare, se disting mai multe tipuri de polimorfism.
Polimorfismul ad-hoc se materializeaz sub forma unor funcii care au
toate acelai nume, dar se disting prin numrul i/sau tipul parametrilor. Acest
polimorfism este denumit i suprancrcare, avnd n vedere semantica specific
fiecrei funcii n parte.
Polimorfismul de incluziune se bazeaz pe o relaie de ordine parial ntre
tipurile de date, denumit relaie de incluziune sau inferioritate. Dac un tip A este
inclus (inferior) ntr-un tip B, atunci se poate trimite un parametru de tip A unei
funcii care ateapt un parametru de tip B. Astfel, un singur subprogram definete
funcional o familie de funcii pentru toate tipurile inferioare celor declarate ca
parametri. Un exemplu clasic este cazul tipului int, inferior tipului float n toate
operaiile de calcul.
Polimorfism parametric const n definirea unui model de procedur
pentru care chiar tipurile sunt parametri. Acest polimorfism, denumit i
genericitate, presupune c procedura se genereaz pentru fiecare tip transmis la
apel ca parametru.
Cele trei tipuri de polimorfism exist (toate sau numai o parte din ele) n
limbajele clasice de programare, dar unele pot s nu fie accesibile programatorului.
Polimorfismul n limbajele orientate obiect. Limbajele orientate obiect
sau extensiile obiect ale unor limbaje cu tip ofer, n mod natural, polimorfismul
ad-hoc i de incluziune. Polimorfismul ad-hoc intrinsec reprezint posibilitatea de a
defini n dou clase independente metode cu acelai nume, cu parametri identici
Elemente de programare orientat obiect

sau diferii. Acest polimorfism nu necesit mecanisme speciale i decurge simplu,


din faptul c fiecare obiect este responsabil de tratarea mesajelor pe care le
primete. Polimorfismul este de aceeai natur i n cazul n care ntre clase exist o
relaie de motenire, cu precizarea c, n cazul n care o metod din clasa derivat
are parametrii identici cu ai metodei cu acelai nume din clasa de baz, nu mai este
suprancrcare, ci redefinire, dup cum s-a precizat mai sus.
Polimorfimsul de incluziune este legat de relaia de motenire i de aceea
se numete polimorfism de motenire. ntr-adevr, relaia de motenire este o relaie
de ordine parial, astfel nct dac clasa D motenete direct sau indirect clasa A,
atunci D este inferior lui A. n aceste condiii, orice metod a lui A este aplicabil
la obiectele de clas D i orice metod, indiferent de context, care are definit un
parametru de tip A (printe) poate primi ca argument corespunztor (parametru
actual) un obiect de clas D (fiu).
Observaie: un obiect de clas A nu poate lua locul unui obiect de clas D,
deoarece A acoper numai parial pe D, care este o extensie i o specializare a
lui A.
Legare static i dinamic a metodelor. Legarea static a metodelor se
regsete att n limbajele orientate obiect, ct i n cele clasice. Compilatorul poate
determina care metod i din care clas este efectiv apelat ntr-un anumit context
i poate genera codul de apel corespunztor.
Fie o clas A i o clas D, unde D este derivat din A. Fie o metod din
clasa A, numit calculeaz, care este redefinit n clasa derivat, D. Atunci cnd
metoda este apelat n contextul unui obiect static, compilatorul poate determina
tipul acelui obiect (ca fiind parte a clasei A sau D). Astfel, el va ti ce metod s
apeleze (a clasei de baz sau cea redefinit, a clasei derivate). n acest caz are loc o
legare static a metodelor (decizia este luat n momentul compilrii).
Fie un pointer p, definit ca pointer spre clasa A. Datorit polimorfismului,
n limbajele orientate obiect unui obiect din clasa printe, desemnat indirect prin
referin (pointer) i nu prin nume, i se poate atribui un obiect fiu. n acest context,
p poate primi ca valoare, n timpul execuiei programului, adresa unui obiect din
clasa A sau din clasa D. Nu se poate ti la momentul compilrii ce se va ntmpla n
timpul execuiei programului, ca urmare nu se poate determina dac, n contextul
dat, trebuie apelat metoda clasei de baz sau metoda clasei derivate. De aceea, n
locul din program n care este apelat metoda, compilatorul adaug o secven de
cod care, la momentul execuiei, va verifica tipul efectiv al obiectului i, dup caz,
va realiza apelarea metodei adecvate. n acest caz are loc legarea dinamic a
metodelor (sau la momentul execuiei). Legarea dinamic este evident mai
costisitoare dect cea static, dar reprezint o necesitate pentru a asigura
elasticitatea necesar n realizarea programelor OOP, obiectele putnd avea
caracter de variabile dinamice.
Programarea calculatoarelor

10.2 Definirea claselor


Definiia unei clase este asemntoare cu definiia unui articol, ns n
locul cuvntului rezervat struct se folosete cuvntul class:
class nume { descriere membri; };

Membrii unei clase pot fi atribute sau metode. Atributele sunt descrise
asemntor declaraiilor de variabile independente (i asemntor cmpurilor unui
articol struct), specificnd tipul i numele atributului respectiv. Membrii unei
clase pot fi de orice tip, mai puin de acelai tip cu clasa descris (dar pot fi pointeri
ctre clasa descris).
Metodele sunt descrise asemntor funciilor independente. Ele pot fi
descrise integral n interiorul clasei (descriind antetul i corpul lor) sau specificnd
n interiorul clasei doar prototipul funciei, corpul urmnd s fie descris ulterior, n
afara clasei. Este preferat a doua variant, deoarece descrierea clasei este mai
compact dect n primul caz. Atunci cnd se descrie ulterior corpul unei metode,
pentru a specifica apartenena sa la clasa respectiv, numele metodei este prefixat
cu numele clasei din care face parte, folosind operatorul de rezoluie (::), astfel:
tip_rezultat nume_clas::nume_metod(lista parametrilor)
corp metod

Mai mult, funciile care sunt integral descrise n interiorul clasei sunt
considerate funcii inline1, de aceea ele trebuie s fie simple. Pentru funciile mai
complexe, ntotdeauna se recomand s fie descrise folosind a doua variant.

ntruct o clas este un tip de dat, declararea unui obiect se face


asemntor oricrei declaraii de dat:
nume_clas nume_obiect;

Atunci cnd se dorete lucrul cu obiecte dinamice, se poate declara o variabil de


tip pointer ctre clas: nume_clas* p_obiect;
Declararea unui obiect mai este numit i instanierea clasei, n sensul c
se creeaz o instan a acelei clase, o entitate concret din mulimea descris de
clasa respectiv.

1
Apelul funciilor inline nu produce un salt n segmentul de cod ctre codul executabil al funciei, aa
cum se ntmpl n cazul funciilor obinuite. Pentru aceste funcii, compilatorul insereaz n
program, n locul apelului, secvena de cod corespunztoare corpului funciei, nlocuind parametrii
formali cu valorile actuale. Funciile inline au un comportament asemntor macrodefiniiilor.
Elemente de programare orientat obiect

Exemplu
Definirea clasei Complex, care implementeaz entitatea matematic numr
complex. Clasa are atributele p_reala i p_imaginara i o metod pentru afiarea
valorii obiectului afiseaza.
class Complex { float p_reala,p_imaginara;
void Afiseaza();
};

void Complex::Afiseaza()
{ printf("\n%5.2f%ci*%5.2f\n",p_reala,p_imaginara>=0?'+':'-',
p_imaginara>=0?p_imaginara:-p_imaginara);
}

Complex tc;

Metoda afieaz ine cont de semnul prii imaginare. Dac aceasta este negativ,
semnul minus este afiat naintea simbolului i al prii imaginare. Se declar
obiectul tc de tipul Complex.

Accesul la membrii obiectului se face folosind operatorul de calificare:

nume_obiect.nume_membru

unde numele obiectului specific din ce obiect este accesat atributul respectiv sau
n contextul crui obiect se execut metoda respectiv. Acest mod de accesare este
folosit atunci cnd se lucreaz cu obiecte statice. n cazul n care nu avem un obiect
ci un pointer ctre un obiect, este necesar i dereferenierea pointerului, nainte de
accesul la membri. Acest lucru este realizat folosind operatorul -> n locul
operatorului de calificare:

p_obiect -> nume_membru

Pentru implementarea conceptului de ncapsulare, n interiorul unei


definiii de clas pot fi folosii modificatori de acces. Acetia sunt private,
protected i public (urmai de caracterul: dou puncte). Domeniul de aciune al
unul modificator de acces ncepe n locul unde apare el i se ncheie la apariia altui
modificator sau la terminarea descrierii clasei. Implicit, toi membri sunt
considerai sub influena modificatorului private, deci orice membru aflat n afara
domeniului de aciune al unui modificator este considerat privat. Modificatorul
public face ca toi membri aflai n domeniul su de aciune s poat fi accesai att
de ctre metodele clasei, ct i de ctre orice entitate din afara clasei (membri
publici). Modificatorul private face ca membrii aflai n domeniul su de aciune s
poat fi accesai numai de ctre metodele clasei respective (membri privai).
Modificatorul protected este similar cu modificatorul private, dar membrii
respectivi pot fi accesai i de ctre metodele claselor derivate, dar numai n obiecte
aparinnd claselor derivate.
Programarea calculatoarelor

De obicei atributele unei clase sunt declarate ca fiind private, iar metodele
sunt mprite, unele fiind publice (interfaa clasei) i unele private (detalii i
mecanisme interne de implementare a clasei. Dei este tehnic posibil ca toi
membrii unei clase s fie privai, un obiect de acest tip nu poate fi folosit, neavnd
o interfa cu mediul exterior lui. De asemenea, toi membrii unei clase pot fi
publici, dar nu este recomandat aceast tehnic din motive de protecie i
securitate.

Exemplu
n acest context, clasa Complex definit n exemplul anterior nu poate fi folosit,
toi membrii ei fiind privai. Pentru a putea folosi obiecte de tipul Complex, metoda
afieaz trebuie s fie public. Descrierea clasei devine:
class Complex { float p_reala,p_imaginara;
public:
void Afiseaza();
};

void Complex::Afiseaza()
{ printf("\n%5.2f%ci*%5.2f\n",p_reala,p_imaginara>=0?'+':'-',
p_imaginara>=0?p_imaginara:-p_imaginara);
}

Prin adugarea modificatorului de acces public, metoda afieaz este pus la


dispoziia mediului extern, ca interfa a obiectului.

Pentru accesul controlat la atributele private, clasele pot pune la dispoziia


mediului extern metode publice de acces, numite uzual metode accesorii. Acestea
au rolul de a prezenta mediului extern valorile unora dintre atribute (acelea care pot
prezenta interes) sau de a modifica valorile unora dintre atribute, n mod controlat
(numai acele atribute pentru care modificarea la iniiativa mediului extern are sens).
Controlul depinde n fiecare caz de scopul clasei respective.

Exemplu
Adugnd metode accesorii clasei Complex, descrierea acesteia devine:

class Complex { float p_reala,p_imaginara;


public:
void Afiseaza();
float GetR();
float GetI();
void SetR(float r);
void SetI(float i);
};

void Complex::Afiseaza()
{ printf("\n%5.2f%ci*%5.2f\n",p_reala,p_imaginara>=0?'+':'-',
p_imaginara>=0?p_imaginara:-p_imaginara);
}
Elemente de programare orientat obiect

float Complex::GetR()
{ return p_reala;
}
float Complex::GetI()
{ return p_imaginara;
}
void Complex::SetR(float r)
{ p_reala=r;
}
void Complex::SetI(float i)
{ p_imaginara=i;
}

Metodele accesorii definite mai sus (GetR, GetI, SetR, SetI) au rolul de a prezenta
valorile atributelor i respectiv de a stabili noi valori pentru ele. n acest exemplu
nu se face nici un fel de control asupra modului n care sunt stabilite noile valori.
Folosind descrierile de mai sus, urmtoarea secven de program:
void main()
{ Complex tc;
Complex *pc;
tc.SetR(5);
tc.SetI(-4);
tc.Afiseaza();
pc=&tc;
pc->Afiseaza();
pc->SetR(-2);
pc->SetI(3);
pc->Afiseaza();
}
produce pe ecran urmtorul rezultat:
5.00-i* 4.00

5.00-i* 4.00

-2.00+i* 3.00

Pentru a evita eventualele confuzii, n interiorul corpului metodelor poate


fi folosit pointerul implicit this atunci cnd se refer membrii obiectului curent.
Acesta este gestionat automat i are ca valoare adresa obiectului curent. Ca urmare,
metoda SetR ar putea fi rescris astfel:
void Complex::SetR(float r)
{ this -> p_reala = r;
}

Pointerul this poate fi folosit i pentru accesarea metodelor obiectului curent, n


acelai mod.
innd cont de domeniul de valabilitate al declaraiilor de tipuri de date,
descrierea unei clase se face de obicei la nceputul fiierului surs n care urmeaz a
fi folosit. Pentru o mai mare generalitate i reutilizare mai uoar, se prefer ca
fiecare clas nou s fie descris ntr-un fiier surs separat, care s fie inclus
folosind directiva #include n programe.
Programarea calculatoarelor

10.3 Constructori
Declararea obiectelor are ca efect alocarea de spaiu n memorie, la fel ca
n cazul declarrii oricrei variabile. Acest spaiu nu este iniializat ns. Mai mult,
n cazul n care obiectele clasei au i spaiu extins de memorie, acesta nu este alocat
automat, obiectul declarat fiind astfel incomplet. Atributele unui obiect nu pot fi
iniializate la declarare ntr-o manier asemntoare datelor de tip articol (struct),
deoarece de obicei atributele sunt private, deci inaccesibile din exteriorul
obiectului. Pentru rezolvarea problemei iniializrii obiectelor exist posibilitatea
utilizrii unor metode speciale, numite constructori. La terminarea ciclului de via
al obiectelor, este necesar dezalocarea lor. n general, aceasta se realizeaz
automat, dar n cazul lucrului cu spaiu extins, ea trebuie gestionat n mod explicit.
Problema ncheierii ciclului de via al obiectelor este rezolvat prin utilizarea unor
metode speciale numite destructori. Constructorii i destructorii nu ntorc niciun
rezultat prin numele lor i antetele lor nu precizeaz nici un tip pentru rezultat (nici
mcar void).
Constructorii sunt metode care au acelai nume cu clasa creia i aparin. O
clas poate avea mai muli constructori, cu liste diferite de parametri (ca tip i/sau
numr) metode suprancrcate. Dac nu este definit niciun constructor pentru o
clas, compilatorul va genera un constructor implicit, care nu face dect alocarea
spaiului propriu al obiectului, n momentul n care acesta a fost declarat. Ca
urmare, n acest caz vom avea obiecte neiniializate, urmnd ca iniializarea
atributelor s se fac ulterior, prin intermediul metodelor accesorii. n exemplul
anterior, pentru clasa Complex s-a generat un constructor implicit care aloc spaiu
pentru atributele p_reala i p_imaginara. Iniializarea s-a fcut prin intermediul
metodelor SetR i SetI. n cazul n care clasa prezint cel puin un constructor
explicit, compilatorul nu mai genereaz constructorul implicit. Ca urmare nu se vor
putea declara obiecte neiniializate dac parametrii constructorului nu au valori
implicite.
Constructorii nu pot fi apelai explicit, precum metodele obinuite. Apelul
lor se realizeaz numai la declararea obiectelor. De asemenea, nu se poate
determina adresa constructorilor, aa cum se poate face n cazul funciilor
obinuite. Am vzut mai sus c declaraia unui obiect care nu are constructor
explicit este identic cu declaraia unei variabile simple. n cazul n care clasa
prezint constructori explicii valorile pentru iniializare sunt transmise acestuia la
declararea obiectului, asemntor listei de parametri reali la apelul unei funcii:
nume_clas nume_obiect(lista_valori);
Elemente de programare orientat obiect

Exemplu
Pentru clasa Complex se poate defini un constructor care s iniializeze cei doi
membri astfel:
class Complex { float p_reala,p_imaginara;
public:
Complex(float a,float b);
};
Complex::Complex(float a,float b)
{ p_reala=a;
p_imaginara=b;
}
Avnd acest constructor n cadrul clasei, nu putem declara obiecte neiniializate (ca
n exemplele anterioare), ci doar obiecte iniializate:
Complex a(3,4); //a reprezint numarul 3+i*4
Complex b(-1,3.2); //b reprezint numarul -1+i*3.2
Complex c; //incorect

Atunci cnd exist un singur constructor explicit, se aplic regulile


transferului parametrilor similar ca la funciile obinuite (se realizeaz conversia
parametrilor reali ctre tipurile formale). Dac sunt mai muli constructori, cu liste
diferite de parametri, se alege acela a crui list de parametri corespunde ca tip i
numr cu lista valorilor (expresiilor) precizate la declararea obiectului.
Pentru constructori, ca i pentru orice alt funcie, pot fi precizate valori
implicite ale parametrilor. Acolo unde la apel lipsesc parametrii actuali, se folosesc
valorile implicite. Ca urmare, la declararea obiectelor pot s nu fie precizate valori
pentru toate atributele.

Exemplu
Se definete clasa Complex astfel:
class Complex { float p_reala,p_imaginara;
public:
Complex(float a=0,float b=0);
};
Complex::Complex(float a,float b)
{ p_reala=a;
p_imaginara=b;
}
Putem declara urmtoarele obiecte:
Complex a; //a reprezint numarul 0+i*0
Complex b(-1); //b reprezint numarul -1+i*0
Complex c(2,3); //c reprezint numarul 2+i*3

Dac prototipul constructorului ar fi fost Complex(float a,float b=0); atunci


la declaraia obiectului trebuie precizat obligatoriu valoarea pentru primul
parametru (cel care va deveni valoarea atributului p_reala); ca urmare, dintre
declaraiile anterioare ar fi fost corecte numai cele ale obiectelor b i c.
Programarea calculatoarelor

Acolo unde se poate folosi un singur parametru la declararea obiectului, se


poate face iniializarea asemntor iniializrii variabilelor simple. Folosind oricare
dintre cei doi constructori din exemplul anterior, putem declara un obiect i n
modul urmtor:
Complex a = 1; //numarul 1+i*0
Valoarea declarat este atribuit primului parametru al constructorului.
Pentru situaiile n care avem nevoie att de obiecte iniializate, ct i de
obiecte neiniializate, se adaug n descrierea clasei att un constructor care
realizeaz iniializarea obiectului, ct i un constructor vid, care simuleaz
constructorul implicit, adugat de compilator atunci cnd nu se definesc
constructori implicii. Constructorul vid are urmtoarea form:

Complex::Complex()
{
}

n acest context, declaraia Complex a; are ca efect crearea obiectului a


neiniializat (se utilizeaz constructorul vid, nu constructorul cu valori implicite
pentru parametri).
Parametrii unui constructor pot fi de orice tip, mai puin de tipul clasei
respective (n exemplele anterioare, constructorul clasei Complex nu poate avea un
parametru de tipul Complex. Este posibil ns s avem un parametru de tip pointer
ctre clasa respectiv sau referin2 ctre clasa respectiv. Un constructor care
primete ca parametru o referin ctre un obiect din acea clas se numete
constructor de copiere. Prin utilizarea unui constructor de copiere se poate
iniializa un obiect nou cu atributele unui obiect existent (se realizeaz o copie a
acelui obiect). Constructorii de copiere pot avea i ali parametri, dar acetia trebuie
s aib valori implicite. Compilatorul genereaz automat un constructor de copiere
pentru toate clasele care nu au un constructor de copiere definit explicit.

Exemplu
Clasa Complex conine un constructor de copiere:

class Complex { float p_reala,p_imaginara;


public:
Complex(float a=0,float b=0);
Complex(Complex &x);
};

2
n C++ este implementat transferul parametrilor prin adres. Pentru a transmite un parametru prin
adres, n lista de parametri se pune naintea numelui su operatorul de refereniere &
Elemente de programare orientat obiect

Complex::Complex(float a,float b)
{ p_reala=a;
p_imaginara=b;
}
Complex::Complex(Complex &x)
{ p_reala=x.p_reala;
p_imaginara=x.p_imaginara;
}

Putem declara urmtoarele obiecte:


Complex a(3,4); //numarul 3+i*4
Complex b = a; //numarul 3+i*4
Complex c(b); //numarul 3+i*4
Obiectul b este o copie a obiectului a i va avea aceeai stare, imediat dup
declarare (aceleai valori ale atributelor). De asemenea, obiectul c este o copie a
obiectului b. Pentru ambele obiecte s-a apelat constructorul de copiere.
Constructorul implicit de copiere realizeaz o copie binar a spaiului
propriu de memorie al obiectului surs. Ca urmare, dac obiectele au spaiu extins
de memorie, n urma copierii ambele obiecte refer acelai spaiu extins de
memorie. Pentru a evita aceast situaie anormal, atunci cnd clasele descriu
obiecte care lucreaz i cu spaiu extins este necesar folosirea unui constructor de
copiere explicit, care s realizeze, pe lng copierea atributelor, alocarea de spaiu
extins propriu pentru obiectul nou i copierea spaiului extins al obiectului surs n
acest spaiu nou.
Constructorii nu pot fi apelai n mod explicit n program. Singura situaie
n care poate s apar un astfel de apel este la declararea unui obiect, n modul
urmtor:
Complex a = Complex(2,5);

n cazul n care o clas are ca membri obiecte, la declararea unui obiect al


clasei se apeleaz nti constructorii pentru obiectele membru i apoi constructorul
noului obiect.

10.4 Destructori
Destructorii sunt metode speciale, asemntoare constructorilor, care au rol
invers: ncheierea ciclului de via al obiectelor. Aa cum pentru fiecare clas se
genereaz un constructor implicit (dac nu a fost prevzut unul explicit),
compilatorul genereaz i un destructor implicit, dac nu a fost prevzut unul
explicit. Spre deosebire de constructori, o clas poate avea numai un destructor
explicit. Ca i constructorii, destructorii nu ntorc niciun rezultat. Numele
destructorului este numele clasei precedat de caracterul ~ (tilda). Destructorii nu au
parametri.
Programarea calculatoarelor

Exemplu
class Complex { float p_reala, p_imaginara;
public
~Complex();
}
Complex::~Complex()
{ //descrierea corpului destructorului
}
Pentru clasa Complex destructorul nu are nimic de fcut i nu e necesar descrierea
unui destructor explicit. Acest exemplu urmrete doar s arate cum se declar un
destructor explicit.
Spre deosebire de constructori, destructorii pot fi apelai explicit, atunci
cnd este necesar tergerea unui obiect. Apelul se face la fel ca pentru orice alt
metod, n contextul obiectului care trebuie ters:
a.~Complex();
Utilizarea destructorilor este obligatorie atunci cnd se lucreaz cu date
dinamice, deoarece destructorii implicii nu pot elibera spaiul alocat dinamic.
n cazul n care la crearea unui obiect au fost apelai mai muli constructori,
la tergerea lui se apeleaz destructorii corespunztori, n ordine invers.

10.5 Funcii prieten


n unele situaii este nevoie ca funcii care nu sunt membri ai unei clase s
poat accesa atributele protejate ale clasei. n acest scop a fost introdus conceptul
de funcie prieten n C++. O funcie prieten nu face parte din clas, dar poate
accesa atributele protejate.
Pentru a specifica o funcie prieten, n interiorul descrierii clasei se scrie
prototipul funciei prieten, prefixat cu cuvntul rezervat friend. ntruct funcia
prieten nu este membru al clasei, n interiorul su nu este definit pointerul this, ceea
ce face ca funcia prieten s nu poat accesa direct atributele obiectului. De aceea
este necesar ca obiectul s fie parametru al funciei prieten. Ca urmare, o funcie
prieten are un parametru n plus fa de o metod.
Modificatorii de acces nu au nicio influen asupra funciilor prieten, de
aceea ele pot fi specificate oriunde n cadrul descrierii clasei.

Exemplu
n acest exemplu funcia Afieaz va fi scoas n afara clasei Complex i va fi
declarat ca funcie prieten.
class Complex { float p_reala,p_imaginara;
public:
friend void Afiseaza(Complex x);
Complex(float a=0,float b=0);
Complex(Complex &x);
};
Elemente de programare orientat obiect

void Afiseaza(Complex x)
{ printf("\n%5.2f%ci*%5.2f\n",x.p_reala,x.p_imaginara>=0?'+':'-',
x.p_imaginara>=0?x.p_imaginara:-x.p_imaginara);
}
void main()
{ Complex a(1,2);
Afiseaza(a);
}
Exemplu
S se implementeze clasa Stiv dinamic. O list dinamic este format din noduri,
deci putem defini nti clasa Nod, urmnd a folosi tipul Nod pentru a descrie clasa
Stiv. Pentru acest exemplu, datele memorate n nodurile stivei sunt de tip float.
#include <stdio.h>
typedef float TIP_INFO;
class Nod { TIP_INFO info;
Nod* next;
public:
float GetInfo();
Nod* GetNext();
Nod(float a, Nod* n);
};
float Nod::GetInfo()
{ return info;
}
Nod* Nod::GetNext()
{ return next;
}
Nod::Nod(float a, Nod* n)
{ info=a;
next=n;
}
class Stiva { Nod* Cap;
public:
Stiva();
Stiva(float a);
~Stiva();
void Push(float a);
float Pop();
int Empty();
void Afiseaza();
};
void Stiva::Afiseaza()
{ Nod* x;
x=Cap;
while(x)
{ printf("%5.2f ",x->GetInfo());
x=x->GetNext();
}
}
Stiva::~Stiva()
{ Nod* x;
while(Cap)
{ x=Cap;
Cap=Cap->GetNext();
delete x;
}
}
Programarea calculatoarelor

float Stiva::Pop()
{ float x;
Nod* y;
y=Cap;
x=Cap->GetInfo();
Cap=Cap->GetNext();
delete y;
return x;}
void Stiva::Push(float a)
{ Cap=new Nod(a,Cap);
}
int Stiva::Empty()
{ return Cap?0:1;
}
Stiva::Stiva(float a)
{ Cap= new Nod(a,NULL);
}
Stiva::Stiva()
{ Cap=NULL;
}
void main()
{ Stiva s;
int i;
float x;
if(s.Empty()) printf("\nStiva este goala");
else printf("\nStiva contine date");
for(i=0;i<10;i++)
s.Push((float)i);
s.Afiseaza();
x=s.Pop();
s.Afiseaza();
if(s.Empty()) printf("\nStiva este goala");
else printf("\nStiva contine date");
}

Clasa Nod conine atributele info (informaia util din nod) i next, iar ca metode
un constructor care iniializeaz atributele obiectului i metode accesorii pentru
accesarea valorilor atributelor.
Clasa Stiva conine un singur atribut, Cap care are ca valoare adresa primului nod
al stivei (vrful stivei). Constructorii clasei asigur crearea unei stive vide sau a
unei stive cu un element. Metodele asigur adugarea unei informaii n stiv,
respectiv extragerea unei informaii. Metoda Afiseaza asigur afiarea pe ecran a
informaiilor din stiv.

10.6 Derivarea claselor


Derivarea claselor este legat de implementarea conceptului de motenire.
n limbajul C++ este permis motenirea multipl. Pentru a defini o clas fiu ca
fiind derivat dintr-o clas printe (sau mai multe clase printe), se procedeaz
astfel:
class nume_clasa_fiu : lista_clase_printe
{ descriere membri noi ai clasei fiu};
Elemente de programare orientat obiect

n lista claselor printe se specific numele claselor printe, separate prin


virgul i, eventual, precedate de modificatori de acces se pot folosi modificatorii
public sau private. Aceti modificatori de acces definesc nivelul de protecie a
membrilor clasei printe n clasa fiu, conform tabelului urmtor:

Nivel acces Modificator de acces Nivel acces


n clasa printe n lista claselor printe n clasa fiu
public inaccesibil
private
private inaccesibil
public protected
protected
private private
public public
public
private private

Pentru fiecare clas printe se poate specifica un modificator de acces.


Dac nu se specific niciun modificator de acces atunci, implicit, se consider
modificatorul public.
Se observ c o clas derivat are acces la membrii clasei printe care au
fost definii ca fiind publici sau protejai i nu are acces la membrii privai. Prin
derivare se construiesc ierarhii de clase, deci din clasa fiu se pot deriva alte clase
noi. Dac la derivare s-a folosit modificatorul de acces private, atunci toi membrii
clasei printe vor deveni privai n clasa fiu i o derivare n continuare nu mai este
posibil, ei fiind inaccesibili pentru orice alt clas derivat din clasa fiu. ntruct
ierarhiile de clase nu sunt definitive, ci ofer posibilitatea extinderii prin adugarea
de noi clase derivate, se prefer ca la derivare s se foloseasc modificatorul de
acces public. De aceea acesta este modificatorul explicit.

Exemplu
Fie clasa Punct care implementeaz entitatea punct geometric. Aceasta are
atributele x i y, care reprezint coordonatele punctului n plan. Este inclus o
singur metod, care deseneaz punctul pe ecran (aceast metod nu va
implementat n acest exemplu).
class punct { int x,y;
public:
void deseneaza();
};

void punct::deseneaza()
{ //corpul nu este descris in acest exemplu
}
Fie clasa Cerc care implementeaz entitatea geometric cerc. Aceasta este descris
prin coordonatele centrului cercului i raza sa. Ca urmare clasa Cerc poate fi
derivat din clasa Punct, adugnd un nou atribut (raza) i o nou metod, pentru
desenarea cercului.
Programarea calculatoarelor

class cerc: punct


{ float raza;
public:
void deseneaza();
};
void cerc::deseneaza()
{ //corpul nu este descris in acest exemplu
}

Clasa cerc o s aib ca membri atributele x, y i raza i metodele deseneaz


(motenit de la clasa Punct, care va fi folosit pentru desenarea centrului cercului)
i deseneaz (nou definit, care va fi folosit pentru desenarea cercului).

n lucrul cu ierarhii de clase se pune problema compatibilitii tipurilor de


date (clase) n cadrul atribuirilor i a conversiilor tipurilor de date. Ca principiu, un
obiect al unei clase printe poate primi ca valoare un obiect al unei clase derivate.
Acelai principiu este valabil i n cazul pointerilor ctre obiecte. Utiliznd
exemplul de mai sus i declaraiile

punct a, *pp;
cerc b, *pc;

sunt corecte atribuirile

pp=&a;
pc=&b;
a=b;
pp=pc;
pp=&b;
Nu sunt corecte urmtoarele atribuiri:
pc=&a;
b=a;
pc=pp;

Pot fi realizate atribuiri folosind conversia explicit a tipurilor de date, astfel:

pc=(cerc *)pp;
pc=(cerc *)&a;

10.6.1 Redefinirea atributelor

Este posibil ca o clas fiu s redefineasc atribute motenite de la clasa


printe (atribute publice sau protejate, ntruct cele private sunt oricum inaccesibile
n clasa fiu). n acest caz, clasa fiu va avea dou atribute cu acelai nume. Implicit,
utilizarea numelui atributului respectiv refer atributul redefinit. Pentru a accesa
atributul motenit, trebuie folosit operatorul de rezoluie, prefixnd numele
atributului cu numele clasei printe.
Elemente de programare orientat obiect

Exemplu
Fie o clas Clasa_parinte care are un atribut a de tip float, atribut protejat, i o
clas Clasa_fiu care redefinete atributul a, de tip double. x este un obiect de tipul
Clasa_fiu.
class Clasa_parinte
{ protected:
float a;
//descrierea restului clasei
};
Class Clasa_fiu: public Clasa_parinte
{ protected:
double a;
//descrierea restului clasei
}
Clasa_fiu x;

Expresia
x.a
refer atributul a al clasei derivate, de tip double. Pentru a accesa atributul a
motenit de la clasa printe, n cadrul unei metode a obiectului x trebuie folosit
expresia
Clasa_parinte::a

10.6.2 Redefinirea metodelor

La fel ca n cazul atributelor, o clas fiu poate s redefineasc metodele


motenite de la clasa printe, n cazul n care metoda motenit nu corespunde
necesitilor. n exemplul anterior, clasa Cerc redefinete metoda deseneaz. Dac
a este un obiect de tipul Cerc, atunci apelul a.deseneaz(); sau
deseneaz(); efectuat din interiorul clasei Cerc va lansa n execuie metoda
redefinit, cea descris de clasa Cerc, care va desena conturul cercului. Atunci cnd
e nevoie s se apeleze metoda motenit numele acesteia se prefixeaz cu numele
clasei printe, folosind operatorul de rezoluie:
punct::deseneaza();

Un astfel de apel poate s apar n interiorul metodei deseneaz a clasei Cerc


pentru a desena centrul cercului.

10.6.3 Constructori i destructori n relaia de motenire

Constructorii i destructorii nu se motenesc precum alte metode. La


crearea unui obiect al unei clase fiu se apeleaz nti constructorul clasei printe i
apoi constructorul clasei fiu. Dac sunt mai multe clase printe (motenire
multipl) se apeleaz constructorii claselor printe, n ordinea n care acestea apar
n lista claselor printe. La tergerea unui obiect al unei clase fiu se apeleaz
destructorii n ordine invers fa de constructori: nti destructorul clasei fiu
Programarea calculatoarelor

i apoi destructorii claselor printe, n ordine invers celei n care acestea apar n
lista claselor printe.
Pentru a preciza parametrii reali utilizai pentru fiecare din constructorii
claselor printe, antetul constructorului clasei derivate are o form special:
class Clasa_fiu: clasa_p1, clasa_p2, clasa_p3
{ //attribute
public:
Clasa_fiu(); //constructorul clasei fiu
}

Clasa_fiu::clasa_fiu():clasa_p1(),clasa_p2(),clasa_p3()
{ //descrierea corpului constructorului
}

Se observ c n descrierea clasei, constructorul se descrie n mod obinuit,


dar ulterior, n antetul constructorului apar apeluri ale constructorilor claselor
printe. Ordinea n care apar aceste apeluri nu are nici o importan, deoarece
ordinea de apelare este dat de ordinea n care sunt specificate clasele printe.
Dac una din clasele printe nu are constructor, atunci nu o s apar un
apel corespunztor n antetul constructorului clasei fiu, pentru ea apelndu-se
automat constructorul implicit.
Dac niciuna dintre clase nu are constructor explicit, atunci se folosesc
constructorii implicii pentru toate clasele.
O situaie deosebit este aceea n care clasa fiu nu are constructor explicit,
dar cel puin una din clasele printe are un constructor explicit. Deoarece n aceast
situaie nu se pot descrie explicit parametrii pentru apelarea constructorilor claselor
printe, aceti constructori trebuie s aib valori implicite pentru toi parametrii.

10.6.4 Clase virtuale

n cazul motenirii multiple pot s apar situaii n care o clas derivate


motenete un atribut (sau mai multe) de mai multe ori, prin intermediul mai multor
linii de motenire. Aceste situaii produc ambiguiti legate de referirea atributului
respectiv.
Limbajul C++ ofer un mecanism simplu prin care s se revin astfel de
situaii, prin utilizarea claselor virtuale. Fie urmtoare ierarhie, n care clasa cf este
derivat din clasele cp1, cp2 i cp3, toate acestea fiind la rndul lor derivate din
clasa cb. Clasa de la baza ierarhiei, cb are un atribut x. Acest atribut va fi motenit
n clasele cp1, cp2, cp3. Clasa cf va moteni 3 exemplare ale atributului x.
Elemente de programare orientat obiect

Figura 10.5 Exemplu de motenire multipl

Pentru a evita aceast situaie, clasa cb poate fi declarat ca virtual la


descrierea claselor cp1, cp2 i cp3, astfel:

class cp1: virtual public cb


{
};
class cp2: virtual public cb
{
};
class cp3: virtual public cb
{
};
class cf: public cp1, public cp2, public cp3,
{
};

Considernd aceste declaraii, clasa cf motenete o singur dat atributul


x, prin intermediul clasei cp1. Ca principiu, atributul este motenit prin intermediul
clasei care apare prima n lista claselor printe.
n cazul n care n lista claselor printe apar i clase virtuale, se apeleaz
nti constructorii claselor virtuale, n ordinea n care au fost specificate, apoi
constructorii claselor nevirtuale, n ordinea n care sunt acestea specificate.

10.6.5 Funcii virtuale

Exist situaii n care nu se poate decide n momentul compilrii care este


contextul curent n care se apeleaz o metod, care a fost redefinit ntr-o clas fiu.
Astfel de situaii apar atunci cnd se lucreaz cu pointeri. Fie o clas cp care
conine metoda executa, i o clas derivat din ea, numit cf, care redefinete
Programarea calculatoarelor

metoda executa, cu aceeai list de parametri. Fie urmtoarele declaraii:

cp a; //a este obiect de tipul cp


cf b; //b este obiect de tipul cf
cp* po; // po este pointer catre clasa cp

Pointerul po poate lua ca valoare att adresa unui obiect de tipul cp, ct i adresa
unui obiect de tipul cf.
Fie apelul
po->executa();

n momentul compilrii nu se poate stabili ce metod s se apeleze, a clasei printe


sau a clasei fiu. n astfel de situaii compilatorul genereaz un apel ctre metoda
clasei printe.
Limbajul C++ ofer posibilitatea de a ntrzia decizia pn la momentul
execuiei. n acest scop metoda clasei printe se declar ca fiind virtual prin
scrierea cuvntului rezervat virtual naintea antetului su. Este suficient ca metoda
clasei de baz s fie declarat ca fiind virtual, n mod automat i metodele claselor
derivate vor fi virtuale.
Constructorii, destructorii i funciile inline nu pot fi virtuale.

10.6.6 Clase abstracte

n limbajul C++ este definit conceptul de funcie virtual pur. Acest


concept este necesar n cazul ierarhiilor de clase. Exist situaii n care toate clasele
ierarhiei trebuie s conin o anumit metod, implementarea ei fiind diferit n
fiecare clas derivat. Clasa de la baza ierarhiei este prea general pentru a putea
implementa metodele. n aceast situaie n clasa de baz se includ metode virtuale
pure. Prezena unei astfel de metode oblig toate clasele derivate s o conin, fie
c o redefinesc fie c nu.
O metod virtual pur se declar asemntor cu o metod virtual,
adugnd la sfritul antetului =0:

virtual tip_rezultat nume_metoda(lista parametri) =0;

Metodele virtuale pure nu au un corp, nefiind implementate.


O clas care conine o metod virtual pur se numete clas abstract. O
clas abstract nu poate fi instaniat, ea coninnd metode care nu sunt
implementate.
Clasele derivate din clase abstracte pot s redefineasc metodele virtuale
pure sau nu. Dac metoda virtual pur nu este implementat, atunci i clasa
respectiv este clas abstract i nu poate fi instaniat.
Elemente de programare orientat obiect

De exemplu, ntr-o ierarhie de clase care descriu figuri geometrice, fiecare


clas are nevoie de metode pentru desenarea figurii respective, calculul ariei sau
perimetrului. La baza ierarhiei se afl o clas abstract care include metode virtuale
pure pentru aceste operaii. Clasele derivate, vor implementa metodele conform
specificului fiecrei figuri geometrice.