Sunteți pe pagina 1din 90

UNIVERSITATEA TITU MAIORESCU DIN BUCURE TI

FACULTATEA DE INFORMATIC

Programarea orientat pe obiecte n limbajul C++


-suport curs pentru forma de nvatamnt ID -

Lector univ. drd. Dsclescu Ana Cristina

- 2010 -

Cuprins
Cap. 1 Introducere in programarea orientata pe obiecte ..................................................................3
1.2 Concepte ale programrii orientate pe obiecte.......................................................................5
1.3 Elemente introductive ale programrii orientate pe obiecte n limbajul C++ ......................7
1.4 Tipuri de date .........................................................................................................................9
1.5 Operatori specifici C++........................................................................................................17
Cap.2 Clase si Obiecte .................................................................................................................21
2.1 Definirea unei clase..............................................................................................................23
2.2 Constructori si destructor .....................................................................................................25
2.3 Tablouri de obiecte...............................................................................................................31
2.4 Funcii i clase friend ...........................................................................................................32
2.5 Funcii inline .......................................................................................................................33
2.6 Date si functii statice ............................................................................................................34
Capitolul 3 .Mostenire....................................................................................................................41
3.1 Relatia de mostenire. Clase de baz si clase derivate ..........................................................41
3.2 Constructori i destructori n clasele derivate ......................................................................44
3.3 Controlul accesului la membrii clasei de baz .....................................................................45
3.4 Mostenirea multipla..............................................................................................................47
3.4 Clase de baz virtuale...........................................................................................................50
3.6 Funcii virtuale i polimorfism.............................................................................................51
3.7 Clase abstracte......................................................................................................................54
3.8 Polimorfism..........................................................................................................................56
Cap. 4 Facilitti ale limbajului C++...............................................................................................64
4.1 Supraincarcarea operatorilor ................................................................................................64
4.2 Funcii operator membre ale claselor ...................................................................................64
4. 3 Template-uri ........................................................................................................................68
4.4 Stream-uri de I/E ..................................................................................................................69
4.5 Funcii de I/O pentru tipurile predefinite .............................................................................69
Funcii de I/O pentru tipuri definite de utilizator .......................................................................71
4. 6 Procesarea fisierelor ............................................................................................................72
Teste recapitulative: .......................................................................................................................76

Cap. 1 Introducere in programarea orientata pe obiecte

Obiective:
Conceptul programre orientata pe obiecte;
Paralela ntre programarea procedural si cea orientat pe obiecte;
Tipuri de date abstracte. Abstractizare;
Concepte de baz ale programrii orientate pe obiecte;
Scurta incursiuni n programarea orientat pe obiecte prin intermediul limbajului C++.
Modalitile (tehnicile, paradigmele) de programare au evoluat de-a lungul anilor, reflectnd
trecerea de la programe de dimensiuni reduse, la programe i aplicaii de dimensiuni foarte mari,
pentru coordonarea crora sunt necesare tehnici evoluate.
Software-ul de dimensiuni mari, care nu poate fi realizat de o singur persoan, intr n categoria
sistemelor complexe, cu o mare diversitate de aplicaii.
Situaiile reale i experiene ale psihologilor au relevat limitele capacitii umane n perceperea
sistemelor complexe, adic imposibilitatea unei persoane de a percepe i controla simultan un
numr mare de entiti de informaie. De aceea, este esenial descompunerea i organizarea
sistemelor complexe, pentru a fi percepute, proiectate sau conduse.
Modul n care este abordat programarea, din punct de vedere al descompunerii programelor,
definete mai multe tehnici de programare , care s-au dezvoltat i au evoluat odat cu evoluia
sistemelor de calcul.
Programarea procedural este prima modalitate de programare care a fost i este nc frecvent
folosit. n programarea procedural accentul se pune pe descompunerea programului n
proceduri (funcii) care sunt apelate n ordinea de desfurare a algoritmului. Limbajele care
suport aceast tehnic de programare prevd posibiliti de transfer a argumentelor ctre funcii
i de returnare a valorilor rezultate. Limbajul Fortran a fost primul limbaj de programare
procedural. Au urmat Algol60, Algol68, Pascal, iar C este unul din ultimele invenii n acest
domeniu.
Programarea modular. n cursul evoluiei programrii procedurale, accentul n proiectarea
programelor s-a deplasat de la proiectarea procedurilor ctre organizarea datelor, aceast
deplasare reflectnd creterea dimensiunilor programelor. O mulime de proceduri corelate,
mpreun cu datele pe care le manevreaz, sunt organizate ca un modul. Tehnica de programare
modular decide descompunerea unui program n module, care ncorporeaz o parte din datele
programului i funciile care le manevreaz. Aceast tehnic este cunoscut ca tehnic de
ascundere a datelor (data-hiding). n programarea modular stilul de programare este n
3

continuare procedural, iar datele i procedurile sunt grupate n module, existnd posibilitatea de
ascundere a unor informaii definite ntr-un modul, fa de celelalte module. Gruparea de date i
proceduri n module nu implic i o asociere strict ntre acestea.
Programarea orientat pe obiecte apeleaza la o modalitate nou de gndire a unei probleme.
Spre deosebire de programarea procedural care se concentreaz pe structuri de date si algoritmi,
programarea orientat pe obiecte se concentreaz pe definirea de obiecte care modeleaz
problema ce trebuie rezolvat.
n programarea orientat pe obiecte (POO) un program are rolul de a simula strile si activittile
obiectelor lumii reale. Pe lng structuri de date (care descriu strile obiectelor, atributele
acestora) trebuie incluse si metodele asociate obiectelor, adic acele functii care modific
atributele obiectelor si care descriu comportamentul lor.

1.1 Abstractizarea datelor. Tipuri de date abstracte


Primul pas pe care il facem cand scriem un program care sa realizeze diferite operatii, este sa
gasim un model care simplifica realitatea, prin separearea detaliilor care interreseaza, de cele care
nu afecteaza problema pe care o rezolvam asfel, datele cu care se lucreaza, operatiile , tin de
specificul fiecarei probleme tratate.
Acest proces de grupare a datelor si metodelor de prelucrare specifice rezolvarii unei probleme se
numeste abstractizare.
n cazul dezvoltrii unui produs software, abstractizarea se poate defini ca fiind o structurare a
unei probleme n entitti bine precizate prin definirea datelor si a operatiilor asociate. Aceste
entitti combin date si operatii care sunt necuplate ntre ele. Procedurile si functiile au fost
primele dou mecanisme de abstractizare larg rspndite n limbajele de programare.
Procedura a reprezentat si prima modalitate de ascundere a informatiei (interfata cu procedura).
Modulele au reprezentat urmtorul mecanism de abstractizare prin gruparea procedurilor si
functiilor ce sunt relationate. n continuare, tipurile de date abstracte au realizat un pas important
ctre dezvoltarea unui software de calitate si au permis trecerea la abordarea orientat pe obiecte
a problemelor.
Un tip de date abstract (TDA) const dintr-o structur de date abstract si o multime de operatii.
Interfata unui TDA este definit de multimea de operatii si reprezint acea portiune a unui TDA
care este vizibil din exterior. Conceptul de tip de date abstract presupune existenta unui
mecanism de separare a specificatiei de implementare. Aceasta nseamn c un utilizator al unui
TDA trebuie s cunoasc doar modul de utilizare al
tipului de date, nu si detaliile de implementare.
Un tip de date abstract (TDA) este caracterizat de urmtoarele proprietti:
1. export un tip;
2. export o multime de operatii (furniznd interfata TDA);
3. singurul mecanism de acces la structura de date a tipului este furnizat de operatiile definite n
interfat;
4. axiomele si preconditiile definesc domeniul deaplicatie al tipului.

De exemplu, dorim sa construim TDA persona. Sa presupunem ca realizam o aplicatie necesara


pentru realizarea recesamnatului populatiei, atunci datele care interseaza pentru tipul persoana
sunt : nume, prenume, loc_nastere, adresa etc. Daca aplicatia presupune, in schimb, gestiunea
intretinerii pentru o asociatie de locatari, atunci pentru tipul persoana sunt necesare si date cum ar
fi : spatiul_locuit, nr_persoane etc.
Deci, prin procesul de abstarctizare, separam datele care intereseaza de cele care nu fac obiectul
aplicatiei. Construim, ulterior, un tip abstract de date care inglobeaza o structura de date
impreuna ca operatii aupra datelor ( ex: calculul intretinerii pentru o persoana, nr_persoane pe
fiecare judet etc.).
Exemplu: TDA_persona
nume
prenume
spatiu_locuit

structura de date

calcul intretinere; - operatii asupra datelor


Programarea orientat pe obiecte este programarea cu tipuri de date abstracte, care combin
functionalittile acestora pentru rezolvarea problemelor. n programarea orientat pe obiecte,
TDA-urile sunt numite clase.

1.2 Concepte ale programrii orientate pe obiecte


Conceptele programrii orientate pe obiecte au aprut din dezvoltrile realizate n cadrul
limbajelor moderne de programare. Astfel, limbajele orientate pe obiecte au noi structuri care
mbunttesc ntretinerea programului si fac ca portiuni mari de program s fie reutilizabile,
conducnd astfel la scderea costului de dezvoltare a produselor software.
Cele sase concepte de baz ce caracterizeaz programarea orientat pe obiecte sunt :
Obiectele;
Clasele;
Mesajele
Incapsularea;
Mostenirea
Polimorfismul.
Obiectele
Un obiect poate fi considerat ca fiind o entitate care ncorporeaz att structuri de date (denumite
atribute) ct si comportament (actiuni). Obiectele sunt inteligente prin faptul c realizeaz
anumite actiuni si stiu cum s execute aceste actiuni. Inteligenta unui obiect reprezint o form
de abstractizare prin faptul c presupune c un obiect poate executa o actiune si ascunde detaliile
referitoare la modul n care se va realiza efectiv actiunea. Obiectele pot fi de tipuri diferite:
entitti fizice, algoritmi, relatii sau subsisteme. Practic, obiectele sunt componente de software
reutilizabil care modeleaz elemente din lumea real.
Pentru tipul de date abstract persoana definit mai sus un exemplu de obiect poate fi definit astfel :

Popescu Ion, 34, 56 m2 .


Clasele
Clasele desemneaz o colecie de obiecte (de natur material sau spiritual) care au n comun
faptul c pot fi caracterizate similar din punct de vedere informaional i comportamental.
Este evident faptul c identificarea unei clase este n mod normal, rezultatul unui demers cognitiv
care presupune caracterizarea unui obiect prin nsuirile lui (informaionale i comportamentale)
care i definesc apartenena la o anumit clas de obiecte. Aadar, conceptul de clas adun
laolalt datele i metodele de prelucrare a acestora.
O clasa reprezinta de fapt o implemntare a tipului abstract de date. O declarare a unei clase
definete un tip nou care reunete date i funcii. Acest tip nou poate fi folosit pentru a declara
obiecte de acest tip, deci un obiect este un exemplar (o instan) a unei clase.

Mesajele
Obiectele pot comunica ntre ele prin intermediul mesajelor. Trimiterea unui mesaj care cere unui
obiect s aplice o anumit actiune numit metod este similar apelului de procedur din
limbajele de programare procedural. n programarea orientat pe obiecte se consider c
obiectele sunt autonome si pot comunica ntre ele prin interschimb de mesaje. Obiectele
reactioneaz atunci cnd primesc mesaje, aplicnd o anumit metod, de exemplu. Ele pot refuza
executarea metodei respective dac, de exemplu, obiectului care este apelat nu i se permite s
execute metoda cerut.
Un mesaj este o cerere adresat unui obiect pentru a invoca una din metodele sale. Astfel, un
mesaj contine numele metodei si argumentele metodei.
Exemplul : calculul intretinerii pentru obiectul Popescu Ion.
Incapsularea
nelegerea acestui principiu presupune dou nivele de abordare.
@ Ca metod de concepie, ncapsularea se refer la capacitatea de a separa aspectele externe ale
unui obiect (interfaa), accesibile altor obiecte, de aspectele implementaionale, interne
obiectului, care sunt ascunse fa de celelalte obiecte. Utilizatorul unui obiect poate accesa doar
anumite metode ale acestuia, numite publice, n timp ce atributele i celelalte metode i rmn
inaccesibile (acestea se numesc private).
ncapsularea este foarte important atunci cnd dorim s schimbm implementarea anumitor
metode (cu scopul de a optimiza un algoritm sau de a elimina posibile erori).
ncapsularea ne va mpiedica s modificm toate caracteristicile obiectului iar aplicaiile care
utilizeaz obiectul nu vor avea de suferit deoarece protocolul de comunicaie al obiectului
motenit de la interfaa clasei (rezultatul ncapsulrii ca metod de concepie) nu s-a schimbat.

@Ca implementare, la nivelul unui limbaj de programare, ncapsularea este asigurat de


exigenele sintactice specifice.
Mostenirea
Mecanismul derivarii permite crearea facila de noi clasa, care preiau caracteristicile unor clase de
baza, deja definite. Derivarea are ca obiectiv reutilizarea soft-ului, prin folosirea uneor functii
deja scrise pentru clasele existente si eliminarea redundantei descrierilor, in cazul claselor care au
elemente comune, functii sau date. Acest concept este prezentat in detaliu in moulul 3.
Polimorfismul
Termenul polimorfism se refer la comportamente alternative ntre clase derivate nrudite. n
cazul n care mai multe clase mostenesc atributele si comportamentele unei clase de baz, pot
apare situatii n care comportamentul unei clase derivate ar trebui s fie diferit de cel al clasei de
baz sau de cel al clasei derivate de tip frate (de pe acelasi nivel). Aceasta nseamn c un mesaj
poate avea efecte diferite n functie de clasa obiectului care primeste mesajul.
De exemplu sa considerm trei clase: clasa de baz Fisier si clasele derivate FisierASCII si
FisierBitmap care mostenesc toate atributele si comportamentele clasei Fisier cu exceptia
comportamentului Tiprire.
Un mesaj care va activa comportamentul Tiprire al unui obiect al clasei Fisier poate determina
afisarea atributelor mrime fisier, tip fisier si data/ora crerii/ultimei modificri a fisierului.
Acelasi mesaj trimis unui obiect al clasei FisierASCII va determina afisarea textului din fisier, n
timp ce dac va fi trimis unui obiect al clasei FisierBitmap va determina executia unui program
de afisare grafic.

1.3

Elemente introductive ale programrii orientate pe obiecte n


limbajul C++

Limbajul C++ este unul dintre cele mai utilizate limbaje de programare orientate pe obiecte;
compilatoare, biblioteci i instrumente de dezvoltare a programelor C++ sunt disponibile att
pentru calculatoare personale ct i pentru cele mai dezvoltate sisteme i staii de lucru.
Limbajul C++ este o versiune extins a limbajului C elaborata de ctre B. Stroustrup n anul 1980
n laboratoarele Bell din Murray Hill, New Jersey. Extensiile dezvoltate de Stroustrup pentru
limbajul C++ permit programarea orientat pe obiecte, pstrnd eficiena, flexibilitatea i
concepia de baz a limbajului C. Numele iniial a fost C cu clase, numele de C++ fiindu-i
atribuit n anul 1983.
Scopul pentru care a fost creat C++ este acelai cu scopul pentru care este abordat n general
programarea orientat pe obiecte: dezvoltarea i administrarea programelor foarte mari. Chiar
dac superioritatea limbajului C++ este evident n cazul dezvoltrii programelor foarte mari, nu
exist limitri n a fi folosit n orice fel de aplicaie, deoarece C++ este un limbaj tot att de
eficient ca i limbajul C.

De la apariia sa, C++ a trecut prin trei revizii, n 1985, 1989 i ultima, prilejuit de definirea
standardului ANSI pentru acest limbaj. O prim versiune a standardului a fost publicat n anul
1994, iar urmtoarea versiune este nc n lucru.
n general, limbajul C++ prevede mai multe faciliti i mai puine restricii dect limbajul C,
astfel nct majoritatea construciilor din C sunt legale i au aceeai semnificaie i n C++.
n acest capitol sunt prezentate unitar i concis conceptele de baz n programarea C++, att cele
care sunt preluate din limbajul C ct i cele nou introduse. Multe dintre ele sunt reluate i
dezvoltate pe parcursul seciunilor urmtoare.
Operatii de intrare/iesire. Stream-uri
Cel mai scurt program C++ este:
main(){ }

Acesta definete o funcie numit main (), care nu primete nici un argument, nu execut nimic i
nu returneaz nici o valoare.
Dac se dorete ca programul s scrie un mesaj la consol (de tipul Primul program in C++!), pe
lng utilizarea funciei obinuite din limbajul C (funcia printf()), n C++ se poate utiliza i o
funcie de scriere la ieirea standard (cout). Aceast funcie este funcia operator de scriere <<
care este definit n fiierul antet iostream.h. Un astfel de program este urmtorul:
#include <iostream.h>
void main(){
cout << Primul program in C++!<< endl;
}

Prima operaie de scriere afieaz la consol irul, Primul program in C++!, iar urmtoarea (endl)
introduce caracterul de linie nou. Operaia de citire de la tastatur poate fi realizat n C++
printr-o instruciune care folosete funcia operator de citire >>. De exemplu, n instruciunile
care urmeaz se citete de la tastatur un numr ntreg:
int i;
cin >> i;

Desigur, funcia scanf() de citire de la tastatur din limbajul C poate fi n continuare folosit, dar
pe parcursul exemplificrilor se vor folosi mai mult aceste funcii C++ de citire i de scriere la
consol.
Programul P1.1 prezint un exemplu de utilizare a operatiilor de intrare/iesire n limbajul C++.
// fisierul sursa P1_1.cpp
#include <iostream.h>
void main()
{

int a, b;
float m;
cout << "\n
cin >> a;
cout << "\n
cin >> b;
m = (float)
cout << "\n
}

Introduceti un numar intreg a = ";


Introduceti un numar intreg b = ";
(a+b)/2;
Media aritmetica este " << m;

Programul P1.1 citeste dou numere ntregi, a si b si calculeaz media lor aritmetic, m, pe care o
afiseaz pe ecran. Citirea celor dou numere se poate realiza si cu o singur operatie de citire asa
cum se observ n programul P1.1_v2
// fisierul sursa P1_1_v2.cpp
#include <iostream.h>
void main()
{
int a, b;
float m;
cout << "\n Introduceti doua numere intregi: ";
cin >> a >> b;
m = (float) (a+b)/2;
cout << "\n Media aritmetica este " << m;
}

Sintaxa operatiei de intrare (citire):


cin >> var_1 >> var_2 >> >> var_n;

Se citesc de la dispozitivul de intrare valorile variabilelor var_1, var_2, , var_n.


Sintaxa operatiei de iesire (scriere, afisare):
cout << expr_1 << expr_2 << << expr_p;

Se afiseaz pe dispozitivul de iesire valorile expresiilor expr_1, expr_2, , expr_p. Expresiile


pot fi, de exemplu, expresii aritmetice, variabile sau pot contine text, care este marcat ntre
ghilimele (). Textul poate contine secvente de tipul \n (trecere la linie nou), \t (afisarea se
face la dreapta n pozitia dat de tab), \a (avertizare sonor) etc.

1.4 Tipuri de date


n C++ sunt definite urmtoarele tipuri fundamentale:
Tipuri de ntreg, pentru definirea numerelor ntregi de diferite dimensiuni (ca numr de
octei ocupai n memorie):
9

short int 2 octei


int
2 sau 4 octei
long
4 sau 8 octei
Tipuri de numere flotante, pentru definirea numerelor reale (reprezentate ca numere cu
virgul flotant):
float
4 octei
double
8 octei
long double 12 sau 16 octei
Aceste tipuri sunt denumite mpreun tipuri aritmetice. Pentru tipurile ntreg, exist
variante de declaraie cu semn (signed) i fr semn (unsigned).
Tipul caracter, pentru definirea caracterelor
char
1 octet
Tipul void specific o mulime vid de valori. Nu se poate declara un obiect cu acest tip,
dar acest tip poate fi utilizat n conversii de pointeri i ca tip de returnare al unei funcii
Pe langa tipurile de date fundamentale enumerate, se pot defini conceptual un numr infinit de
tipuri derivate pornind de la tipurile fundamentale. Tipurile derivate sunt:
tablouri de obiecte,
pointeri la obiecte,
referine,
funcii,
constante simbolice,
clase, structuri, uniuni,
pointeri la membrii claselor.
n continuare se vor prezenta primele cinci tipuri derivate, iar celelate vor fi introduse pe
parcursul seciuniunilor urmtoare. Ca terminologie, clasele (mpreun cu structurile i uniunile)
sunt denumite tipuri definite de utilizator (user-defined types), iar celelate tipuri sunt denumite
tipuri predefinite (built-in types).
Tablouri de obiecte
Un tablou (array) de obiecte poate fi construit din obiecte dintr-un tip fundamental (cu excepia
tipului void), din pointeri, din enumeraii sau din alte tablouri. n traducere, pentru array se mai
ntlnesc termenii vector i matrice. n acest text sunt folosii termenii tablou (pentru array
multidimensional) i vector (pentru array unidimensional).
Declaraia: T D[expresie] introduce un tablou de obiecte de tipul T, cu numele D i cu un numr
de elemente al tabloului dat de valoarea expresiei, care trebuie s fie de tip constant ntreg. Pentru
valoarea N a acestei expresii, tabloul are N elemente, numerotate de la 0 la N-1.
Un tablou bidimensional se poate construi printr-o declaraie de forma:

10

T D[dim1][dim2];
i reprezint dim1 tablouri unidimensionale, fiecare de dimensiune dim2. Elementele tabloului
bidimensional se memoreaz cu valori succesive pentru indicele din dreapta astfel:
D[0][0], D[0][1], D[0][dim2-1],
D[1][0], D[1][1], D[1][dim2-1],.
D[dim1-1][0], D[dim1-1][1], D[dim1-1][dim2-1].

ntr-un mod asemntor se pot construi tablouri multidimensionale, cu o limitare a numrului de


dimensiuni care depinde de implementare.
Pointeri
Pentru majoritatea tipurilor T, T* este un tip denumit pointer la T, adic o variabil de tipul T*
memoreaz adresa unui obiect de tipul T.
Operaia fundamental asupra unui pointer este operaia de derefereniere (dereferencing), adic
accesarea obiectului a crui adres o reprezint pointerul respectiv. Operatorul de derefereniere
este operatorul unar *. De exemplu:
char c1 = a; // variabila c1
char* p1 = &c1; // p memoreaz adresa lui c1
char c2 = *p1; // dereferentiere, c2 = a;
Operatorul & este operatorul adres, care se utilizeaz pentru a obine adresa unei variabile.
Tipul void* este folosit pentru a indica adresa unui obiect de tip necunoscut.
Asupra pointerilor sunt admise unele operaii aritmetice. De exemplu, se consider un vector de
caractere dintre care ultimul este caracterul 0 (se mai numete ir de caractere terminat cu nul).
Pentru calculul numrului de caractere se pot folosi operaii cu pointeri astfel:
int strlen(char* p){
int i = 0;
while (*p++) i++;
return i;
}
Funcia strlen() returneaz numrul de caractere ale irului, fr caracterul terminal 0, folosind
operaia de incrementare a pointerului i operaia de derefereniere pentru a testa valoarea
caracterului. O alt implementare posibil a funciei este urmtoarea:
int strlen(char* p){
char* q = p;
while (*q++);
return q-p-1;
}
n C++, ca i n limbajul C, pointerii i tablourile sunt puternic corelate. Un nume al unui tablou
poate fi folosit ca un pointer la primul element al tabloului. De exemplu, se poate scrie:
char alpha[] = abcdef;

11

char* p = alpha;
char* q = &alpha[0]; // p = q

Rezultatul aplicrii operatorilor aritmetici +, -, ++, -- asupra pointerilor depinde de tipul


obiectului indicat. Atunci cnd se aplic un operator aritmetic unui pointer p de tip T*, se
consider c p indic un element al unui tablou de obiecte de tip T; p+1 va indica urmtorul
element al tabloului, iar p-1 va indica elementul precedent al tabloului. Acest lucru nseamn c
valoarea lui p+1 este cu sizeof(T) octei mai mare dect valoarea lui p.
Referine
O referin (reference) este un nume alternativ al unui obiect. Utilizarea principal a referinelor
se face pentru specificarea argumentelor i a valorilor returnate de funcii, n general, i pentru
suprancrcarea operatorilor n special. Notaia X& nseamn referin la un obiect de tipul X. De
exemplu:
int i = 1;
int& r = i; // r i i se refer la aceeai entitate
int x = r; // x = 1
r++; // i = 2;
Implementarea obinuit a unei referine se face printr-un pointer constant care este derefereniat
de fiecare dat cnd este utilizat.
Aa cum se poate observa, pentru definirea unei referine se folosete operatorul adres &, dar
difer tipul construciei n care este folosit. De exemplu:
int a = 5;
int* pi = &a; // & calculeaz adresa;
// pi este adresa lui a
int& r = a; // & introduce o referinta;
// r este o referin (alt nume) pt. a

O referin este utilizat ca argument pentru o funcie care poate s modifice valoarea acestui
argument. De exemplu:
void incr(int& x) {x++;}
void f(){
int i = 1;
incr(i); // i = 2;
}
O alt utilizare important a referinelor este pentru definirea funciilor care pot fi folosite att ca
membru drept ct i ca membru stng al unei expresii de asignare. De exemplu:
#include <iostream.h>
int& fr(int v[], int i){
return v[i];
}
void main(){

12

int x[] = {1,2,3,4};


fr(x,2) = 7;
cout <<fr(x,0)<<fr(x,1)<<fr(x,2)<< fr(x,3)<<endl;
}

La execuia acestui program se obine mesajul:


1 2 7 4
Deoarece valoarea returnat de funcie este referina (numele) unui element al vectorului, acesta
poate fi modificat prin folosirea funciei fr() ca membru stng al egalitii.
Funcii
Funciile sunt tipuri derivate i reprezint una din cele mai importante caracteristici ale limbajelor
C i C++. Forma general de definire a unei funcii este:
tip_returnat nume_func(tip1 arg1,tip2 arg2,,tipn argn) {
//corpul functiei
}

Funcia cu numele nume_func returneaz o valoare de tip tip_returnat i are un numr n de


argumente formale declarate ca tip i nume n lista de argumente formale. Argumentele formale
din declaraia unei funcii se mai numesc i parametrii funciei. Dac o funcie nu are argumente,
atunci lista din parantezele rotunde este vid. Notaia din C: f(void) este redundant.
O funcie este un nume global, dac nu este declarat de tip static. O funcie declarat static are
domeniul de vizibilitate restrns la fiierul n care a fost definit.
Corpul funciei este propriu ei i nu poate fi accesat din afara acesteia (nici printr-o instruciune
goto). Corpul unei funcii este o instruciune compus, adic o succesiune de instruciuni i
declaraii incluse ntre acolade. n corpul funciei se pot defini variabile, care sunt locale i se
memoreaz n segmentul de stiv al programului. Dac nu sunt declarate static, variabilele locale
se creaz la fiecare apel al funciei i se distrug atunci cnd este prsit blocul n care au fost
definite. Nu se pot defini funcii n interiorul unei funcii. Argumentele formale ale unei funcii
sunt considerate variabile locale ale funciei, ca orice alt variabil definit n funcia respectiv.
Dac o funcie nu are de returnat nici o valoare, atunci tip_returnat din declaraia funciei este
tipul void i nu este necesar o instruciune de returnare (return) n funcie. n toate celelalte
cazuri, n corpul funciei trebuie s fie prevzut returnarea unei variabile de tipul tip_returnat,
folosind instruciunea return. Dac n definiie nu este prevzut un tip_returnat, se consider
implicit returnarea unei valori de tip ntreg.
Prototipurile funciilor. Pentru apelul unei funcii este necesar cunoaterea definiiei sau a
prototipului acesteia. Prototipul unei funcii este de forma:
tip_returnat nume_func(tip1 arg1,., tipn argn);

Numele argumentelor formale sunt opionale n prototipul unei funcii. Prototipurile permit
compilatorului s verifice tipurile argumentelor de apel i s semnaleze eroare la conversii

13

ilegale. Spre deosebire de limbajul C, unde este admis i simpla declaraie a numelui funciei
(fr tipurile argumentelor de apel), utilizarea prototipurilor este obligatorie n C++. De exemplu:
double f2(int, double); // prototip functie f2
double f3(int a, double f){ // definitie functie f3
/*..*/
double t = f/a;
return t;
}
void fp(){
double r1 = f1(7, 8.9); // eroare,
// identificator nedeclarat
double r2 = f2(7, 8.9); // corect, fol. prototipul
char str[] = "abcde";
double r3 = f3(7, str); // eroare de tip argument
}
double f1(int a, double f) {
/*..*/
double t = a + f;
return t;
}
double f2(int a, double f) { // definiie funcie f2()
/*...*/
double t = a*f;
return t;
}

La compilare apare o eroare datorit apelului funciei f1(), care nu este definit, nici declarat
prin prototip n domeniul funciei apelante fp() i o eroare datorat apelului funciei f3() cu un
argument (argumentul al doilea) care nu poate fi convertit la tipul argumentului formal.
Transferul argumentelor funciilor. La apelul unei funcii, argumentele de apel (se mai numesc i
argumente
reale
sau
efective)
iniializeaz
argumentele
formale
din declaraia funciei, n ordinea din declaraie. Argumentele unei funcii se pot transfera n dou
moduri: apelul prin valoare i apelul prin referin.
n apelul prin valoare se copiaz valoarea argumentului real n argumentul formal corespunztor
al funciei. n acest caz, modificrile efectuate asupra argumentului funciei nu modific
argumentul real.
n apelul prin referin este accesat direct variabila din argumentul real transmis funciei, care
poate fi deci modificat. Ca exemplificare, se definete o funcie swap() care realizeaz
intershimbul ntre valorile a dou variabile. Dac nu se folosesc referine, argumentele de apel ale
funciei trebuie s fie pointeri la variabilele respective. Pointerii, ca argumente de apel, nu vor fi
modificai, dar variabilele indicate de acetia pot fi modificate. Funcia swap() cu argumente
pointeri arat astfel:
void swap(int* x, int* y){
int t;

14

t = *x; // dereferentiere
*x = *y;
*y = t;
}
Aceeai funcie swap(), folosind argumente de tip referin, arat astfel:
void swap(int& x, int& y){
int t;
t = x;
x = y;
y = t;
}

Se poate observa perfecta simetrie ntre cele dou implementri i c, n mod evident, referina
folosete adresa variabilei pentru a o putea modifica (deci un pointer). Dar, n cazul referinelor,
pointerul i defererenierea sunt ascunse, programatorul nu trebuie s le prevad explicit,
programul rezultat este mai concis i mai clar.
Referinele sunt deosebit de utile n apelul funciilor ale cror argumente sunt obiecte de
dimensiuni mari i copierea lor n argumentele formale (plasate n segmentul de stiv al
programului) ar fi foarte ineficient.
Argumente implicite ale funciilor. Se ntmpl frecvent ca o funcie s aib un numr mai mare
de argumente dect sunt necesare n cazurile simple dar frecvente de apel. Dac nu este necesar
s fie transmis ntotdeauna valoarea real a unui argument i acesta poate lua, de cele mai multe
ori, o valoare implicit, atunci n declaraia funciei se prevede o expresie de iniializare a acestui
argument, iar din apel poate s lipseasc valoarea argumentului corespunztor.
De exemplu, o funcie pentru stabilirea datei calendaristice, care prevede valori implicite pentru
argumentelelunaian:
struct data{
int zi;
int luna;
int an;
} g_data;
void setdata(int zi, int luna=9, int an =1999){
g_data.zi = zi;
g_data.luna = luna;
g_data.an = an;
}
void main(){
setdata(15); // 15 9 1999
setdata(21,7); // 21 7 1999
setdata(20,1,2000); // 21 1 2000
}

Numai argumentele de la sfritul listei pot fi argumente implicite. De exemplu, este eronat
urmtoarea declaraie:

15

void setdata(int zi, int luna=9, int an); // eroare

Constante simbolice
O constant simbolic (sau constant cu nume) este un nume a crui valoare nu poate fi
modificat n cursul programului. n C++ exist trei modaliti de a defini constante simbolice:
Orice valoare, de orice tip care poate primi un nume, poate fi folosit ca o constant
simbolic prin adugarea cuvntului-cheie const n declaraia acesteia.
Orice nume de funcie sau de tablou este o constant simbolic.
O enumeraie definete o mulime de constante ntregi.
De exemplu, urmtoarele declaraii introduc constante simbolice prin folosirea cuvntului-cheie
const:
const int val = 100;
const double d[] = {1.2, 2.8, 9.5};

Deoarece constantele nu pot fi modificate, ele trebuie s fie iniializate n declaraie. ncercarea
de modificare ulterioar este detectat ca eroare n timpul compilrii:
val++; // eroare
d = 200; // eroare

Cuvntul-cheie const modific tipul obiectului, restricionnd modul n care acesta poate fi
folosit.
Un aspect interesant i intens folosit n programare, este acela de a declara pointeri la constante.
Atunci cnd se folosete un pointer, sunt implicate dou obiecte: pointerul nsui i obiectul ctre
care indic pointerul.
Prin prefixarea declaraiei unui pointer cu cuvntul const, obiectul indicat este fcut constant, nu
pointerul nsui. De exemplu:
const char* pc = abcd;// pc este pointer la o constant
pc[2] = m; // eroare, nu se poate modifica
// obiectul constant
pc = ghij; // corect, este modificat
// valoarea pointerului
pc++; // corect
Pentru ca pointerul nsui s fie constant, se folosete operatorul *const:
char *const cp = abcd;// cp este pointer constant;
cp[2] = m; // corect, modifica valoarea
cp++; // eroare, pointer constant

16

Posibilitatea de declarare a pointerilor la constante este folosit n special pentru transmiterea


argumentelor funciilor. Prin declararea unui argument de tip pointer la constant, este interzis
modificarea de ctre funcie a obiectului indicat. De exemplu:
char* strcpy(char* d, const char* s);

n aceast funcie irul s nu poate fi modificat.


n mod asemntor, specificatorul const care nsoete un argument tip referin la apelul unei
funcii, mpiedic modificarea acestuia de ctre funcia respectiv.

1.5 Operatori specifici C++


Majoritatea operatorilor C++ sunt preluai din limbajul C, cu aceeai sintax i reguli de operare.
n plus fa de operatorii C, n C++ mai sunt introdui urmtorii operatori:
operatorul de rezoluie (::)
operatorii de alocare-eliberare dinamic a memoriei new i delete.
Operatorul de rezoluie
Operatorul de rezoluie (::) este folosit pentru modificarea domeniului de vizibilitate al unui
nume. Pentru acest operator (scope resolution operator), n traduceri se mai ntlnesc termenii de
operator de domeniu sau operator de acces. Operatorul de rezoluie permite folosirea unui
identificator ntr-un bloc n care el nu este vizibil. Dac operatorul de rezoluie nu este precedat
de nici un nume de clas, atunci este accesat numele global care urmeaz acestui operator. De
exemplu:
int g = 10;
int f(){
int g = 20;
//
return ::g;
}
void main(){
cout << f() << endl; // afiseaza 10
}
n acest exemplu operatorul de rezoluie a fost folosit pentru a accesa variabila global g, ascuns
de variabila local cu acelai nume din funcie. Deoarece utilizarea cea mai extins a operatorului
de rezoluie este legat de utilizarea claselor, el va fi reluat pe parcursul seciunilor urmtoare.
Operatorii new i delete
n limbajul C se pot aloca dinamic zone n memoria liber (heap) folosind funcii de bibliotec
(de exemplu, malloc(), calloc(), realloc()) i se pot elibera folosind funcia free(). La aceste
posibiliti, care se pstreaz n continuare n C++, se adaug operatorii de alocare i eliberare
dinamic a memoriei, new i delete. Aceti operatori unari prezint avantaje substaniale fa de
funciile de alocare din C i de aceea sunt n mod evident preferai n programele scrise n C++.
17

Pentru alocarea unei singure date (obiect), operatorul new are urmtoarea form general:
tip_data* p = new tip_data(initializare);
unde tip_data este un tip de date predefinit sau definit de utilizator (clas), p este pointerul
(adresa de nceput) a zonei alocate n memoria liber, returnat la execuia operatorului new, iar
initializare este o expresie care depinde de tipul datei i permite iniializarea zonei de memorie
alocate. Dac alocarea nu este posibil, pointerul returnat este NULL.

Forma de utilizare a operatorului new pentru alocarea unui vector de date (tablou unidimensional)
de dimensiune dim, este urmtoarea:
tip_data* p = new tip_data[dim];

La alocarea unui vector nu se poate transmite o expresie de iniializare a zonei de memorie


alocat.
Operatorul delete elibereaz o zon din memoria heap. El poate avea una din urmtoarele forme:
delete p; delete []p;

Prima form se utilizeaz pentru eliberarea unei zone de memorie ocupat de o singur dat
(obiect), nu de un vector. Pointerul p trebuie s fie un pointer la o zon de memorie alocat
anterior printr-un operator new. Operatorul delete trebuie s fie folosit doar cu un pointer valid,
alocat numai cu new i care nu a fost modificat sau nu a mai fost eliberat zona de memorie mai
nainte (cu un alt operator delete sau prin apelul unei funcii free()). Folosirea operatorului delete
cu un pointer invalid este o operaie cu rezultat nedefinit, cel mai adesea producnd erori de
execuie grave.
Cea de-a doua form a operatorului delete[] se folosete pentru eliberarea unei zone de memorie
ocupat de un vector de date. Pentru tipurile de date predefinite ale limbajului, se poate folosi i
prima form pentru eliberarea unui vector, dar, n cazul obiectelor de tipuri definite de utilizator,
acest lucru nu mai este valabil. Aceast situaie va fi detaliat n seciunea urmtoare.
Cteva exemple de utilizare a operatorilor new i delete:
int *pi = new int(3); // alocare int i iniializare
double *pd = new double; // alocare double neinitializat
char *pc1 = new char[12]; // vector de 12 caractere
char *pc2 = new char[20]; // vector de 20 caractere
delete pi;
deletepd
delete pc1; //corect, char e tip predefinit
delete []pc2; // corect, elibereaza vector

n legtur cu cele dou metode de alocare dinamic, prin operatorii new-delete i prin funciile
de bibliotec malloc-free, fr s fie o regul precis, se recomand evitarea combinrii lor,
deoarece nu exist garania compatibilitii ntre ele.

18

Teste de autocontrol
1.1 Care din afirmatiile urmtoare sunt adevrate si care sunt
false? Justificati rspunsul n cazul afirmatiilor care sunt false.
(a) Toate variabilele trebuie declarate nainte de a fi utilizate.
(b) Un program C++ care afiseaz trei linii pe ecran trebuie s
contin 3 instructiuni de afisare cout.
(c) Comentariile dintr-un program C++ determin afisarea
textului aflat dup // pe ecran la executia programului.
(d) n limbajul C++ toate variabilele locale trebuie declarate la
nceputul functiei de care apartin.
1.2 Descriei pe scurt diferena dintre funciile care returneaz valoare i cele care returneaz
referin.

1.3 Presupunnd c variabilele a si b au valorile 3, respectiv 4, s se precizeze ce anume se va


afisa pe ecran ca urmare a executiei urmtoarelor instructiuni:
(a) cout << a+b;
(b) cout << a=;
(c) cout << a= << a;
(d) cin >> a >> b;
(e) // cout << a+b= << a+b;
(f) cout << \n\t;
(g) cout << a*b << = << b*a;
(h) cout << a;
(i) s = a+b;
1.4 Care sunt principalele diferente ntre programarea orientat pe obiecte si programarea
procedural?
1.5 Care sunt principalele mecanisme de abstractizare?
1.6 Cum se numesc TDA-urile n programarea orientat pe obiecte?
1.7 Care sunt elementele principale ale unui TDA?
1.8 De ce sunt considerate a fi inteligente obiectele?
1.9 Dati exemple de clase si obiecte ale acestora.
1.10 Obiectele pot comunica ntre ele? Justificati rspunsul.
1.11 Care din urmtoarele concepte sunt concepte de baz ale programrii orientate pe obiecte?
(a) obiect
(b) mostenire
(c) metod
19

(d) ncapsulare
(e) modul
(f) procedur
(g) polimorfism
(h) stream
(i) cout
1.12 Definiti tipul de date abstract (TDA).
1.13 Definiti urmtoarele tipuri de date abstracte:
(a) LISTA
(b) COADA
Se vor specifica: structura de date, operatorii de baz si cei suplimentari si axiomele n mod
similar cu definitia TDA STIVA.
1.14 S se scrie declaraiile pentru urmtoarele tipuri de variabile: pointer la un caracter, un
vector de 10 valori ntregi, pointer la un vector de 10 valori ntregi, un pointer la un pointer la un
caracter.
1. 15 S se scrie un program care tiprete dimensiunea tipurilor fundamentale de date. Se va
folosi operatorul sizeof.

20

Cap.2 Clase si Obiecte

Obiective
Definirea notiunilor de clas, obiect, atribut, metod;
ntelegerea notiunilor de instant, instantiere, constructor, destructor;
nsusirea modului n care se realizeaz accesarea membrilor unei clase;
Definirea constructorilor unei clase (implicit, cu parametri, de copiere, de convertire si
atribuire);
Definirea destructorului unei clase;
Definirea si utilizarea claselor compuse si a obiectelor compuse;
Pointerului this;
Particularitti ale limbajului C++ (functii friend, functii inline, membrii statici, tablouri
de obiecte, pointeri la metode)

Un tip de date ntr-un limbaj de programare este o reprezentare a unui concept. De exemplu, tipul
float din C++, mpreun cu operaiile definite asupra acestuia (+, -, *, etc.) reprezint o versiune a
conceptului matematic de numere reale. Pentru alte concepte, care nu au o reprezentare direct
prin tipurile predefinite ale limbajului, se pot defini noi tipuri de date care s specifice aceste
concepte.
O clas este un tip de date definit de utilizator. O declarare a unei clase definete un tip nou care
reunete date i funcii. Acest tip nou poate fi folosit pentru a declara obiecte de acest tip, deci un
obiect este un exemplar (o instan) a unei clase.
Definitie. O clas este implementarea unui TDA. Ea defineste atribute si metode care
implementeaz structuri de date si operatii ale TDA-ului.
Definitie. Un obiect este o instant a unei clase. El este unic identificat de numele lui si defineste
o stare care este reprezentat de valorile atributelor, la un moment dat. Starea unui obiect se
schimb n raport cu metodele care i sunt aplicate.
Definitie. Comportamentul unui obiect este definit de multimea metodelor care i pot fi
aplicate.
Definitie. O metod este o functie asociat unei clase. Un obiect apeleaz (invoc) o metod
drept reactie la primirea unui mesaj.

21

Primul pas pentru gruparea datelor si metodelor de prelucrare, l-au reprezentat stucturile; ele
permiteau declararea unor ansambluri eterogene de date ce erau manipulate unitar.
Consideram structura angajat care curinde urmatoarele date: nume, varsta, salariul.
struct angajat
{
char nume[20];
int varsta;
float salariul;
}

Declaratii de variabile:
angajat a1; // o structura de tip angajat p1
angajat tablouangajat[10]; // un tablou cu zece elemente de tip angajat
angajat *a2; // un pointer catre o structura de tip angajat a2
angajat &a3=a1; // o referinta catre o structura de tip angajat a3
Membrii unei structuri sau ai unei clase sunt accesati cu ajutorul operatorilor de acces: operatorul
., respectiv, operatorul ->.
9 Operatorul . acceseaz o structur sau un membru al unei clase prin numele variabilei
pentru obiect sau printr-o referint la obiect.
9 Operatorul -> acceseaz un membru al unei structuri sau clase printr-un pointer la
obiect (a2 = &a1)
Programul P2.1 implementeaz tipul de date angajat cu ajutorul unei unei structuri C. Functia
afisare( ) are rolul de a afisa datele unui angajat. Programul realizeaz constructia tipului de data
abstract angajat prinn definirea unei structuri , setarea valorilor pentru cmpurile structurilor
(membrii structurilor) si afisarea acestora.
// fisierul sursa P2_1.c
#include <stdio.h>
#include <conio.h>
#include <string.h>
// definitia structurii angajat
struct angajat {
char num[20];
int varsta;
float salariul;
} a1 = {"Pop", 28,1530};
// definitia functiei de afisare a datelor unei angajat
void afisare(struct angajat a)
{
printf("\n Nume \t Varsta \t Salariul");
printf("%s\t%i\t%d",a.nume,a.varsta,a.salariul);
}
void main()

22

{
struct angajat a2;
strcpy(a2.nume, "Ion");
a2.varsta = a1.varsta; a2.salariul = 1600;
printf("\n Angajat nr 1 \n");
afisare(a1);
printf("\n Angajat nr 2 \n");
afisare(a2);
getch();
}

2.1 Definirea unei clase


Clasele permit programatorului s modeleze obiectele care au atribute (reprezentate de datele
membru) si comportament (reprezentat de functiile membru, numite si metode). Practic, o clas
ncapsuleaz o multime de valori si o multime de operatii.
Sintaxa definirii unei clase:
class <nume_clas>
{
private:
// membrii privati
public:
// membrii publici
protected:
// membrii protejati
};

Cuvntul-cheie class introduce declaraia clasei (a tipului de date) cu numele nume_clasa. Dac
este urmat de corpul clasei (cuprins ntre acolade), aceast declaraie este totodat o definiie.
Dac declaraia conine numai cuvntul-cheie class i numele clasei, atunci aceasta este doar o
declaraie de nume de clas.
Corpul clasei conine definiii de date membre ale clasei i definiii sau declaraii de funcii
membre ale clasei, desprite printr-unul sau mai muli specificatori de acces. Un
specificator_acces poate fi unul din cuvintele-cheie din C++:
public

private

protected

Specificatorii private i protected asigur o protecie de acces la datele sau funciile membre ale
clasei respective, iar specificatorul public permite accesul la acestea i din afara clasei. Efectul
unui specificator de acces dureaz pn la urmtorul specificator de acces. Implicit, dac nu se
declar nici un specificator de acces, datele sau funciile membre sunt de tip private. De aceea,
toate datele sau funciile declarate de la nceputul blocului clasei pn la primul specificator de
acces sunt de tip private. ntr-o declaraie de clas se poate schimba specificatorul de acces ori de
cte ori se dorete: unele declaraii sunt trecute public, dup care se poate reveni la declaraii
private, etc. Diferena ntre tipurile de acces private i protected const n modul n care sunt
motenite drepturile de acces n clase derivate.

23

Definirea obiectelor
Dup definirea unei clase C, aceasta poate fi utilizat ca tip n definirea obiectelor, tablourilor de
obiecte si a pointerilor, astfel:
C
C
C
C

o1, // obiect de tip C


Tablou[20], // tablou de obiecte de tip C
*ptrC, // pointer la un obiect de tip C
&tC = o1; // referinta la un obiect de tip C

Numele unei clase devine specificator de tip si se pot defini, teoretic, o infinitate de obiecte ale
clasei.
Accesarea membrilor unei clase
Membrii publici ai unei clase pot fi accesati cu ajutorul operatorilor . si ->, dup cum
obiectul de care apartin este desemnat prin nume sau printr-un pointer. Variabilele membru
private nu pot fi accesate dect n cadrul metodelor clasei respective.
Programul 2.2 implementeaz tipul de date angajat sub forma unei clase C++. Clasa angajat
cuprinde datele membre: varsta, salariul, nume si functii membre: init() pentru initializarea
datelor membre, functiia membra de acces spune_varsta(), functia afisare() pentru afisarea
datelor membre.
class angajat
{
private:
int varsta;
protected
float salariul;
public:
char nume[20];
void init ( char n[]=Anonim, int v=0, float s=0)
{strcpy(nume,n);
varsta=v;
salariul=s;
}
int spune_varsta() { return varsta};
void afisare()
{cout<<nume: <<nume<<endl;
cout<<varsta: <<varsta<<endl;
cout<<salariul: <<salariul;
}
}
void main()
{ angajat a;
a.init(); // apelul functiei membre init() pentru obiectul a

24

cout<<a.spune_varsta();//apelul functiei membre soune_varsta() pentru


obiectul a
}

Dupa rolul pe care il joaca in cadrul clasei, functiile membre ale unei clase se impart in patru
categorii:
9 constructori, responsabili cu crearea obiectelor;
9 destructor, reponsabil cu distrugere obiectelor si eliberarea memoriei ocupate de acesta;
9 functii de acces, care mediaza legatura obiectului cu exteriorul;
9 metode, functii care introduc operatiile si prelucraile specifice obiectului.

2.2 Constructori si destructor


Utilizarea unor funcii membre ale unei clase, aa cum este funcia init() din clasa angajat, pentru
iniializarea obiectelor este neelegant i permite strecurarea unor erori de programare. Deoarece
nu exist nici o constrngere din partea limbajului ca un obiect s fie iniializat (de exemplu, nu
apare nici o eroare de compilare dac nu este apelat funcia init() pentru un obiect din clasa
angajat), programatorul poate s uite s apeleze funcia de iniializare sau s o apeleze de mai
multe ori. n cazul simplu al clasei prezentate ca exemplu pn acum, acest lucru poate produce
doar erori care se evideniaz uor. n schimb, pentru alte clase, erorile de iniializare pot fi
dezastruoase sau mai greu de identificat.
Din aceast cauz, limbajul C++ prevede o modalitate elegant i unitar pentru iniializarea
obiectelor de tipuri definite de utilizator, prin intermediul unor funcii speciale numite funcii
constructor (sau, mai scurt, constructori).
Constroctorul este o functie membra speciala care este apelata automat atunci cand este creat un
obiect. El poarta numele clasei si nu are nuci un tip la intoarcere, deoarece este apelat automat.
Principalele motivatii pentru care se utilizeaza constructorii sunt:
- complexiatatea structurii obiectelor date de de existenta variabilelor si functiilor,
de existenta sectiunii privata, publica si protejata, face dificila initializarea directa
a obictelor;
- exista situatii in care doar unele date membre trebuie intializate, altele sunt
incarcate in urma apelarii unor metode;
- datele de obicei sunt declarate in sectiunea private si deci nu pot fi accesate din
exterior ci prin intermediul metodelor.
O clasa poate mentiona mai multi construcori, prin supraincarcare, folosirea unuia dintre ei la
declararea unei variabile de clasa data, fiind dedusa in functie de numarul si tipul parametrilor de
apel.
Vom redefinii calsa angajat pentru care vom defini doi constructori asfel:
class angajat
{int varsta;
float salariul;
char nume[20];
public:
angajat() { strcpy(nume,Noname); varsta=0;salariul=0}

25

angajat(char *n, int v, float s)


{strcpy(nume,n);varsta=v;salariul=s;}
char *spune_nume(){return nume;}
int spune_varsta(){return varsta;}
}

Se observa ca in clasa angajat s-au definit doi constructori:


- unul care nu are nici un parametru, angajat() care intilaizaza datele membre cu
valori constante; constructorul fara parametrii se numeste constructor impicit;
- al doilea constructor primeste trei parametri si are ca scop initializarea datelor
membre din variabile elemntare; un asfel de constructor se numeste constructor cu
argumente.
Constructorii definiti mai sus se pot apela asfel:
angajat a1 // aplelul constructorului implicit
angajat a2(Pop,28,1200) //apelul constructorului cu argumente.
Daca clasa nu mentioneaza nici un constructor, atunci se defineste automat de catre compilator
un constructor care este utilizat pentru generarea de obiecte ale casei respective.
In multe aplicatii este recomandat ca o clasa sa mentoineze mai multi constructori deoarece
modurile de intializare a datelor membre sun diverse:
- initializarea membrilor cu constante;
- intializarea din datele elementare;
- initializarea prin citire de la tastatura;
- initializarea prin citire din fisier;
- initializarea din datele unui obiect existent.
Observatie. Pentru un obiect este selectat un singur constructor care este apleat o singura data.
Programul 2.2 impementeaza clasa Complex care descrie un numar cpmplex. Clasa contine datele
membre partea reala a numarului complex re, partea imaginara a numarului complex im. Sunt
definiti trei constructori Complex(), Complex (double v), Complex ( double x, double y) precum si
o functie membra care permite afisarea datelor unui numar complex.
#include <iostream.h>
class Complex{
double re;
double im;
public:
Complex(){cout << "Constructor fara argumente\n";
re = 0;
im = 0;
}
Complex(double v){cout << "Constructor cu 1 arg\n");
re = v;
im = v;
}
Complex(double x, double y){

26

cout << "Constructor cu 2 arg\n";


re = x;
im = y;
}
void afisare()
{cout<<partea reala<<re<<endl;
Cout<<partea imaginara<<im;
};
void main (){
Complex c1;
Complex c2(2);
Complex c3(3,5);
}

La execuia funciei main(), sunt afisate urmtoarele mesaje:


Constructor fara argumente
Constructor cu 1 arg
Constructor cu 2 arg

n fiecare dintre aceste situaii a fost creat un obiect de tip Complex, c1, c2, c3 i de fiecare dat
a fost apelat constructorul care are acelai numr i tip de argumente cu cele de apel.
Dintre modurile de intializare amintite mai sus, un rol important o are intializarea unui obiect prin
copierea datelor unui alt obiect de acelai tip. Aceast operaie este posibil prin intermediul unui
constructor mai special al clasei, numit constructor de copiere. Forma general a constructorului
de copiere al unei clase X este:
X::X(X& r){ } // initializare obiect folosind referina r

Constructorul primete ca argument o referin r la un obiect din clasa X i iniializeaz obiectul


nou creat folosind datele coninute n obiectul referin r. Pentru crearea unui obiect printr-un
constructor de copiere, argumentul transmis trebuie s fie o referin la un obiect din aceeai
clas.
De exemplu, pentru obiecte de tip Complex:
void main(){
Complex c1(2,3); // Constructor initializare
Complex c2(c1); // Constructor copiere
Complex c3 = c2; // Constructor copiere
c3.afisare(); // afiseaza 2 3
}

La crearea primului obiect c1 este apelat constructorul de iniializare cu dou argumente al clasei
Complex. Cel de-al doilea obiect c2 este creat prin apelul constructorului de copiere al clasei
Complex, avnd ca argument referina la obiectul c1. Este posibil i declaraia de forma Complex
c3 = c2; a unui obiect prin care se apeleaz, de asemenea, constructorul de copiere.

27

Constructorul de copiere poate fi definit de programator; dac nu este definit un constructor de


copiere al clasei, compilatorul genereaz un constructor de copiere care copiaz datele membru
cu membru din obiectul referin n obiectul nou creat. Aceast modalitate de copiere mai este
denumit copie la nivel de bii (bitwise copy) sau copie membru cu membru.
Pentru clasa Complex, constructorul de copiere generat implicit de compilator sau definit de
programator arat astfel:
Complex(Complex &r)
{
cout << Constructor copiere\n;
re = r.re;
im = r.im;
}

Importanta constructorului de copiere este subliniata de situatia in care datele membre ale unei
clase sunt alocate dinamic. Constructorul de copiere generat implicit de compilator copiaz doar
datele membre declarate n clas (membru cu membru) i nu tie s aloce date dinamice pentru
obiectul nou creat. Folosind un astfel de constructor, se ajunge la situaia c dou obiecte, cel nou
creat i obiectul referin, s conin pointeri cu aceeai valoare, care indic spre aceeai zon din
memorie. O astfel se situaie este o surs puternic de erori de execuie subtile i greu de depistat.
Exemplificam acest caz definind clasa angajat asfel:
class angajat
{float salariul;
public:
char *nume;
angajat(char *n,int s)
{int nr=strlen(n);
nume=new char[nr];// alocare dinamica pentru sirul nume
strcpy(nume,n);
salariul=s;
}
int spune_salariul(){return salariul;}
}
void main()
{ angajat a1(Popescu,35);
angajat a2=a1;
strcpy(a2.nume,Ion);
cout<<a1.nume<< <<a1.salariul<<endl;
cout<<a2.nume<< <<a2.salariul;
}

Dupa rularea programului se va afisa:

Ion
Ion

35
35

La construirea obiectului a2 s-a aplelat constructorul de copiere implicit care a copiat membrul
nume al obiectului a1, dar nu a realizat alocarea dinamica. Asfel, obiectele a1 si a2 folosesc
aceeasi zona de memorie alocata pentru campul nume.
28

Se observa asfel, necesiatea de a introduce in clasa amgajat un constructor de copiere care sa


aloce explitit memorie , dupa care sa faca copierea continutului zonei de memorie a obiectului
sursa la destinatie:
class angajat
{float salariul;
public:
char *nume;
angajat(char *n,int s)
{int nr=strlen(n);
nume=new char[nr];
strcpy(nume,n);
salariul=s;
}
angajat(angajat &a)
{
int nr= strlen(a.nume)
strcpy(nume,p.nume);
varsra=a.varsta;
}
int spune_salariul(){return salariul;}
}
void main()
{ angajat a1(Popescu,35);
angajat a2=a1;
strcpy(a2.nume,Ion);
cout<<a1.nume<< <<a1.salariul<<endl;
cout<<a2.nume<< <<a2.salariul;
}
Rezultatul afisat prin rularea aceeluiasi program este:
Popescu
Ion

35
35

In concluzie, pentru un obiect cu un membru pointer spre o zona alocata dinamic progrmatorul va
furniza un contructor de copiere care sa aloce memorie pentru noul obiect.
Multe clase definite ntr-un program necesit o operaie care efectueza tergerea complet a
obiectelor. O astfel de operaie este efectuat de o funcie membr a clasei, numit funcie
destructor.
Cand este declarat explicit in cadrul calsei, destructorul porta numele clasei, precedat de semnul
~ (de ex. ~angajat()).
Destructorul, spre deosebire de constructor, este unic si nu are parametrii de apel.
Dac programatorul nu defineste un destructor explicit, compilatorul C++ va genera unul
implicit. Destructorul generat de compilator apeleaz destructorii pentru variabilele membru ale
clasei. Membrii unei clase sunt ntotdeauna distrusi n ordinea invers celei n care au fost creati.

29

#include <iostream.h>
class Complex
{
double re;
double im;
public:
Complex(double x, double y){
cout << "Constructor cu 2 arg\n";
re = x;
im = y;
}
~Complex(){cout << "Destructor";
}
Void main()
{
Complex z(3,4);
}

Rezultatul afisat dupa rularea programului este:

Constructor cu 2 arg
Destructor
Se observa in programul anterior ca nu s-a realizat un apel explicit al destructorului. Acesta a fost
apelat automat la sfarsitul programului.
Destructorii sunt apelai implicit si in alte situaii, cum ar fi:
atunci cnd un obiect local sau temporar iese din domeniul de definiie;
la apelul operatorului delete, pentru obiectele alocate dinamic.

Pointerul this
Fiecare obiect al unei clase are acces la propria adres prin intermediul unui pointer numit this.
Acest pointer este utilizat n mod implicit pentru referirea att la membrii date ct si la functiile
membru ale unui obiect. El poate fi utilizat si explicit cand se doreste folosirea adresei obiectului.
Adresa obiectului, desi este transparenta pentru utilizator, exista memorata intr-un pointer numit
this.
Ca expemplul vom defini, in programul 2.3, o clasa care va contine o metoda ce utilizeza
pointerul this.
class C
{ int x;
public:
C(int a){
x=a;}
void afisare(){
cout<<this->x;}
}
void main()
{C ob(7);
ob.afisare;
}

30

2.3 Tablouri de obiecte


n limbajul C++ implementarea tablourilor de obiecte se poate realiza cu conditia respectrii
urmtoarei restrictii: clasa care include obiectul trebuie s contin un constructor implicit.
Aceast restrictie se datoreaz imposibilittii de a furniza argumentele necesare constructorilor.
Programul P2.4 defineste un tablou de obiecte de tip angajat.
// fisierul sursa P24.cpp
class angajat
{
private:
int varsta;
float salariul;
public:
char nume[20];
angajat()
{strcpy(nume,Anonim);
varsta=0;
salariul=0;
}
angajat( char n[]=Anonim, int v=0, float s=0)
{strcpy(nume,n);
varsta=v;
salariul=s;
}
void seteaza_valori()
{cout<<Numele:
cin>>nume;
cout>>salariul:
cin>>salariul;
cout<<varsta
cin>>varsta;
}
void afisare()
{cout<<nume: <<nume<<endl;
cout<<varsta: <<varsta<<endl;
cout<<salariul: <<salariul;
}
}
void main()
{ angajat tab[20]; // tablou static sau automatic
int i, n;
cout<<"\n Cate obiecte doriti sa creati?";
cin>>n;
// initializeaza n obiecte
for (i=0;i<n;i++)

31

tab[i].seteaza_valori();
// afiseaza cele n obiecte
for (i=0;i<n;i++)
tab[i].afisare();
}

2.4 Funcii i clase friend


Este cunscut deja faptul ca aceesul la mebrii unei clase declarati in sectiunile private si protected
este restrictionat. Acestea pot fi utilizate doar de functiile membre ale clasei.
Accesul la membrii unei clase poate fi ingadiut, totusi si unor functii externe clasei sau
apartinand altor clase. Astfel de functii se numesc functii prietene clasei respective (functii
friend) si sunt declarate astfel cu ajutorul specificatorului friend.
Functiile friend raman externe, nefiind legate de clasa si cu atat mai putin de un obiect anume.
Pentru a avea acces la datele unui obiect, functia friend trebuie sa primeasca drept parametru de
intrare referinta la obiectul respectiv.
Declararea unei funcii f() de tip friend se realizeaza in interiorul clasei iar funcia nsi se
definete n alt parte n program astfel:
class C{
//..
friend tip_returnat f(lista_argumente);
};
..
tip_returnat f(lista_argumente){
// corpul functiei
}
Programul P2.5 prezint un exemplu de declarare si utilizare a unei functii friend a clasei
Nr_Complex, modulul(), care calculeaz modulul unui numar complex.

// fisierul sursa P2_5.cpp


#include <iostream.h>
#include <math.h>
class Complex {
double re, im;
public:
Complex(double x, double y)
{ real = x;
imag = y;
}
friend double modul(Complex &);
};
double abs(Nr_Complex& z)
{
return sqrt(z.real*z.real+z.imag*z.imag);
}

32

void main()
{
Complex Z=Complex(3,4);
cout<<"\n Modulul lui numarului complex z este "<< modul(Z);
}

Trebuie subliniat faptul ca utilizarea functiilor friend trebuie fcut cu mare grij ntruct acestea
ncalc principiul ncapsulrii, respectiv, ascunderea informatiei, principiu fundamental n POO.
Avantajul principal al declarrii unei functii friend este accesul rapid pe care aceasta l are direct
la membrii privati ai clasei cu care este prieten.

2.5 Funcii inline


n programare aplelul functiilor poate produce un cost ridicat de execuie, datorit operaiilor
necesare pentru rezervarea spaiului n stiv, pentru transferul argumentelor i returnarea unei
valori. Pentru a reduce timpul de executii C++ pune la dispozitie functii inline.
n general, o funcie declarat inline se schimb la compilare cu corpul ei, i se spune c apelul
funciei se realizeaz prin expandare. n felul acesta se elimin operaiile suplimentare de apel i
revenire din funcie. Dezavantajul funciilor inline este acela c produc creterea dimensiunilor
programului compilat, de aceea se recomand a fi utilizate pentru funcii de dimensiuni mici
(maximum 3-4 instruciuni). n plus, mai exist i unele restricii privind funciile inline: ele nu
pot fi declarate funcii externe, deci nu pot fi utilizate dect n modulul de program n care au fost
definite i nu pot conine instruciuni ciclice (while, for, do-while).
n cazul claselor, functiile membru care sunt definite n interiorul clasei devin n mod implicit
inline. n cazul n care le definim n afara clasei si vrem s fie inline trebuie s utilizm sintaxa
declarrii unei functii inline.
Sintaxa declarrii unei functii inline:
inline <tip> <nume_functie>([<lp>])

unde <tip> reprezint tipul ntors de functie, iar <lp> lista de parametri.
Programul P2.6 prezint un exemplu de definire si utilizare a unei functii inline pentru calculul
ariei unui patrat cu latura data, arie_patrat() .

// fisierul sursa P2_6.cpp


#include <iostream.h>
inline double arie_patrat(double a)
{
return a*a;
}

33

void main()
{
double x;
cout<<"\n Latura patratului:";
cin>>x;
cout<<"\n Aria patratului este "<<arie_patrat(x);
}

2.6 Date si functii statice


Implicit membrii de date sunt alocati pe obiecte. De exemplul fiecare angajat al unei firme are
propiul sau nume, cnp etc. Exista, insa unele propietati care sunt impartite de catre toate obiectele
unei clase, cum ar fi de exemplul, pentru clasa angajat, numarul total de angajati ai unei firme,
salariul mediu al firmei etc.
O varinata posibila ar fi stocarea acestor informatii intr-o variabila globala uzuala. De exemplu,
am putea utiliza o variabila de tip intreg pentru a pastra numarul de obiecte angajat. Problema
acestei solutii este ca variabilele globale sunt declarate in afara clasei; pentru putea fi aduse in
interiorul calsei aceste variabile trebuie sa fie declarate de tip static.
Datele statice nu se regasesc in fiecare set de valori ale clasei, ci intr-un singur exemplar, pentru
toate obiectele clasei. Datele ce apar in toate obiectele se aloca de catre un constructor , dar cum
un membru static nu face parte din nici un obiect, nu se aloca prin constructor. Asfel, la definirea
clasei, o data statica nu se considera definita, ci doar declarata, urmand a avea o definitie externa
clasei. Legatura cu declaratia din interiorul clasei se face prin operatorul de rezolutie :: precedat
de numele clasei din care face parte precum si de tipul variabilei statice, asfel:
class angajat
{
//...
Static int total_ang;
//...
}
int angajat::total_ang=0;;

Variabila total_ang va fi unica pentru toti angajatii. Se poate observa ca o data cu definirea
varbilei statice s-a realizat si intializarea acesteia.
Functiile membre statice efectueaza prelucrari care nu sunt individualizate pe obiecte, ci
prelucrari care au loc la nivelul clasei. Functiile statice nu apartin unui obiect anume si deci nu
beneficiaza de referinta implicita a obiectului asociat (pointerul this).
Daca functia membra statica opereaza pe o data nestatica a clasei, obiectul trebuie transmis
explicit ca parametru , in timp ce cu datele membre statice , lucreaza in mod direct.
Programul 2.7 redefineste clasa angajat, prin adaugarea datelor membre statice total_ang si
total_b care vor retine numarul total de angajati si numarul total de barbati. Clasa va contine si
trei functii membre statice care vor returna valorile retinute in cele doua date statice.

34

// fisierul sursa P2.7.cpp


class angajat
{
private:
int varsta;
float salariul;
char gen;
static int total_ang;
static int total_b;
public:
char nume[20];
angajat()
{strcpy(nume,Anonim);
varsta=0;
salariul=0;
total_ang++;
}
~angajat(){total_ang--;}
void seteaza_valori()
{cout<<Numele:
cin>>nume;
cout>>salariul:
cin>>salariul;
cout<<varsta
cin>>varsta;
}
static int spune_total_ang(){return total_ang;}
static int spune_total_b(){return total_b;}
static int numara_total_b(angajat *ang)
{if(ang->gen==B) total_b++;}
void afisare()
{cout<<nume: <<nume<<endl;
cout<<varsta: <<varsta<<endl;
cout<<salariul: <<salariul;
}
}
int angajat::total_ang=0;
int angajat::total_b=0;
void main()
{ angajat tab[20];
int i, n;
cout<<"\n Cate obiecte doriti sa creati?";
cin>>n;
for (i=0;i<n;i++)
{tab[i].seteaza_valori();
angajat::numara_total_b(&tab[i]);
}

35

cout<<firma are un total de


angajati<<angajat::spune_total_ang;
cout<<din care<<angajat::spune_total_b<< sunt barbati;
}

In programul de mai sus se remarca faptul ca intretinerea variabilei total_ang cade in sarcina
constructorilor si a destructorului, care incrementeaza sau decrementreaza acesta variabila.
De asemenea, functia membra statica numara_total_b() primeste ca parametru referinta la un
obiect de tip angajat pentru ca utilizeaza data membra gen care nu este statica.
ntruct variabilele membru statice nu apartin unui obiect anume, ele pot fi accesate si prin
prefixarea numelui clasei de operatorul ::.

36

Teste de autocontrol
2.1 Care este valoarea de adevr a afirmatiilor urmtoare:
(a) O clas C++ si o structur C definesc acelasi concept, de TDA.
(b) O clas C++ ncorporeaz att date ct si operatii asociate datelor, n timp ce o structur C
defineste doar datele, operatiile nefiind legate direct de datele structurii.
(c) O clas C++ ascunde datele n zona privat, n timp ce structura C permite accesul direct la
datele sale neasigurnd consistenta acestora.
(d) Structurile C nu pot fi afisate ca o singur entitate si nu pot fi comparate dect membru cu
membru.
2.2 Care este numrul maxim de constructori si destructori care se pot defini ntr-o clas?
Selectati rspunsul corect.
(a) un constructor si un destructor
(b) un constructor si mai multi destructori
(c) 10 constructori si un destructor
(d) o infinitate de constructori (teoretic) si un singur destructor
2.3 Considerm clasa Carte definit astfel:
class Carte {
char* titlu;
int an_aparitie;
char* editura;
char* ISBN;
public:
Carte();
Carte(char* T, int A, char* Ed, char* Nr);
Carte(char* T, char* Ed);
Carte(char* T, int A);
~Carte();
void afisare_date();
};

si obiectele ob1, ob2, ob3, ob4 definite astfel:


Carte ob1=Carte(Compilatoare, 2000, Teora, 052-1432);
Carte ob2=Carte(Limbajul Java, Editura Tehnica);
Carte ob3=Carte(Programare logica, 2000);
Carte ob4;
(a) Explicati cum va diferentia compilatorul cei trei constructori cu parametri, lund drept
exemplu obiectele ob1, ob2 si ob3.
(b) Ce constructor va aplica compilatorul pentru crearea obiectului ob4?
2.4 Care este rolul functiilor inline?
2.5 Care este valoarea de adevr a urmtoarelor afirmatii?
(a) Functiile friend se pot declara numai n sectiunea privat a clasei cu care sunt prietene.
(b) Utilizarea functiilor friend ncalc principiul ncapsulrii, principiu fundamental n POO.
(c) Avantajul principal al utilizrii functiilor friend este accesul rapid la membrii privati ai clasei
cu care sunt prietene.
(d) Relatia de prietenie este simetric si tranzitiv.
37

2.6 Explicati de ce este necesar definirea unui constructor implicit n cazul utilizrii tablourilor
de obiecte n limbajul C++?
2.7 Care este valoarea de adevr a afirmatiilor urmtoare:
(a) Membrii statici ai unei clase pot fi att variabile (date membru) ct si metode (functii
membru).
(b) Cuvntul cheie static desemneaz o proprietate a clasei si nu a unui obiect specific clasei.
(c) Domeniul de valabilitate al membrilor statici este tot programul n care este definit clasa din
care fac parte.
(d) n limbajul C++ datele statice trebuie initializate o singur dat.
2.8 S se defineasc o clas Date pentru memorarea datei sub forma (zi, lun, an). Clasa va
conine atia constructori ct sunt necesari pentru urmtoarele definiii de obiecte:
Date
Date
Date
Date

d1(15, 3, 99); // zi, lun, an


d2(20, 4); // zi, lun, an curent
d3(18); // zi, lun curent, an curent
d4; // zi curent,lun curent, an curent

2.9 Construiti clasa student care contine datele membre: nume, prenume, nr_matricol, an, grupa.
a). Introduceti in clasa cel putin doi constructori.
b). Realizati un tablou de obiecte de tipul clasei student;
c). Afisati numarul studentilor din grupa 201, utilizand date si functii membre statice;
2.10 Se consider urmtorul program n care o variabil global numbers memoreaz numrul
obiectelor n via la un moment dat:
#include <iostream.h>
int numbers = 0;
class Item {
public:
Item(){ // constructorul creste cu 1 nr obiectelor
numbers++;
cout << "Nr. obiecte " << numbers << endl;
}
~Item() { // destructorul descreste cu 1 nr ob.
numbers--;
cout << "Nr. obiecte " << numbers << endl;
}
};
void main() {
Item ob1, ob2, ob3;
{ // se deschide un nou bloc
Item ob4, ob5;
}
Item *pob6 = new Item[3];
delete [] pob6;
}

Care sunt mesajele care apar la consol la execuia acestui program? S se explice evoluia
numrului de obiecte n via n cursul execuiei.

38

2.11 Construiti clasa credite care contine datele membre private val_credit, nr_cont, si date
membre publice nume, tip_credit.
a). Introduceti in clasa cel putin doi constructori pentru intializarea obectelor din clasa credite.
b). Realizati un tablou cu 20 obiecte din clasa credite.
c). Realizati functii de acces la datele membre private.
d). Afisati numarul creditelor de tip ipotecar, utilizand date si functii membre.
2. 12 Definii clasa pacient care contine datele private: vrsta, profesie, salariul i data membra
public nume.
Realizai:
a). un tablou de 10 obiecte de tipul clasei definit anterior;
b). funcii membre de acces la datele private ale clasei;
c). numarul pacientilor care au varsta peste media vrstelor utiliznd o funcie membr static;
d). realizai o funcie operator pentru a calcula suma veniturilor pentru pacienii cu acelai nume
2. 13 Realizai clasa fractie care cuprinde datele membre private :numitor, numarator.
1. realizai funcii de acces la datele membre ale clasei definit anterior;
2. realizai funcii membre pentru a calcula suma si inmultirea a doua fractii, utlizand functii
operator;
3. construii un tablou cu 10 obiecte de tipul definit anterior;
4. realizai ordonarea fraciilor in ordine crescatoare dupa numitor.
2.14 Spunei de cte ori se execut fiecare constructor n programul de mai jos i n ce ordine.
#include <iostream.h>
class cls1
{ protected: int x;
public: cls1(){ x=13; } };
class cls2: public cls1
{ int y;
public: cls2(){ y=15; }
int f(cls2 ob) { return (ob.x+ob.y); } };
int main()
{ cls2 ob; cout<<ob.f(ob);
return 0;
}

2.15 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, altfel,
spunei de ce nu este corect. #include <iostream.h>
class cls1
{ int x;
public: cls1(){ x=13; }
int g(){ static int i; i++; return (i+x); } };
class cls2
{ int x;
public: cls2(){ x=27; }
cls1& f(){ static cls1 ob; return ob; } };

39

int main()
{ cls2 ob;
cout<<ob.f().g();
return 0;
}

2.16 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, altfel,
spunei de ce nu este corect. #include <iostream.h>
class cls1
{ protected: int x;
public: cls1(int i=10) { x=i; }
int get_x() { return x;} };
class cls2: cls1
{ public: cls2(int i):cls1(i) {} };
int main()
{ cls d(37);
cout<<d.get_x();
return 0;
}

40

Capitolul 3 .Mostenire
Obiective
Definirea notiunilor de clas de baz, clas derivat, ierarhie de clase, mostenire
nsusirea modalittii de creare de noi clase pornind de la clase existente
ntelegerea rolului membrilor protejati ai unei clase
nsusirea mecanismului de redefinire a membrilor unei clase ntr-o clas derivat

3.1 Relatia de mostenire. Clase de baz si clase derivate


Derivarea permite definirea ntr-un mod simplu, eficient i flexibil a unor clase noi prin
adugarea unor funcionaliti claselor deja existente, fr s fie necesar reprogramarea sau
recompilarea acestora. Clasele derivate exprim relaii ierarhice ntre conceptele pe care acestea
le reprezint i asigur o interfa comun pentru mai multe clase diferite. De exemplu, entitile
cerc, triunghi, dreptunghi, sunt corelate ntre ele prin aceea c toate sunt forme geometrice, deci
ele au n comun conceptul de form geometric. Pentru a reprezenta un cerc, un triunghi sau un
dreptunghi, ntr-un program, trebuie ca aceste clase, care reprezint fiecare form geometric n
parte, s aib n comun clasa care reprezint n general o form geometric.
Mostenirea reprezint o form de implementare a reutilizrii codului. Ea apare n urma crerii de
noi clase prin operatia de derivare.
Derivarea reprezint definirea unei noi clase prin extinderea uneia sau a mai multor clase
existente. Noua clas se numeste clas derivat, iar clasele existente din care a fost derivat se
numesc clase de baz. n cazul n care exist o singur clas de baz, mostenirea se numeste
mostenire singular. Limbajul C++ accept existenta mai multor clase de baz.
O clas derivat mosteneste toti membrii tuturor claselor sale de baz. Adic, clasa derivat
contine toate variabilele membru continute n clasele de baz si suport toate operatiile furnizate
de clasele de baz. O clas derivat poate fi la rndul ei clas de baz pentru noi clase. Astfel se
poate genera o ierarhie de clase (graf de mostenire).
Sintaxa declarrii unei clase derivate dintr-o clas de baz :
class <clasa_derivat> : public <clasa_de_baz>
{ // membrii clasei derivate
};

Mostenirea sau relatia de derivare este indicat n antetul clasei derivate prin cuvntul cheie
public prefixat de caracterul : si urmat de numele clasei de baz.
n cadrul relatiei de mostenire poate apare n clasa de baz o sectiune protejat, marcat prin
cuvntul cheie protected, care permite accesul claselor derivate din ea la datele si functiile
membru din sectiunea respectiv.

41

Observatii
O clas derivat nu poate accesa direct membrii privati ai clasei sale de baz. Dac s-ar
permite asa ceva s-ar nclca unul din principiile fundamentale ale POO (ncapsularea).
O clas derivat poate accesa membrii privati doar prin intermediul functiilor publice si
protejate ale clasei de baz.
O clas derivat poate accesa membrii publici si protejati ai clasei de baz
Relatia de mostenire este tranzitiv.
Functiile friend nu se mostenesc.
Se consider un program P3.1 care descrie organizarea personalului unei instituii fr folosirea
claselor derivate. O clas numit Angajat deine date i funcii referitoare la un angajat al
instituiei:
class Angajat{
char *nume;
float salariu;
public:
Angajat();
Angajat(char *n, float sal);
Angajat(Angajat& r);
void display();
};
Angajat::display(){
cout << nume << << salariu << endl;
}

Diferite categorii de angajai necesit date suplimentare fa de cele definite n clasa Angajat,
corespunztoare postului pe care l dein. De exemplu, un aministrator este un angajat (deci sunt
necesare toate datele care descriu aceast calitate) dar mai sunt necesare i alte informaii, de
exemplu precizare seciei pe care o conduce. De aceea, clasa Administator trebuie s includ un
obiect de tipul Angajat, la care adaug alte date:
class Administrator{
Angajat ang;
int sectie;
public:
void display();
}

Posibilitatea de a include ntr-o clas date descrise ntr-o alt clas are n limbajele orientate pe
obiecte un suport mai eficient i mai simplu de utilizat dect includerea unui obiect din tipul
dorit: derivarea claselor, care motenesc (date i funcii membre) de la clasa de baz.
Un administrator este un angajat, de aceea clasa Administrator se poate construi prin derivare din
clasa Angajat astfel:

42

class Administrator : public Angajat {


int sectie;
public:
void display();
}
In clasa Administrtrator nu se pot aceesa datele private din clasa Angajat, chiar data tipul
mostenerii este public. Asa cu s-a subliniat inainte, datele private ale clasei de baza nu pot fi
utilizate de catre clasa derivata.
Metoda cea mai adecvat de acces la membrii private clasei de baz din clasa derivat este prin
utilizarea funciilor membre publice ale clasei de baz. De exemplu, nu se poate implementa
funcia display() din clasa Administrator prin accesarea membrilor private ai clasei Angajat:
void Administrator::display(){
cout << nume << << salariu << endl; // eroare
cout << sectie << endl;
}
n schimb, se poate folosi funcia membr public display() a clasei Angajat:
void Administrator::display(){
Angajat::display();
cout << sectie << endl;
}

Redefinirea funciei display() n clasa derivat ascunde funcia cu acelai nume din clasa de baz,
de aceea este necesar calificarea funciei cu numele clasei de baz folosind operatorul de
rezoluie: Angajat::display().
Din clasa derivat (n funcii membre ale acesteia sau pentru obiecte din clasa derivat) este
accesat membrul redefinit n clasa derivat. Se spune c membrul din clasa de baz este ascuns
(hidden) de membrul redefinit n clasa derivat. Un membru ascuns din clasa de baz poate fi
totui accesat dac se folosete operatorul de rezoluie (::) pentru clasa de baz. De exemplu:
class Base {
public:
int a, b;
}
class Derived {
public:
int b, c; // b este redefinit
};
void fb() {
Derived d;
d.a = 1; // a din Base
d.Base::b = 2; // b din Base
d.b = 3; // b din Derived
d.c = 4; // c din Derived
}

43

3.2 Constructori i destructori n clasele derivate


Constructorii i destructorii sunt funcii membre care nu se motenesc. La crearea unei instane a
unei clase derivate (obiect) se apeleaz implicit mai nti constructorii claselor de baz i apoi
constructorul clasei derivate. Ordinea n care sunt apelai constructorii claselor de baz este cea
din lista claselor de baz din declaraia clasei derivate. Constructorii nu se pot redefini pentru c
ei, n mod obligatoriu, au nume diferite (numele clasei respective).
La distrugerea unui obiect al unei clase derivate se apeleaz implicit destructorii n ordine
invers: mai nti destructorul clasei derivate, apoi destructorii claselor de baz, n ordinea
invers celei din lista din declaraie.
La instanierea unui obiect al unei clase derivate, dintre argumentele care se transmit
constructorului acesteia o parte sunt utilizate pentru iniializarea datelor membre ale clasei
derivate, iar alt parte sunt transmise constructorilor claselor de baz. Argumentele necesare
pentru iniializarea claselor de baz sunt plasate n definiia (nu n declaraia) constructorului
clasei derivate.
Un exemplu simplu, n care constructorul clasei derivate D transfer constructorului clasei de
baz B un numr de k argumente arat astfel:
class B{
//.
public:
B(tip1 arg1,,tipk argk);
};
class D:public B {
//.
public:
D(tip1 arg1, ,tipk argk,,tipn argn);
};
D::D(tip1 arg1, ,tipk argk, .,tipn argn)
:B(arg1, arg2, ,argk);
{
// initialzare date membre clasa derivata
}
Sintaxa general pentru transmiterea de argumente din clasa derivat ctre clasa de baz:
<construct_cls_derivata>(<L1>) : <construct_cls_baza>(<L2>)
{
// corpul constructorului clasei derivate
}

<L1> reprezint lista de argumente ale constructorului clasei derivate, iar <L2> lista de
argumente ale constructorului clasei de baz. Lista <L1> include lista <L2>.
Observatii:
Constructorii si destructorul clasei de baz nu se mostenesc.

44

Constructorul clasei de baz se va executa naintea constructorului clasei derivate, iar


destructorul clasei derivate se va executa naintea destructorului clasei de baz.

3.3 Controlul accesului la membrii clasei de baz


Accesul la membrii clasei de baz motenii n clasa derivat este controlat de specificatorul de
acces (public, protected, private) din declaraia clasei derivate.
O regul general este c, indiferent de specificatorul de acces declarat la derivare, datele de tip
private n clasa de baz nu pot fi accesate dintr-o clas derivat. O alt regul general este c,
prin derivare, nu se modific tipul datelor n clasa de baz.
Un membru protected ntr-o clas se comport ca un membru private, adic poate fi accesat
numai de membrii acelei clase i de funciile de tip friend ale clasei. Diferena ntre tipul private
i tipul protected apare n mecanismul de derivare: un membru protected al unei clase motenit
ca public ntr-o clas derivat devine tot protected n clasa derivat, adic poate fi accesat numai
de funciile membre i friend ale clasei derivate i poate fi transmis mai departe, la o nou
derivare, ca tip protected.
Motenirea de tip public a clasei de baz
Dac specificatorul de acces din declaraia unei clase derivate este public, atunci:
Datele de tip public ale clasei de baz sunt motenite ca date de tip public n clasa derivat
i deci pot fi accesate din orice punct al domeniului de definiie al clasei derivate.
Datele de tip protected n clasa de baz sunt motenite protected n clasa derivat, deci pot
fi accesate numai de funciile membre i friend ale clasei derivate.
n programul urmator P3.2 sunt prezentate i comentate cteva din situaiile de acces la membrii
clasei de baz din clasa derivat atunci cnd specificatorul de acces este public.
class Base {
inta;
protected:
int b;
public:
int c;
void seta(int x){a = x; cout << "seta din baza\n";}
void setb(int y){b = y; cout << "setb din baza\n";}
void setc(int z){c = z; cout << "setc din baza\n";}
};
class Derived : public Base {
int d;
public:
void seta(int x) {
a = x; // eroare, a este private
}
void setb(int y) {

45

b = y;
cout << "setb din derivata\n";
}
void setc(int z) {
c = z;
cout << "setc din derivata\n";
}
};
void fb(){
Derived obd;
obd.a = 1; // eroare, a este private in baza
obd.seta(2); // corect, se apeleaz baza::seta
obd.b = 3; // eroare, b este protected
obd.Base::setb(5);// corect, Base::setb este public
obd.setb(4); // corect, Derived::setb este public
obd.c = 6; // corect, c este public
obd.Base::setc(7);// corect, Base::setc este public
obd.setc(8); // corect, Derived::setc este public
}

Dac se comenteaz liniile de program care provoac erori i se execut funcia fb(), se obin
urmtoarele mesaje la consol:
seta
setb
setb
setc
setc

din
din
din
din
din

baza
baza
derivata
baza
derivata

Motenirea de tip protected a clasei de baz


Dac specificatorul de acces din declaraia clasei derivate este protected, atunci toi membrii de
tip public i protected din clasa de baz devin membri protected n clasa derivat. Bineneles,
membrii de tip private n clasa de baz nu pot fi accesai din clasa derivat.
Se reiau clasele din exemplul precedent cu motenire protected:
class Derived : protected Base {
// acelasi corp al clasei
};

n aceast situaie, n funcia fb() sunt anunate ca erori de compilare toate apelurile de funcii ale
clasei de baz pentru un obiect derivat, precum i accesul la variabila c a clasei de baz:
void fb(){
Derived obd;
obd.a = 1; // eroare, a este private in baza
obd.seta(2); // eroare, Base::seta()este protected
obd.b = 3; // eroare, b este protected

46

obd.Base::setb(5); // eroare, Base::setb este prot.


obd.setb(4); // corect, Derived::setb este public
obd.c = 6; // eroare, c este protected
obd.Base::setc(7); // eroare, Base::setc este prot.
obd.setc(8); // corect, Derived::setc este public
}

Dac se comenteaz toate liniile din funcia fb() care produc erori, la execuia acesteia se afieaz
urmtoarele rezultate:
setb din derivata
setc din derivata

Din acest exemplu reiese pregnant faptul c n motenirea protected a unei clase de baz nu mai
pot fi accesai din afara clasei derivate nici unul dintre membrii clasei de baz.
Motenirea de tip private a clasei de baz
Dac specificatorul de acces din declaraia clasei derivate este private, atunci toi membrii de tip
public i protected din clasa de baz devin membri de tip private n clasa derivat i pot fi
accesai numai din funciile membre i friend ale clasei derivate. Din nou trebuie reamintit c
membrii de tip private n clasa de baz nu pot fi accesai din clasa derivat. Din punct de vedere
al clasei derivate, motenirea de tip private este echivalent cu motenirea de tip protected. ntradevr, dac modificm clasa derivata din Exemplul 5.2 astfel:
class Derived : private Base {
// acelasi corp al clasei
};

mesajele de erori de compilare i de execuie ale funciei fb() sunt aceleai ca i n motenirea
protected.
Ceea ce difereniaz motenirea de tip private fa de motenirea de tip protected este modul cum
vor fi trasmii mai departe, ntr-o nou derivare, membrii clasei de baz. Toi membrii clasei de
baz
fiind
motenii
de
tip
private,
o nou clas derivat (care motenete indirect clasa de baz) nu va mai putea accesa nici unul
din membrii clasei de baz.

3.4 Mostenirea multipla


Relatia de derivare conduce la generarea unor ierarhii de clase. n astfel de ierarhii poate apare
att mostenirea singular ct si cea multipl.
Un exemplu de ierarhie de clasa este reprezentat in figura 3.4
Persoana

Student

Elev

Angajat

Medic

Profesor

47

Fig. 3.4 Ierarhie de clase

Clasa de baz a ierarhiei de clase este clasa Persoana. Din aceast clas sunt derivate direct
clasele Elev, Student si Salariat. La rndul ei clasa Salariat este clas de baz pentru clasele
Medic si Profesor.
Programul P3.3 prezint un exemplu de implementare a claselor Persoana, Salariat, Arhitect,
Inginer si Medic.
// fisierul sursa p3_5.cpp
#include <iostream.h>
#include <string.h>
#include <assert.h>
// definitia clasei de baza Persoana
class Persoana {
char* nume;
char* pren;
public:
Persoana(char*, char*);
~Persoana();
void afisare();
};
Persoana::Persoana(char* N, char* P)
{
nume = new char[strlen(N)+1];
strcpy(nume, N);
pren = new char[strlen(P)+1];
strcpy(pren, P);
}
Persoana::~Persoana()
{
delete nume;
delete pren;
}
void Persoana::afisare()
{
cout << "\n Nume: "<< nume << " Prenume: " << pren;
}
// definitia clasei Salariat derivata din clasa Persoana
class Salariat : public Persoana {
float salariu;
public:
Salariat(char *n, char *p, float s=0);
void seteazaSalariu(float s);
void afisare();
};
48

Salariat::Salariat(char* n, char* p, float S)


:Persoana(n, p)
{
seteazaSalariu(s);
}
void Salariat::seteazaSalariu(float S)
{
salariu = s;
}
void Salariat::afisare()
{
cout << "\n Salariat:";
Persoana::afisare();
cout << "\n Salariu:" << salariu;
}
// definitia clasei Inginer derivata din clasa Salariat
class Inginer : public Salariat {
char* domeniu;
public:
Inginer(char* n, char* p, float s, char* d);
void seteazaDomeniu(char *d);
void afisare();
};
Inginer::Inginer(char* n, char* p, float s, char * d)
:Salariat(n, p, s)
{
domeniu = new char[strlen(d)+1];
strcpy(domeniu, d);
}
void Inginer::seteazaDomeniu(char* d)
{
strcpy(domeniu, d);
}
void Inginer::afisare()
{
cout << "\n Inginer ";
Salariat::afisare();
cout << "\n\t Domeniu de lucru este " << domeniu;
}
class Profesor : public Salariat {
char* tip;
public:
Profesor(char* n, char* p, float s, char* t);
void seteazaTip(char *t);
void afisare();
};
Profesor::Profesor(char* n, char* p, float s, char* t)
:Salariat(n, p, s)
{

49

tip = new char[strlen(t)+1];


strcpy(tip_inginer, t);
}
void Profesor::seteazaTip(char* t)
{
strcpy(tip_inginer, T);
}
void Profesor::afisare()
{
cout << "\n Profesor ";
Salariat::afisare();
cout << "\n\t Tip profesor " << tip;
}
void main()
{
Persoana P1=Persoana("Popescu", "Ana");
Salariat S1=Salariat("Ion", "Alexandru", 2300);
Inginer I1=Inginer("Syan", "Maria", 3400,
"inginer civile");
Profesor PR1=Inginer("Pop", "Cristi", 2200,
"informatica");
P1.afisare();
S1.afisare();
I1.afisare();
PR1.afisare();
}
Mostenirea utilizat este cea public. Functia afisare() a fost redefinit n toate clasele derivate,
ea apelnd varianta din clasa imediat urmtoare pe nivelul superior din ierarhia de clase.
De exemplu, functia afisare() din clasa Salariat apeleaz functia afisare() din clasa Persoana
astfel,
Persoana::afisare();

Functia afisare() din clasele Inginer si Profesor apeleaz functia afisare() din clasa Salariat:
Salariat::afisare();

3.4 Clase de baz virtuale


ntr-o motenire multipl este posibil ca o clas s fie motenit indirect de mai multe ori, prin
intermediul unor clase care motenesc, fiecare n parte, clasa de baz. De exemplu:
class
class
class
class

L
A
B
D

{
:
:
:

public: int x;};


public L { /* */};
public L { /* */};
public A, public B { /* */};

Acest motenire se poate reprezenta printr-un graf aciclic direcionat care indic relaiile dintre
subobiectele unui obiect din clasa D. Din graful de reprezentare a motenirilor, se poate observa
faptul c baza L este replicat n clasa D.

50

Un obiect din clasa D va conine membrii clasei L de dou ori, o dat prin clasa A (A::L) i o
dat prin clasa B (B::L). In acest caz ser creaza o ambiguitate in situatia urmatoare :
D ob;
ob.x = 2; // eroare D::x este ambiguu; poate fi n baza L a clasei A sau n baza L a
clasei B

Aceste ambiguitati se pot elimina prin urmatoarele doua metode:


- prin calificarea variabilei cu domeniul clasei creia i aparine:
ob.A::x = 2; // corect, x din A
ob.B::x = 3; // corect, x din B

- crearea unei singure copii a clasei de baz n clasa derivat. Pentru aceasta este necesar ca acea
clas care ar putea produce copii multiple prin motenire indirect (clasa L, n exemplul de mai
sus) s fie declarat clas de baz de tip virtual n clasele care o introduc n clasa cu motenire
multip. De exemplu:
class
class
class
class

L
A
B
D

{
:
:
:

public: int x; };
virtual public L { /* */ };
virtual public L { /* */ };
public A, public B { /* */ };

O clas de baz virtual este motenit o singur dat i creeaz o singur copie n clasa derivat.
Graful de reprezentare a motenirilor din aceste declaraile de mai sus poate fi ilustrat asfel:

3.6 Funcii virtuale i polimorfism


O funcie virtual este o funcie care este declarat de tip virtual n clasa de baz i redefinit ntro clas derivat. Redefinirea unei funcii virtuale ntr-o clas derivat domin definiia funciei n

51

clasa de baz. Funcia declarat virtual n clasa de baz acioneaz ca o descriere generic prin
care se definete interfaa comun, iar funciile redefinite n clasele derivate precizeaz aciunile
specifice fiecrei clase derivate.
Mecanismul de virtualitate asigur selecia funciei redefinite n clasa derivat numai la apelul
funciei pentru un obiect cunoscut printr-un pointer. n apelul ca funcie membr a unui obiect dat
cu numele lui, funciile virtuale se comport normal, ca funcii redefinite.
n limbajele de programare, un obiect polimorfic este o entitate, ca de exemplu, o variabil sau
argumentul unei functii, creia i se permite s pstreze valori de tipuri diferite n timpul executiei
programului. Functiile polimorfice sunt acele functii care au argumente polimorfice. n limbajele
de programare orientate pe obiecte, polimorfismul mpreun cu legarea dinamic reprezint una
din caracteristicile extrem de utile care conduc la cresterea calittii programelor.
Implementarea obiectelor polimorfice se realizeaz prin intermediul functiilor virtuale.
Sintaxa declarrii unei functii virtuale:
virtual <tip_functie> <nume_functie> ([<lp>]);
<tip_functie> reprezint tipul ntors de functie, <lp> este

Cnd un pointer al clasei de baz puncteaz la o functie virtual din clasa derivat si aceasta este
apelat prin intermediul acestui pointer, compilatorul determin care versiune a functiei trebuie
apelat, tinnd cont de tipul obiectului la care puncteaz acel pointer. Astfel, tipul obiectului la
care puncteaz determin versiunea functiei virtuale care va fi executat.
In programul P3.4 se consider o clas de baz B i dou clase derivate D1 i D2. n clasa de
baz sunt definite dou funcii: funcia normal f()i funcia virtual g(). n fiecare din clasele
derivate se redefinesc cele dou funcii f() i g(). n funcia main() se creeaz trei obiecte: un
obiect din clasa de baz B indicat prin pointerul B* pb i dou obiecte din clasele derivate D1 i
D2. Fiecare dintre obiectele derivate poate fi indicat printr-un pointer la clasa derivat respectiv
(D1* pd1, respectiv D2* pd2), precum i printr-un pointer la baz corespunztor (B* pb1 = pd1,
respectiv B* pb2 = pd2).
class B {
public:
void f() { cout << "f() din B\n"; }
virtual void g(){ cout << "g() din B\n"; }
};
class D1:public B {
public:
void f() { cout << "f() din D1\n"; }
void g() { cout << "g() din D1\n"; }
};
class D2:public B {
public:
void f() { cout << "f() din D2\n"; }

52

void g() { cout << "g() din D2\n"; }


};
void fv1 {
B* pb = new B;
D1* pd1 = new D1;
D2* pd2 = new D2;
B* pb1 = pd1;
B* pb2 = pd2;
// f() este functie normala,g() este functie virtuala
// Obiect B, pointer B*
pb->f(); // f() din B
pb->g(); // g() din B
// Obiecte D1, D2 , pointeri D1*, D2*
pd1->f(); // f() din D1
pd2->f(); // f() din D2
pd1->g(); // g() din D1
pd2->g(); // g() din D2
// Obiecte D1, D2 , pointeri B*, B*
pb1->f(); // f() din B
pb2->f(); // f() din B
pb1->g(); // g() din D1
pb2->g(); // g() din D2
delete pb;
delete pd1;
delete pd2;
};

n primele situaii, cnd pointerul este pointer la tipul obiectului, nu se manifest nici o deosebire
ntre comportarea unei funcii virtuale fa de comportarea unei funcii normale: se selecteaz
funcia corespunztoare tipului pointerului i obiectului.
Diferena de comportare se manifest n ultima situaie, atunci cnd este apelat o funcie pentru
un obiect de tip clas derivat printr-un pointer la o clas de baz a acesteia. Pentru funcia
normal f() se selecteaz varianta depinznd de tipul pointerului. Pentru funcia virtual g() se
selecteaz varianta n funcie de tipul obiectului, chiar dac este accesat prin pointer de tip baz.
Polimorfismul, adic apelul unei funcii dintr-o clas derivat prin pointer de tip clas de baz,
este posibil numai prin utilizarea pointerilor la obiecte. Obiectele nsele determin varianta
funciei apelate, deci nu se pot selecta alte funcii dect cele ale obiectului de tipul respectiv. De
exemplu, pentru aceleai clase definite ca mai sus, se consider funcia fv2():
void fv2(){
B obB;
D1 obD1;
D2 obD2;
obB.f(); // f() din B
obB.g(); // g() din B
obD1.f(); // f() din D1
obD1.g(); // g() din D1

53

obD2.f(); // f() din D2


obD2.g(); // g() din D2
}

Observati
Constructorii nu pot fi functii virtuale. n schimb, destructorii pot fi functii virtuale.
Functiile inline nu pot fi virtuale.
Functiile virtuale sunt ntotdeauna functii membru nestatice ale unei clase.

3.7

Clase abstracte

De cele mai multe ori, o funcie declarat de tip virtual n clasa de baz nu definete o aciune
semnificativ i este neaprat necesar ca ea s fie redefinit n fiecare din clasele derivate. Pentru
ca programatorul s fie obligat s redefineasc o funcie virtual n toate clasele derivate n care
este folosit aceast funcie, se declar funcia respectiv virtual pur. O funcie virtual pur
este o funcie care nu are definiie n clasa de baz, iar declaraia ei arat n felul urmtor:
virtual tip_returnat nume_functie(lista_argumente) = 0;
O clas care conine cel puin o funcie virtual pur se numete clas abstract. Deoarece o clas
abstract conine una sau mai multe funcii pentru care nu exist definiii, nu pot fi create instane
din acea clas, dar pot fi creai pointeri i referine la astfel de clase abstracte. O clas abstract
este folosit n general ca o clas fundamental, din care se construiesc alte clase prin derivare.
Orice clas derivat dintr-o clas abstract este, la rndul ei clas abstract (i deci nu se pot crea
instane ale acesteia) dac nu se redefinesc toate funciile virtuale pure motenite. Dac o clas
redefinete toate funciile virtuale pure ale claselor ei de baz, devine clas normal i pot fi
create instane ale acesteia.
Exemplul urmtor (5.6) evideniaz caracteristicile claselor abstracte i ale funciilor virtuale
pure.
Programul P3.5 se realizeaza conversia unor date dintr-o valoare de intrare ntr-o valoare de
ieire; de exemplu, din grade Farenheit n grade Celsius, din inch n centimetri, etc.
class Convert{
protected:
double x; // valoare intrare
double y; // valoare iesire
public:
Convert(double i){x = i;}
double getx(){return x;}
double gety(){return y;}
virtual void conv() = 0;
};
// clasa FC de conversie grade Farenheit in grade Celsius
class FC: public Convert{
public:
FC(double i):Convert(i){}
void conv(){y = (x-32)/1.8;}

54

};
// clasa IC de conversie inch in centimetri
class IC: public Convert{
public:
IC(double i):Convert(i){}
void conv(){y = 2.54*x;}
}

void main (){


Convert* p = 0; // pointer la baza
cout<<"Introduceti valoarea si tipul conversiei: ";
double v;
char ch;
cin >> v >> ch;
switch (ch){
case 'i': //conversie inch -> cm (clasa IC)
p = new IC(v);
break;
case 'f': //conv. Farenheit -> Celsius (clasa FC)
p = new FC(v);
break;
}
if (p){
p->conv();
cout << p->getx() << "---> " << p->gety()<< endl;
delete p;
}
}

Clasa de baz abstract Convert este folosit pentru crearea prin derivare a unei clase specifice
fiecrui tip de conversie de date dorit. Aceast clas definete datele comune, necesare oricrui
tip de conversie preconizat, de la o valoare de intrare x la o valoare de ieire y. Funcia de
conversie conv() nu se poate defini n clasa de baz, ea fiind specific fiecrui tip de conversie n
parte; de aceea funcia conv() se declar funcie virtual pur i trebuie s fie redefinit n fiecare
clas derivat.
n funcia main() se execut o conversie a unei valori introduse de la consol, folosind un tip de
conversie (o clas derivat) care se selecteaz pe baza unui caracter introdus la consol.
Acesta este un exemplu n care este destul de pregnant necesitatea funciilor virtuale: deoarece
nu se cunoate n momentul compilrii tipul de conversie care se va efectua, se folosete un
pointer la clasa de baz pentru orice operaie (crearea unui obiect de conversie nou, apelul
funciei conv(), afiarea rezultatelor, distrugerea obiectului la terminarea programului). Singura
difereniere care permite selecia corect a funciilor, este tipul obiectului creat, care depinde de
tipul conversiei cerute de la consol.

55

3.8

Polimorfism

Polimorfismul permite unei entitti (de exemplu, variabil, functie, obiect) s aib o varietate de
reprezentri. El este furnizat att la momentul compilrii (legare timpurie), prin folosirea
operatorilor si a functiilor redefinite, ct si la momentul executiei (legare trzie), prin utilizarea
functiilor virtuale.
Conceptul de legare dinamic permite unei variabile s aib tipuri diferite n functie de continutul
ei la un moment dat. Aceast abilitate a variabilei se numeste polimorfism, iar variabila se
numeste variabil polimorfic.
n limbajul C++ variabilele polimorfice apar doar prin utilizarea pointerilor sau referintelor. n
cazul n care un pointer al clasei de baz puncteaz ctre o functie virtual, programul va
determina la momentul executiei la care tip de obiect puncteaza pointerul si apoi va selecta
versiunea corespunztoare functiei redefinite.
Programul P4.4 defineste clasa de baz Persoana si dou clase derivate, Student si Salariat. Clasa
de baz Persoana este o clas abstract avnd declarat o functie virtual pur, venit(), definit n
clasele derivate. De asemenea, clasa Persoana mai contine o alt functie virtual, afisare(), care
este redefinit n clasele derivate. n programul principal sunt create dou obiecte S1 si T1 din
clasele Student si respectiv Salariat. Pentru fiecare din cele dou obiecte se execut o secvent de
patru instructiuni: primele dou instructiuni ilustreaz legarea static, prin apelul celor dou
functii afisare() si venit(), corespunztoare obiectului referit prin nume; ultimele dou instructiuni
ilustreaz legarea dinamic apelnd cele dou functii afisare() si venit() prin intermediul a dou
functii, Ref_Pointer(.), Ref_Referinta(.), care utilizeaz un pointer, respectiv o referint ctre
clasa de baz Persoana.
Aceste ultime dou instructiuni genereaz un comportament polimorfic, la momentul executiei
programului.
// fisierul sursa p4_4.cpp
#include <iostream.h>
#include <string.h>
#include <assert.h>
// clasa de baza Persoana, clasa abstracta
class Persoana {
char* prenume;
char* nume;
public:
Persoana(char*, char*);
~Persoana();
char* preiaPrenume();
char* preiaNume();
// functie virtuala pura
virtual double venit() = 0;
virtual void afisare();
};
Persoana::Persoana(char* P, char* N)
{
prenume = new char[strlen(P)+1];

56

strcpy(prenume, P);
nume = new char[strlen(N)+1];
strcpy(nume, N);
}
Persoana::~Persoana()
{
delete prenume;
delete nume;
}
char* Persoana::preiaPrenume()
{
return prenume;
}
char* Persoana::preiaNume()
{
return nume;
}
void Persoana::afisare()
{
cout << prenume << " " << nume << "\n";
}
// definim clasa Student derivata din clasa Persoana
class Student : public Persoana {
double bursa;
double media;
public:
Student(char*, char*, double = 0.0, double = 0.0);
void seteazaBursa(double);
void seteazaMedia(double);
virtual double venit();
virtual void afisare();
};
Student::Student(char* P, char* N, double B, double M)
:Persoana(P,N)
{
M>=8.50?seteazaBursa(B):seteazaBursa(0.0);
seteazaMedia(M);
}
void Student::seteazaBursa(double B)
{
bursa = B>0?B:0;
}
void Student::seteazaMedia(double M)
{
media = M>0?M:0.0;
}
double Student::venit()
{
return bursa;

57

}
void Student::afisare()
{
cout << "\n Student:";
Persoana::afisare();
cout << "\t Media = " << media << "\n";
}
// definim clasa Salariat derivata din clasa Persoana
class Salariat:public Persoana {
double salariu;
double venit_ora;
int nr_ore;
public:
Salariat(char*, char*, double = 0.0, double = 0.0, int = 0);
void seteazaSalariu(double);
void seteazaVenitOra(double);
void seteazaNrOre(int);
virtual double venit();
virtual void afisare();
};
Salariat::Salariat(char* P,char* N,double S,double V,int nr)
:Persoana(P, N)
{
seteazaSalariu(S);
seteazaVenitOra(V);
seteazaNrOre(nr);
}
void Salariat::seteazaSalariu(double S)
{
salariu = S>0 ? S : 0;
}
void Salariat::seteazaVenitOra(double V)
{
venit_ora = V>0.0 ? V : 0.0;
}
void Salariat::seteazaNrOre(int nr)
{
nr_ore = nr>0 ? nr : 0;
}
double Salariat::venit()
{
return salariu + nr_ore*venit_ora;
}
void Salariat::afisare()
{
cout << "\n Salariat:";
Persoana::afisare();
}
// functie care apeleaza functiile virtuale prin legare

58

// dinamica, in cazul unui pointer al clasei de baza


// referire prin pointer
void Ref_Pointer(Persoana *Ptr)
{
Ptr->afisare();
cout << " venit (lei) " << Ptr->venit();
}
// functie care apeleaza functiile virtuale prin legare
// dinamica, in cazul unei referinte la clasa de baza
// referire prin referinta
void Ref_Referinta(Persoana &Ptr)
{
Ptr.afisare();
cout << " venit (lei) " << Ptr.venit();
}
void main()
{
Student S1("Alexandra", "Stoica", 1500000, 10);
cout << "\n Legare statica:\n";
S1.afisare();
cout << " venit (lei) "<<S1.venit();
cout << "\n Legare dinamica:\n";
Ref_Pointer(&S1);
Ref_Referinta(S1);
Salariat T1("Silvan", "Manole", 50000000, 100000, 30);
cout << "\n Legare statica:\n";
T1.afisare();
cout<<" venit (lei) "<<T1.venit();
cout << "\n Legare dinamica: \n";
Ref_Pointer(&T1);
Ref_Referinta(T1);
}
Observatii
Principiul care st la baza polimorfismului este o singur interfat, metode multiple.
Practic, polimorfismul reprezint abilitatea obiectelor din clase diferite ale unei ierarhii de
clase de a rspunde diferit la acelasi mesaj (adic,la apelul unei functii membru).
Implementarea polimorfismului este realizat prin intermediul functiilor virtuale.
n cazul n care o functie membru ne-virtual este definit ntr-o clas de baz si redefinit
ntr-o clas derivat, comportamentul este ne-polimorfic. Astfel, dac functia membru
este apelat printr-un pointer al clasei de baz la obiectul clasei derivate, se utilizeaz
versiunea clasei de baz. Dac functia membru este apelat printr-un pointer al clasei
derivate, se utilizeaz versiunea clasei derivate.

59

Teste de autocontrol
3.1 Definiti relatia de derivare.
3.2 Definiti mostenirea.
3.3 Dac din clasa X se genereaz o clas Z cum se numesc cele dou clase?
(a) X clas derivat, Z clas de baz
(b) X superclas, Z subclas
(c) X clas copil, Z clas printe
(d) X clas de baz, Z clas derivat
3.4 Care dintre afirmatiile urmtoare sunt adevrate si care sunt false?
(a) Obiectele unei clase derivate au acces la membrii privati ai clasei sale de baz.
(b) Relatia de mostenire este tranzitiv.
(c) Functiile friend ale clasei de baz se mostenesc de ctre clasa derivat.
(d) Constructorul si destructorul clasei de baz se mostenesc n clasa derivat.
3.5 Selectati rspunsul corect referitor la ordinea de apelare a constructorilor si a destructorilor n
cazul claselor derivate dintr-o clas de baz.
Ordinea de apelare este urmtoarea:
(a) constructorul clasei derivate constructorul clasei de baz destructorul clasei derivate
destructorul clasei de baz
(b) constructorul clasei de baz constructorul clasei derivate destructorul clasei derivate
destructorul clasei de baz
(c) constructorul clasei derivate constructorul clasei de baz destructorul clasei de baz
destructorul clasei derivate
(d) constructorul clasei de baz constructorul clasei derivate
destructorul clasei de baz
3.6 Care este avantajul principal oferit de mecanismul mostenirii?
3.7 Utilizarea mostenirii si a polimorfismului permite eliminarea unei anumite instructiuni. Care
este aceast instructiune n limbajul C++?
3.8 Cum este specificat o functie virtual pur?
3.9 Cum se numeste o clas care contine una sau mai multe functii virtuale pure?
3.10 Dac apelul unei functii este rezolvat la momentul executiei legarea este
(a) static
(b) timpurie
(c) nul
(d) dinamic

60

3.11 Care dintre afirmatiile urmtoare sunt adevrate?


(a) Constructorii pot fi functii virtuale.
(b) Destructorul poate fi functie virtual.
(c) Orice functie membru static este functie virtual.
(d) Functiile inline nu pot fi functii virtuale.
3.12 Care este diferenta ntre o functie virtual si o functie virtual pur?
3.13 Ce se ntelege prin polimorfism pur? Comparati cu notiunea de suprancrcare.
3.14 Cum pot fi definite variabilele polimorfice n limbajul C++?
3.15 Explicati pe scurt notiunea de polimorfism n cazul programrii orientate pe obiecte.
3.16 Care este rolul claselor abstracte?
3.17 Scrieti un program care defineste clasa de baz Aparat si clasa derivat Radio. Printre
functiile membru includeti si o functie de afisare a datelor membru ale clasei. n programul
principal se vor crea dou obiecte ob1 al clasei Aparat si ob2 al clasei Radio si se vor apela
functiile de afisare.
3.18 Redefiniti functia de afisare a clasei Aparat n clasa Radio si rescrieti programul de la
exercitiul 3.17.
3.19 Rescrieti programul de la exercitiul T3.2 realiznd suprancrcarea constructorului clasei
derivate Radio.
3.20 Care sunt erorile din urmtoarea secvent de program?
//
class A {
int a,b;
public:
A(int , int, double);
void afisare();
//
protected:
double t;
};
//
class B::public A {
double w;
public:
B(int, int, double);
void afisare();
void setValori(int x, int y, double z)
{ a=x; b=y; t=z;}
};
//
61

3.21 Fiind date urmtoarele tipuri de clase:


class B { /* instructiuni */ };

class D1: virtual B { /* instructiuni */ };


class D2: virtual B { /* instructiuni */ };
class D3: B { /* instructiuni */ };
class D4: private B { /* instructiuni */ };
class D5: virtual public B { /* instructiuni */ };
class M1: D1, public D2, D3, private D4, virtual D5
{ /* instructiuni */ };
class M2: D1, D2, virtual D3, virtual D4, virtual D5
{ /* instructiuni */ };
spunei de cte ori este motenit clasa B n clasa M1. Dar n clasa M2 ? Justificai.
3.22 Spunei care dintre declaraiile funciei main() sunt corecte n programul de mai jos. Justificai.
#include <iostream.h>

class B1 { public: int x; };


class B2 { int y; };
class B3 { public: int z; };
class B4 { public: int t; };
class D: private B1, protected B2, public B3, B4
{ int u; };
int main()
{ D d;
cout<<d.u;
cout<<d.x;
cout<<d.y;
cout<<d.z;
cout<<d.t;
return 0;
}
3. 23 Spunei dac programul de mai jos este corect. n caz afirmativ, precizai exact constructorii
care se execut i n ce ordine. n caz negativ spunei de ce nu este corect. #include<iostream.h>

class cls1
{ public: int x;
cls1(int i=13) { x=i; } };
class cls2: virtual public
{ public: cls2(int i=15) {
class cls3: virtual public
{ public: cls3(int i=17) {

cls1
x=i; } };
cls1
x=i; } };
62

class cls4: public cls1


{ public: cls4(int i=19) { x=i; } };
class cls5: public cls2, public cls3, public cls4
{ public: int y;
cls5(int i,int j):cls4(i),cls2(j){ y=i+j; }
cls5(cls5& ob) ){ y=-ob.y; }};
int main()
{ cls5 ob1(-9,3), ob2=ob1;
cout<<ob2.y;
return 0;
}
3.24 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz
negativ spunei de ce nu este corect. #include <iostream.h>
class B
{ int a;
public: B(int i=0) { a=i; }
int get_a(){ return a; } };
class D: private B
{ public: D(int x=0): B(x) {}
int get_a() { return B::get_a(); } };
int main()
{ D d(-89);
cout<<d.get_a();
return 0;
}

63

Cap. 4 Facilitti ale limbajului C++

Obiective
Redefinirea operatorilor
Definirea template-urilor
nsusirea modului de lucru cu fisiere n limbajul C++

4.1 Supraincarcarea operatorilor


Limbajul C++ permite programatorului s defineasc diverse operatii cu obiecte ale claselor,
utiliznd simbolurile operatorilor standard. Pentru tipurile fundamentale ale limbajului sunt
definite seturi de operatori care permit operaii de baz executate ntr-un mod convenabil. Dar,
dup cum este cunoscut, n limbaj sunt definite prea puine tipuri de date ca date fundamentale,
iar pentru reprezentarea altor tipuri care sunt necesare n diferite domenii (cum ar fi aritmetica
numerelor complexe, algebra matricilor, etc.), se definesc clase care conin funcii ce pot opera
asupra acestor tipuri.
Limbajul C++ nu permite crearea de noi operatori, n schimb permite redefinirea majorittii
operatorilor existenti astfel nct atunci cnd acesti operatori sunt aplicati obiectelor unor clase s
aib semnificatia corespunztoare noilor tipuri de date.
Principalele avantaje ale redefinirii operatorilor sunt claritatea si usurinta cu care se exprim
anumite operatii specifice unei clase. Solutia alternativ ar fi definirea unor functii si apelul
functiilor n cadrul unor expresii.
Funciile operator pentru o anumit clas pot s fie sau nu funcii membre ale clasei. Dac nu
sunt funcii membre ele sunt, totui, funcii friend ale clasei i trebuie s aib ca argument cel
puin un obiect din clasa respectiv sau o referin la aceasta.

Funcii operator membre ale claselor


Forma general pentru funciile operator membre ale clasei este urmtoarea:
tip_returnat operator # (lista_argumente){
// operaii
}
Semnul # reprezint oricare dintre operanzii care pot fi suprancrcai.

Programul P 4.1 prezinta clasa Point care descrie un vector ntr-un plan bidimensional prin
dou numere de tip float, x i y. Valorile x i y reprezint coordonatele punctului de

64

extremitate al vectorului. Pentru aceast clas se pot defini mai multe operaii cu vectori, ca de
exemplu:

Suma a doi vectori


Diferena a doi vectori
Produsul scalar a doi vectori
Multiplicarea unui vector cu o constant (scalare)

Aceste operaii se pot implementa prin suprancrcarea corespunztoare a operatorilor. Pentru


nceput se vor defini funciile operator+() i operator() pentru calculul sumei,
respectiv a diferenei a doi vectori.
class Point{
float x;
float y;
public:
Point(){
x = 0;
y = 0;
}
Point(double a, double b){
x = a;
y = b;
}
void Display() {
cout << x << " " << y << endl;
}
Point operator+(Point op2); // suma a doi vectori
Point operator-(Point op2); // diferena a doi vect
double operator*(Point op2);// produs scalar
Point& operator*(double v); // multipl. cu o const.
};
Point Point::operator+(Point op2){
point temp;
temp.x = x + op2.x;
temp.y = y + op2.y;
return temp;
}
Point Point::operator-(Point op2){
point temp;
temp.x = x + op2.x;
temp.y = y + op2.y;
return temp;
}
double Point::operator*(Point op2){
return x*op2.y + y*op2.x;
}

65

Point& Point::operator*(double
x *=v;
y *=v;
return *this;
}
void f1(){
Punct pct1(10,20);
Punct pct2(30,40);
Punct pct3;
pct1.Display();
pct2.Display();
pct3 = pct1 + pct2;
pct3.Display();
pct3 = pct2 pct1;
pct3.Display();
}

v){

// afiseaza 10 20
// afiseaza 30 40
// afiseaza 40 60
// afiseaza 20 20

Funcia operator+() are un singur argument, chiar dac ea suprancarc un operator binar
pentru ca argumentul transmis funciei este operandul din dreapta operaiei, iar operandul din
stnga este chiar obiectul pentru care se apeleaz funcia operator.
Pentru acelai operator se pot defini mai multe funcii suprancrcate, cu condiia ca selecia
uneia dintre ele n funcie de numrul i tipul argumentelor s nu fie ambigu. n clasa Point s-a
suprancrcat operatorul * cu dou funcii: prima pentru calculul produsului scalar a doi vectori,
cealalt pentru multiplicarea vectorului cu o constant.
n implementarea prezentat, funcia operator+() creaz un obiect temporar, care este distrus
dup returnare. n acest fel, ea nu modific nici unul dintre operanzi, aa cum nici operatorul +
pentru tipurile predefinite nu modific operanzii.
n general, un operator binar poate fi suprancrcat fie printr-o funcie membr nestatic cu un
argument, fie printr-o funcie nemembr cu dou argumente.
Un operator unar poate fi suprancrcat fie printr-o funcie membr nestatic fr nici un
argument, fie printr-o funcie nemembr cu un argument. La suprancrcarea operatorilor de
incrementare sau decrementare (++, --) se poate diferenia un operator prefix de un operator
postfix folosind dou versiuni ale funciei operator. n continuare sunt prezentate cteva funcii
operator ale clasei Point pentru operatori unari.
class Point{
//
public:
Point operator!();
Point operator++();
Point operator();
Point operator++(int x);
Point operator(int x);
};

66

Point operator!(){
x = -x;
y = -y;
return *this;
}
Point Point::operator++(){
x++;
y++;
return *this;
}
Point Point::operator--(){
x--;
y--;
return *this;
}
Point Point::operator ++(int x){
++x;
++y;
return *this;
}
Point Point::operator --(int x){
--x;
--y;
return *this;
}

Dac ++ precede operandul, este apelat funcia operator++(); dac ++ urmeaz


operandului, atunci este apelat funcia operator++(int x), iar x are valoarea 0.
La suprancrcarea unui operator folosind o funcie care nu este membr a clasei este necesar s
fie transmii toi operanzii necesari, deoarece nu mai exist un obiect al crui pointer (this) s
fie transferat implicit funciei. Din aceast cauz, funciile operator binar necesit dou
argumente de tip clas sau referin la clas, iar funciile operator unar necesit un argument de
tip clas sau referin la clas. n cazul operatorilor binari, primul argument transmis este
operandul stnga, iar al doilea argument este operandul dreapta
In programul P4.2 o parte din funciile operator ale clasei Point sunt implementate ca funcii
friend ale clasei.
class Point
{
int x;
int y;
public:
//.
friend Point operator+(Point op1, Point op2);
friend Point operator-(Point op1, Point op2);

67

Point operator+(Point op1, Point op2){


Point temp;
temp.x = op1.x + op2.x;
temp.y = op1.y + op2.y;
return temp;
}
Point operator-(Point op1, Point op2){
Point temp;
temp.x = op1.x - op2.x;
temp.y = op1.y - op2.y;
return temp;
}
void f2(){
Punct pct1(10,20);
Punct pct2(30,40);
Punct pct3;
pct1.Display();
pct2.Display();
pct3 = pct1 + pct2;
pct3.Display();
pct3 = pct2 pct1;

// afiseaza 10 20
// afiseaza 30 40
// afiseaza 40 60

Observatii
Dac functia operator este implementat ca o functie membru, operandul cel mai din
stnga (eventual, unicul operand) trebuie s fie un obiect al clasei sau o referint ctre un
obiect al clasei. Implementarea sub form de functie nemembru este indicat n cazul n
care cel mai din stnga operand este un obiect al unei clase diferite sau al unui tip
predefinit.
O functie operator ne-membru trebuie declarat functie friend dac functia respectiv
trebuie s acceseze direct membrii privati sau protejati ai clasei respective.

4. 2 Template-uri
O alt facilitate important a limbajului C++ este dat de posibilitatea definirii unor sabloane
numite template-uri. Un template reprezint o modalitate de parametrizare a unei clase sau a unei
functii prin utilizarea unui tip n acelasi mod n care parametrii unei functii furnizeaz o
modalitate de a defini un algoritm abstract fr identificarea valorilor specifice.
O clas template specific modul n care pot fi construite clase individuale, difereniate prin tipul
sau tipurile de date asupra crore se opereaz.
Sintaxa definirii unui template:
template <class <parametru> > class <nume_clas>
68

{
// definitia clasei sablon
};
Programul P4.3 prezint un exemplu de definire a unei functii template, afisare_vector(.).
Aplicarea functiei template celor trei variabile T1, T2 si respectiv T3 va determina afisarea unui
vector de numere ntregi, a unui vector de numere reale, respectiv a unui vector de caractere.

// fisierul sursa P4.3.cpp


#include <iostream.h>
template <class T>
void afisare_vector(const T *t, const int nr)
{
for (int i=0;i<nr;i++)
cout<<t[i]<<" ";
}
void main()
{
const int nr1 = 5, nr2 = 7, nr3 = 4;
int T1[nr1] = {1,2,3,4,5};
double T2[nr2] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7};
char T3[nr3] = "Ana";
cout<<"\n Vectorul contine: ";
// functia template pentru vector de numere intregi
afisare_vector(T1, nr1);
cout<<"\n Vectorul contine: ";
// functia template pentru vector de numere reale
afisare_vector(T2, nr2);
cout<<"\n Vectorul contine: ";
// functia template pentru vector de caractere
afisare_vector(T3, nr3);
}
Observatie
Template-urile furnizeaz o alt form de polimorfism.

4.3 Stream-uri de I/E


Bibliotecile standard ale limbajului C++ furnizeaz un set extins de facilitti pentru operatii de
intrare/iesire (I/E). Dac o functie de I/E este definit corespunztor unui anumit tip de date, ea
va putea fi apelat pentru a lucra cu acel tip de date. Se pot specifica att tipuri standard, ct si
tipuri definite de utilizatori.
Funcii de I/O pentru tipurile predefinite
Operaiile de I/O din C++ se efectueaz folosind funciile operator de inserie << i operator de
extragere >>.

69

Funciile de operare asupra streamurilor specific modul n care se execut conversia ntre un ir
de caractere din stream i o variabil de un anumit tip. Aceste funcii operator sunt definite n
clasa ostream, respectiv istream, pentru toate tipurile predefinite ale limbajului, iar pentru
tipurile definite de utilizator ele pot fi suprancrcate.
Funcia de citire de la consol a unei secvene de numere ntregi separate prin spaii albe
(whitespace adic unul din caracterele blanc, tab, newline, carriage return, formfeed) poate arta
asfel:
void main(){
int size = 10;
int array[10];
for(int i=0;i<size;i++){
if (cin >> array[i])
cout << array[i] << " ";
else {
cout << "eroare non-int";
break;
}
}
}

O intrare diferit de ntreg va cauza eroare n operaia de citire i deci oprirea buclei for. De
exemplu, din intrarea:
1 2 3 4.7 5 6 7 8 9 0 11
se va citi primele patru numere, dup care apare eroare n operaia de intrare, citirea numerelor
ntregi se ntrerupe i pe ecran apare mesajul:
1 2 3 4 eroare non-int
Caracterul punct este lsat n streamul de intrare, ca urmtor caracter de citit.
O alt soluie pentru citirea unei secvene de intrare este prin folosirea uneia din funciile get()
definite n clasa iostream astfel:
class iostream : public virtual ios {
//
istream& get(char& c);
istream& get(char* p, int n, char ch=\n);
};

Aceste funcii treateaz spaiile albe la fel ca pe toate celelalte caractere. Funcia get(char&
c) citete un singur caracter n argumentul c. De exemplu, o funcie fg() de copiere caracter cu
caracter de la intrare (streamul cin) la ieire (streamul cout) poate arta astfel:
void stream(){
char c;
while(cin.get(c)) cout << c;

70

i funcia operator >> () are un echivalent ca funcie de scriere la consol fr


suprancrcarea operatorului >>, numit funcia put(), astfel nct funcia fg() se poate
rescrie astfel:
void stream(){
char c;
while(cin.get(c)) cout.put(c);
}

Funcii de I/O pentru tipuri definite de utilizator


Funciile de I/O pentru tipuri definite de utilizator se obin prin suprancrcarea operatorilor de
inserie i de extragere, care au urmtoarea form general:
ostream& operator<<(ostream& os,tip_clasa nume){
// corpul functiei
return os;
}
istream& operator<<(istream& is,tip_clasa& nume){
// corpul functiei
return is;
}

Primul argument al funciei este o referin la streamul de ieire, respectiv de intrare. Pentru
funcia operator de extragere << al doilea argument este dat printr-o referin la obiectul care
trebuie s fie extras din stream; n aceast referin sunt nscrise datele extrase din streamul de
intrare. Pentru funcia operator de inserie >> al doilea argument este dat prin tipul i numele
obiectului care trebuie s fie inserat, sau printr-o referin la acesta. Funciile operator de inserie
i extracie returneaz o referin la streamul pentru care au fost apelate, astfel nct o alt
operaie de I/O poate fi adugat acestuia.
Funciile operator << sau >> nu sunt membre ale clasei pentru care au fost definite, dar pot (i
este recomandabil) s fie declarate funcii friend n clasa respectiv.
In programul P 4.4 se defineste clasa Complex care cuprinde functiile operator << si >>.
class Complex {
double x, y;
public:
Complex(){x = 0; y = 0}
Complex(double r, double i){
x = r;
y = i;
}
..
friend ostrem& operator << (ostrem& os,Complex z);

71

friend istream& operator >>(istream& is,Complex& z);


};
ostream& operator<<(ostream& os, Complex& z){
os << ( << z.x << ,<< z.y << );
return os;
}
istream& operator>>(istream& is, Complex z){
is >> z.x >> z.y;
return is;
}
void main(){
Complex z;
cout << "Introduceti x, y :";
cin >> z;
cout << "z = " << z << '\n';
cin.get();
int size = 10;

Execuia acestei funcii produce urmtoarele mesaje la consol:


Introduceti x, y: 1.3 4.5
z = (1.3,4.5)
Introduceti un sir:123456
123456

4. 4 Procesarea fisierelor
Procesarea fisierelor n limbajul C++ necesit includerea fisierelor antet <iostream.h> si
<fstream.h>. Fisierul antet <fstream.h> cuprinde definitiile claselor streamului: ifstream (pentru
intrrile dintr-un fisier operatii de citire din fisier), ofstream (pentru iesirile ctre un fisier
operatii de scriere n fisier) si fstream (pentru intrrile/iesirile de la/ctre un fisier).
Pentru utilizarea unui fiier pe disc acesta trebuie s fie asociat unui stream. Pentru aceasta se
creaz mai nti un stream, iar apelul funciei open() a streamului execut asocierea acestuia cu
un fiier ale crui caracteristici se transmit ca argumente ale funciei open(). Funcia open() este
funcie membr a fiecreia dintre cele trei clase stream (ifstream, ofstream i fstream)
Implicit, fiierele se deschid n mod text. Valoarea ios::binary determin deschiderea n mod
binar a fiierului.
Pentru nchiderea unui fiier se apeleaz funcia close(), care funcie membr a claselor stream
(ifstream, ofstream i fstream).
Pentru scrierea i citirea dintr-un fiier de tip text se folosesc funciile operator << i >> ale
streamului asociat acelui fiier. Aceste funcii operator suprancrcate pentru un anumit tip de
date pot fi folosite fr nici o modificare att pentru a scrie sau citi de la consol ct i pentru a
scrie sau citi dintr-un fiier pe disc.
72

Programul P4.5 creaz un fiier de inventariere inventar.txt care conine numele


articolului, preul unitar, numrul de buci i valoarea total.
void finv(){
ofstream output("inventar.txt");
if (!output)
cout << "Nu se poate deschide
fisierul inventar.txt\n";
char buffer[80];
double pret,total;
int buc;
while(1){
cout << "\nArticol: ";
cin.get(buffer,80);
if (buffer[0] == 0)
break;
cout << "Pret unitar: ";
cin >> pret;
cout << "Nr. bucati: ";
cin >> buc; cin.get();
total = buc*pret;
output << buffer << endl;
output << pret<< endl;
output << buc << endl;
output << total << endl;
}
output.close();
}

La citirea dintr-un fiier de tip text de pe disk folosind operatorul >> apar, la fel ca la citirea
de la consol, anumite modificri de caractere. Pentru a evita astfel de modificri se folosesc
funciile de I/O binare care vor fi prezentate n seciunea urmtoare.

73

Teste de autocontrol
4.1 Care este rolul redefinirii operatorilor n limbajul C++?
4.2 Care dintre afirmatiile urmtoare sunt adevrate?
(a) Precedenta unui operator poate fi modificat prin redefinire.
(b) Aritatea unui operator nu poate fi modificat prin redefinire.
(c) Asociativitatea unui operator poate fi modificata prin redefinire.
(d) Semnificatia modului n care lucreaz un operator asupra obiectelor de tipuri
predefinite nu poate fi schimbat prin redefinire.
4.3 Care sunt clasele predefinite, dedicate procesrii fisierelor C++?
4.4 S se citeasc un fiier care conine numere flotante, s se construiasc numere complexe din
perechi de cte dou numere flotante i s se afieze la consol numerele complexe.
4.4 Pentru clasa String s se defineasc urmtoarele operaii de comparaie folosind funcii
operator friend:
int operator ==( const String& s1, const String& s2 );
int operator ==( const String& s1, const char* s2 );
int operator ==( const char* s1, const String& s2 );
int operator !=( const String& s1, const String& s2 );
4.5 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ
spunei de ce nu este corect. #include<iostream.h>
class B
{ protected: int x;
B(int i=10) { x=i; }
public: virtual B operator+(B ob) { B y(x+ob.x);
return y;} };
class D: public B
{ public: D(int i=10) { x=i; }
void operator=(B p) { x=p.x; }
B operator+(B ob) { B y(x+ob.x+1);
return y; }
void afisare(){ cout<<x; } };
int main()
{ D p1(-59),p2(32),*p3=new D;
*p3=p1+p2;
p3->afisare();
return 0;
}

74

4.6 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ
spunei de ce nu este corect. #include<iostream.h>
class cls
{ public: int sa;
cls(int s=0) { sa=s; }
operator int() { return sa; }
int f(int c) { return (sa*(1+c/100)); } };
int main()
{ cls p(37);
cout<<p.f(p);
return 0;
}
4.7 Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ
spunei de ce nu este corect. #include<iostream.h>
class B
{ public: int x;
B(int i=0) { x=i; }
virtual B aduna(B ob) { return(x+ob.x); }
B minus() { return(1-x); }
void afisare(){ cout<<x; } };
class D: public B
{ public: D(int i=0) { x=i; }
B aduna(B ob) { return(x+ob.x+1); } };
int main()
{ B *p1, *p2;
p1=new D(138);
p2=new B(-37);
*p2=p2->aduna(*p1);
*p1=p2->minus();
p1->afisare();
return 0;
}

75

Teste recapitulative:
Specificai varianta corect :
1. Clasa
class c { float a;
void afisisare();
}

are membrii:
a). publici;
c). protected;

b). privai
d). date private i metode pubice.

2. Fie secvena:
class c {....};
void main()
{ c e
/* instructiuni */
}

n acest caz:
a). c este un obiect i e este clas;
b). c este o instana a clasei i e este un obiect;
c). c este o clasa i e este un obiect;
d). descrierea este eronat deoarece se folosete acelai identificator pentru clas i obiect.
3. Avnd declaraia:
class persoana {
char nume[20];
int varsta;
public:
persoana();
int spune_varsta() { return varsta;}
};
void main()
{persoana p;
cout<<p.nume<<p.varsta;
}

n acest caz :
a). programul afiseaz numele i vrsta unei persoane;
b). nu este permis afisarea datelor unei persoane;
c). descriere eronat pentru c funcia membra nu este utilizat;
d). afiseaz doar vrsta unei persoane.
4. Fie clasa :
class c { int a,b;
public:
float c (int, int)
int det_a {return a;}
c (); }

Declaraia float c(int, int) ar putea corespunde unui constructor al clasei?


76

a). da, fiind o supraincarcare a celui existent;


b). nu., deoarece creaz ambiguitate;
c). nu, deoarece constructorul nu are tip returnat;
d). nu, deoarece nu este de tip friend.
5. Fie declaraia :
class c1 {int a ;}
class c2 :public c1
{ public :
int b ;
void scrie_a() {cout<<a ;}
}
void main ()
{ c2 ob ;
ob.scie_a () ;
}

Selectai afirmaia corect :


a). funcia scrie_a() nu are drept de acces asupra unui membru privat;
b). programul afiseaz valoare lui a;
c). derivarea public este incorect relaizat;
d). prin derivare public, accesul la membrii moteniti devine public.
6. Fie declaraia
class c1{/* instructiuni */}
class c2:public c1 {/* .*/}

Atunci clasa c2 faa de clasa c1 este:


a). derivat; b). de baz; c). friend

d). virtual.

7. In secvena urmatoare:
class cls { public:static int s;};
int cls::s=0;
int main(){int i=7;cls::s=i;cout<<cls::s;}

Utilizarea lui s este:


a). ilegal, deoarece nu exist nici un obiect creat;
b). ilegal, deoarece variabilele statice pot fi doar private;
c). ilegal, deoarece s este dublu definit, n clas i n afara ei;
d). corect, deoarece membrii statici exist nainte de a se crea obiecte din clasa.
8. Secvena urmatoare:
class persoana
{ int varsta, salariul;
friend ostream & operator<<(ostream &out,persoana p)
{out<<p.varsta<< <<p.salariul; return out;}
public:
persoana(int v){varsta=v;salariul=0;}
persoana(){varsta=0;salariul=0;}
}
int main()
{persoana p(1);cout<<p;}

Afiseaz:
a). afiseaz 1 0;

b). afiseaz 0 0

77

c). afiseaz 1 1;

d). afiseaz 0 1.

9. Secvena urmatoare:
class vector{int*pe,nr_c;
public:
operator int(){return nr_c;}
vector(int n){
pe=new int[n];nr_c=n;
while(n--) pe[n]=n;}
void f(int i){cout<<i<<endl;}
int main()
{vector x(10); f(x)}
Afiseaz:

a). 10 b). 9
c). numerele de la 1 la 10
d). numerele de la 0 la
10. Secventa urmatoare:
class vector{int*pe,nr_c;
public:
operator int(){return nr_c;}
vector(int n){
pe=new int[n];nr_c=n;
while(n--) pe[n]=n;}
void f(int i){cout<<i<<endl;}
int main()
{vector x(10); f(x)}

Afiseaza:
a). 10
b). 9
c). numerele de la 1 la 10
d). numerele de la 0 la 9
11. Secvena urmatoare afiseaz:
class persoana
{int varsta;
public:
persoana(int v=18){varsta=18;}
operator int(){return varsta;}
persoana& operator++()
{varsta++;return *this;}
persoana operator ++(int)
{persoana aux=*this;varsta++;return aux;}
int main(){
persoana p(20);
int x=p++,y=++p;
cout<< x<< y ;}

Afiseaza :
a).20 20;
b).20 21 ;
c).21 22;
d).20 22.
78

12 . Fie secvena :
class c { int a;
public: c();
c(const c&);
void operator=(c&);}
int main()
{ c a;
//instructiuni;
c b=a;}

Linia c b=a; determin:


a). execuia constructorului de copiere;
b). execuia metodei prin care se supraincarc operatorul =;
c). execuia att a constructorului de copiere, ct i a metodei operator =;
d). execuia contructorului implicit
13. Se considera urmatoarea secventa de program:
class complex
{
double real;
double imag;
public:
complex(double x=1.0, double y=20.0){real=x; imag=y;}
complex( const complex &u)
{
real=u.real;
imag=u.imag;
}
..............
}

Precizati n care situatie se creaza un obiect anonim:


a) complex z1(3.42, -12.9);
b) complex z2=z1;
c) complex z3(z1);
d) complex z1= complex(45.0,0.9);
e) complex z(23.25);
14. Se considera urmatoarea secventa de program:
class complex
{
double real;
double imag;
public:
complex(double x=10.0, double y=10.0){real=x; imag=y;}
complex(const complex &u)
{
real=u.real;
imag=u.imag;
}
..............
}

Precizati n care situatie se realizeaza o copiere a unui obiect n alt obiect:


79

a) complex z1(3.42, -12.9);


b) complex z2=z1;
c) complex z3(1.0,-1.0);
d) complex z(10.7,0.8);
e) complex z(23.25);
15. Se considera urmatoarea secventa de program:
class complex
{
double real;
double imag;
public:
complex(double x=-11.0, double y=-56.90){real=x; imag=y;}
complex( const complex &u)
{
real=u.real;
imag=u.imag;
}
..............
}

Precizai in care situatie se creaza un obiect anonim:


a) complex z1(3.42, -12.9);
b) complex z1= complex(0.0,0.9);
c) complex z2=z1;
d) complex z3(z1);
e) complex z(23.25);
16. Se considera urmatoarea secventa de program:
class complex
{
double re;
double im;
public:
complex(double x=-11.0, double y=-56.90){re=x; im=y;}
complex( const complex &u)
{
real=u.re;
imag=u.im;
}
............
}

Precizati n situatie se utilizeaza constructorul de copiere:


a) complex z1(3.4, -12.9);
b) complex z3(0.0,-10.9);
c) complex z2(0.0,1.0);
d) complex z3(z1);
e) complex z(2.25);
17. Se considera urmatoarea secventa de program:
class complex
{
double real;

80

double imag;
public:
complex(double x=-11.0, double y=-56.90){real=x; imag=y;}
complex( const complex &u)
{
real=u.real;
imag=u.imag;
}
..............

}
Precizati situatia n care nu era necesara folosirea unui constructor cu parametri care iau valori n
mod implicit:
a) complex z2(3.42,-12.9);
b) complex z3(z2);
c) complex z=z2;
d) complex z4(z);
e) complex z5=z4;
18. Se da secventa de program:
class A
{
int a[3];
public:
A(int i, int j, int k){a[0]=i; a[1]=j; a[2]=k;}
int &operator[](int i){return a[i];}
};
void main(void)
{
A ob(1,2,3);
cout << ob[1];
ob[1]=25;
cout<<ob[1];
}

Ce se poate afirma despre operator[]()?


a) produce suprancarcarea unei functii;
b) produce suprancarcarea unui operator unar;
c) suprancarca operatorul [];
d) este o functie membru oarecare a clasei A care nu produce suprancarcarea unui operator;
e) reprezinta un operator ternar;
19. Suprancarcarea unor operatori se poate realiza prin functii operator sau functii friend.
Diferenta ntre aceste doua posibilitati consta n:
a) lista de parametri;
b) obiect returnat;
c) precedent a operatorilor;
d) n-aritatea operatorului;
e) alte situatii.
20. In secventa de program:
.................
int k=100;

81

void fct()
{
int k;
...........
k++;
...........
}
void gct()
{
int k=2;
...........
::k++; // (?)
...........
}

In instructiunea marcata cu (?), k este o variabila:


a) externa;
b) statica;
c) registru;
d) globala;
e) automatica;
21. Se considera secventa de program:
class B1 { class D1:public B1,public B2 {
public: public:
B1(){cout<<"(1)\n";} D1(){cout<<"(7)\n";}
~B1(){cout<<"(2)\n";} ~D1(){cout<<"(8)\n";}
}; };
class B2 { class D2:public D1,public B3 {
public: public:
B2(){cout<<"(3)\n";} D2(){cout<<"(9)\n";}
~B2(){cout<<"(4)\n";} ~D2(){cout<<"(10)\n";}
}; };
class B3 {
public:
B3(){cout<<"(5)\n";}
~B3(){cout<<"(6)\n";}
};
void main(){
D1 ob1;
D2 ob2;
}

Care mesaj se va scrie?


a) (1),(3),(7),(3),(5),(7),(9),(10),(6),(8),(4),(2),(2),(3),(2),(2);
b) (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(9),(7),(2),(3),(2),(2);
c) (1),(3),(7),(1),(3),(7),(5),(9),(10),(6),(8),(4),(2),(8),(4),(2);
d) (1),(3),(5),(7),(9),(2),(4),(6),(6),(8),(8),(10),(2),(2),(4),(2);
e) (1),(3),(7),(1),(3),(7),(9),(5),(10),(6),(4),(8),(2),(8),(4),(2);
22. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ
spunei de ce nu este corect. #include <iostream.h>
template <class tip>
class cls
{ tip z;

82

public: cls(tip i) { z=i; }


tip operator-(cls); };
template <class tip>
tip cls<tip>::operator-(cls<tip> a)
{ return z-a.z;
}
template <class tip>
tip dif(tip x, tip y)
{ return x-y;
}
int dif(int x, int y)
{ return x>=y?x-y:y-x;
}
int main()
{ cls<int> i=3; cls<float> j=4;
cout<<dif(i,j);
return 0;
}

23. Descriei pe scurt cum putei prelua o dat prin incluziune i a doua oar prin motenire o clas
numar ntr-o clas lista care descrie liste nevide de dimensiune variabil de elemente de
tip numar.
24. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ
spunei de ce nu este corect.
#include<iostream.h>
class cls
{ static int x;
public: cls(int i=25) { x=i; }
friend int& f(cls); };
int cls::x=-13;
int& f(cls c) { return c.x; }
int main()
{ cls d(15);
cout<<f(d);
return 0;
}

25. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ
spunei de ce nu este corect.
#include<iostream.h>
class cls
{ int v,nr;
public: cls(int i) { nr=i; v=new int[i]; }
friend int& operator[](int);
friend ostream& operator<<(ostream&,cls); };
int& operator[](cls& x, int i)
{ return x.v[i]; }
ostream& operator<<(ostream& o, cls x)
{ for(int i=0;i<x.nr;i++) cout<<x.v[i]<< ; return o; }
int main()
{ cls x(10);

83

x[5]=7;
cout<<x;
return 0;
}

26. Descriei pe scurt metoda de identificare a tipului n timpul rulrii (RTTI).


27. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz negativ
spunei de ce nu este corect.
#include<iostream.h>
class cls
{ static int i;
int j;
public: cls(int x=7) { j=x; }
static int imp(int k){ cls a; return i+k+a.j; } };
int cls::i;
int main()
{ int k=5;
cout<<cls::imp(k);
return 0;
}

28. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz
negativ spunei de ce nu este corect. #include<iostream.h>
class cls
{ int x;
public: cls(int i=32) { x=i; }
int f() const; };
int cls::f() const { return x++; }
void main()
{ const cls d(-15);
cout<<d.f();
}

29. Spunei dac o variabil constant poate fi transmis ca parametru al unei funcii i dac da, n ce
situaii. Justificai.
30. Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz pentru o
valoare ntreag citit egal cu 7, n caz negativ spunei de ce nu este corect.
#include <iostream.h>
float f(float f)
{ if (f) throw f;
return f/2;
}
int main()
{ int x;
try
{
cout<<Da-mi un numar intreg: ;
cin>>x;
if (x) f(x);
else throw x;

84

cout<<Numarul <<x<< e bun!<<endl;


}
catch (int i)
{ cout<<Numarul <<i<< nu e bun!<<endl;
}
return 0;
}

31. Spuneti dac programul de mai jos este corect. n caz afirmativ, spunei ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include<iostream.h>
class B
{ int x;
public: B(int i=2):x(i){}
int get_x() const { return x; } };
class D: public B
{ int *y;
public: D(int i=2):B(i){ y=new int[i];
for(int j=0; j<i; j++) y[j]=1; }
D(D& a){ y=new int[a.get_x()];
for(int i=0;i<a.get_x();i++) y[i]=a[i]; }
int& operator[](int i) { return y[i]; } };
ostream& operator<<(ostream& o, const D& a)
{ for(int i=0;i<a.get_x();i++) o<<a[i];
return o;
}
int main()
{ D ob(5);
cout<<ob;
return 0;
}

32. Descrieti trei metode de proiectare diferite prin care elementele unei clase se pot regsi n
dublu exemplar, sub diverse forme, n definitia altei clase.
33. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spunei de ce nu este corect.
#include<iostream.h>
class B
{ int x;
public: B(int i=10) { x=i; }
int get_x() { return x; } };
class D: public B
{ public: D(int i):B(i) {}
D operator+(const D& a) {return x+a.x; } };
int main()
{ D ob1(7), ob2(-12);
cout<<(ob1+ob2).get_x();
return 0;
}

34. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include<iostream.h>
class B
{ public: int x;
B(int i=16) { x=i; }

85

B f(B ob) { return x+ob.x; } };


class D: public B
{ public: D(int i=25) { x=i; }
B f(B ob) { return x+ob.x+1; }
void afisare(){ cout<<x; } };
int main()
{ B *p1=new D, *p2=new B, *p3=new B(p1->f(*p2));
cout<<p3->x;
return 0;
}

35. Spuneti ce este obiectul implicit al unei metode si descrieti pe scurt propriettile pe care le
cunoasteti despre acesta.
36. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include<iostream.h>
class cls
{ int *v,nr;
public: cls(int i) { nr=i; v=new int[i];
for (int j=1; j<nr; j++) v[j]=0; }
int size() { return nr; }
int& operator[](int i) { return *(v+i); } };
int main()
{ cls x(10);
x[4]=-15;
for (int i=0; i<x.size(); i++) cout<<x[i];
return 0;
}

37. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include<iostream.h>
class cls
{ int x;
public: cls(int i=-20) { x=i; }
const int& f(){ return x; } };
int main()
{ cls a(14);
int b=a.f()++;
cout<<b;
return 0;
}

38. Descriei pe scurt mostenirea virtual si scopul n care este folosit.


39. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include<iostream.h>
class B
{ static int x;
int i;
public: B() { x++; i=1; }
~B() { x--; }

86

static int get_x() { return x; }


int get_i() { return i; }
};
int B::x;
class D: public B
{ public: D() { x++; }
~D() { x--; }
};
int f(B *q)
{ return (q->get_i())+1;
}
int main()
{ B *p=new B;
cout<<f(p);
delete p;
p=new D;
cout<<f(p);
delete p;
cout<<D::get_x();
return 0;
}

40. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include<iostream.h>
class B
{ int x;
public: B(int i=17) { x=i; }
int get_x() { return x; }
operator int() { return x; } };
class D: public B
{ public: D(int i=-16):B(i) {} };
int main()
{ D a;
cout<<27+a;
return 0;
}

41. Enumerai 3 metode de implementare a polimorfismului de compilare.


42. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include<iostream.h>
class cls
{ static int x;
public: cls (int i=1) { x=i; }
cls f(cls a) { return x+a.x; }
static int g() { return f()/2; } };
int cls::x=7;
int main()
{ cls ob;
cout<<cls::g();
return 0;
}

87

43. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include<iostream.h>
class cls
{ int *v,nr;
public: cls(int i=0) { nr=i; v=new int[i];
for (int j=0; j<size(); j++) v[j]=3*j; }
~cls() { delete[] v; }
int size() { return nr; }
int& operator[](int i) { return v[i]; }
cls operator+(cls); };
cls cls::operator+(cls y)
{ cls x(size());
for (int i=0; i<size(); i++) x[i]=v[i]+y[i];
return x; }
int main()
{ cls x(10), y=x, z;
x[3]=y[6]=-15;
z=x+y;
for (int i=0; i<x.size(); i++) cout<<z[i];
return 0;
}

44. Descrieti pe scurt mecanismul de tratare a exceptiilor.


45. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include<iostream.h>
class B
{ int i;
public: B() { i=1; }
int get_i() { return i; }
};
class D: public B
{ int j;
public: D() { j=2; }
int get_j() {return j; }
};
int main()
{ B *p;
int x=0;
if (x) p=new B;
else p=new D;
if (typeid(p).name()=="D*") cout<<((D*)p)->get_j();
return 0;
}

46. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include <iostream.h>
class cls
{ int x;

88

public: cls(int i) { x=i; }


int set_x(int i) { int y=x; x=i; return y; }
int get_x(){ return x; } };
int main()
{ cls *p=new cls[10];
int i=0;
for(;i<10;i++) p[i].set_x(i);
for(i=0;i<10;i++) cout<<p[i].get_x(i);
return 0;
}

47. Descrieti pe scurt diferenta dintre un pointer si o referint.


48. Spuneti dac programul de mai jos este corect. n caz afirmativ, spuneti ce afiseaz, n caz
negativ spuneti de ce nu este corect.
#include <iostream.h>
template<class T>
int f(T x, T y)
{ return x+y;
}
int f(int x, int y)
{ return x-y;
}
int main()
{ int a=5;
float b=8.6;
cout<<f(a,b);
return 0;
}

49. Spunei pe scurt prin ce se caracterizeaz un cmp static al unei clase.


50 . Spunei dac programul de mai jos este corect. n caz afirmativ, spunei ce afieaz, n caz
negativ spunei de ce nu este corect. #include<iostream.h>
class cls1
{ public: int
cls1() { a=7;
class cls2
{ public: int
cls2(int i) {
cls2(cls1& x)
int main()
{ cls1 x;
cout<<x.a;
cls2 y(x);
cout<<y.b;
return 0;
}

a;
} };
b;
b=i; }
{ b=x.a; } };

89

Bibliografie
1. Oprea M., Programare orientat pe obiecte - Exemple n limbajul C++, Editura Matrix Rom.
2. Dr. Kris Jamasa, Totul despre C si C++, Editura Teora.
3. Ion Smeureanu , Programare orientat pe obiecte in Limbajul C++, Editura CISON,
Bucuresti 2005.
4.Liviu Negrescu, Limbajul C++, Editura ALBASTRA , Cluj 2000.
5.Luminita Duta, Programarea calculatoarelor in limbajul C++ , Editura Cetatea de Scaun
2006.

90

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