Sunteți pe pagina 1din 88

Programare orientat pe obiecte

(suport de curs)
1. Principiile programrii orientate pe obiecte.
Strategia procedural i structurat de scriere a programelor presupune existena unui program
principal i a unui ir de proceduri (subprograme, funcii), iar rezultatul final este obinut prin apelarea
procedurilor respective transmindu-le lor datele necesare:

Programarea orientat pe obiecte (POO) este tehnologia de programare n care programul este
reprezentat ca o colecie de obiecte discrete, care conin nite seturi de structuri de date i proceduri ce
interacioneaz cu alte obiecte:

Exist mai multe sensuri ale cuvntului obiect:


corp solid, cunoscut direct cu ajutorul simului;

lucru sau complex de lucruri aprute ca rezultat al procesului de munc (de exemplu,
obiect de consum, obiect de uz casnic);

materie asupra creia este orientat activitatea spiritual sau artistic (de exemplu, obiect
de cercetare, obiect de descriere);

admiraie);

fiin sau lucru pentru care cineva manifest un sentiment (de exemplu, obiect de
disciplin de studiu ntr-o instituie de nvmnt;

filos. Corp sau fenomen existent n realitate, n afara subiectului i independent de contiina acestuia;
lingv. Parte de propoziie care indic asupra cui este orientat aciunea verbului; complement.
Aceste definiii cuprind diferite domenii ce in de natur i de activitatea omului. Ele toate pot fi
modelate n POO prin declararea claselor i crearea obiectelor respective.
1

Pentru a aplica tehnologia orientat pe obiecte, iniial, este necesar de a realiza o etap important abstractizarea. Modelul fizic al oricrui domeniu de problem este unul complex, depinznd de muli
parametri i relaii. Astfel, se face o simplificare a modelului fizic, obinnd modelul logic al domeniului
de problem. Sunt omise din modelul fizic o serie de proprieti i legturi care nu sunt eseniale n
contextul problemei formulate. Acest proces de simplificare se numete abstractizare:

Modelul fizic
abstractizare
Modelul logic
Deci, abstractizarea presupune srcirea datelor iniiale.
Exemplu. La elaborarea unui program de eviden a cadrelor ntr-o ntreprindere, este necesar s
descriem obiectul Angajat al ntreprinderii care n esen este o persoan (o fiin uman). Orice
persoan angajat la ntreprindere poate fi descris (caracterizat) printr-un set de proprieti, cum ar fi de
exemplu:
-

nume;
prenume;
domiciliu;
anul naterii;
funcia;
salariu;
vechimea n munc;

nlime;
culoarea ochilor;
culoarea prului;
greutatea;
numrul de copii;
etc.

- De aici se observ c o serie de proprieti nu sunt adecvate problemei formulate, de exemplu,


nlime, culoarea ochilor, culoarea prului i greutatea, ele fiind eliminate din descriere.
- Pe lng proprieti un obiect poate fi caracterizat i printr-o serie de operaii (funcionalitate), de
exemplu:
- Schimbarea salariului;
- Schimbarea domiciliului;
- Schimbarea funciei;
- Creterea vechimii n munc;
- Schimbarea numelui;
- etc.
- Definiie. Obiectul n programare reprezint modulul de program care ndeplinete urmtoarele
cerine principale:
ntrunete date (caracterizeaz proprietile obiectului) i operaiile asupra lor (se mai numesc metode i
caracterizeaz comportarea obiectului sau posibilitile obiectului, adic ceea ce poate face obiectul);
posed proprietile de motenire, ncapsulare i polimorfism.
- La baza programrii orientate pe obiecte st noiunea de clas.
- O clas n programare ntrunete toate obiectele de una i aceeai natur (la fel ca i n lumea
real). Obiectele care aparin uneia i aceleiai clase posed una i aceeai structur, comportare i relaii
cu obiecte din alte clase.
- Definiie. Clasa este tipul abstract de date creat de utilizator (programator). n clas se descriu
mpreun cmpurile de date i metodele sub form de funcii membre. Metodele efectueaz operaii
asupra acestor date i alte aciuni ce caracterizeaz comportamentul obiectelor clasei respective. n baza
claselor deja create exist posibilitatea de a crea clase derivate care motenesc proprietile claselor de
baz.
- Exemplarul de obiect n POO se mai numete instan (engl. instance), este un obiect concret din
setul de obiecte ale uneia i aceleiai clase. Crearea exemplarului de obiect al unei clase se numete
instanierea obiectului.
- Cnd crem o clas, definim implicit un nou tip de date. Deci, un obiect este o variabil de un tip
definit de utilizator (tipul clas).
- Programarea orientat pe obiecte se bazeaz pe un ir de principii.
- Principiul 1. ncapsulare.
- ncapsularea n POO (engl. encapsulation) este unirea ntr-un singur tot a datelor i metodelor,
ceea ce permite ascunderea structurii interne de date i a metodelor obiectului de restul programului.
Celelalte obiecte din program au acces numai la interfaa obiectului. Interfaa este reprezentat prin
membrii publici prin care se efectueaz toate interaciunile cu acest obiect.
- ncapsularea este un mecanism care leag ntr-un tot ntreg codul i datele, pstrndu-le pe ambele
n siguran contra interveniilor din afar i de utilizri greite. n plus, ncapsularea asigur crearea
obiectelor. Un obiect este o entitate logic ce ncapsuleaz att date, ct i codul care manevreaz aceste
date. ntr-un obiect, o parte din cod i/sau date pot fi particulare (private sau protejate) acelui obiect i
inaccesibile pentru oricine din afara lui. Astfel, un obiect dispune de un nivel semnificativ de protecie
care mpiedic modificarea accidental sau utilizarea incorect a prilor obiectului de ctre seciuni ale
programului cu care acestea nu au legtur.
- Principiul 2. Motenire.

- Motenirea n POO (engl. inheritance) reprezint proprietatea obiectului, care const n aceea c
caracteristicile unui obiect (obiectul-printe) pot fi transmise unui alt obiect (obiect-fiu) fr descrierea lor
repetat. Motenirea simplific descrierea obiectelor i admite clasificarea lor.
- Exemplu. Clasa furnici face parte din clasa de insecte fr aripi, care la rndul su face parte din
clasa mai general insecte. O astfel de clasificare se numete taxonomie. Taxonomia este o tiin care se
ocup cu stabilirea legilor de clasificare i de sistematizare a domeniilor cu o structur complex.
Furnici
Greieri
Fluturi
Mute
- Fr utilizarea claselor cu proprietatea de motenire fiecare obiect ar trebui definit cu enumerarea
tuturor caracteristicilor sale. ns, prin folosirea clasificrilor, un obiect are nevoie doar de definirea
acelor caliti care l fac unic n clasa sa. Mecanismul motenirii permite ca un obiect s fie descris ca un
exemplar al unui caz mai general.
- Principiul 3. Polimorfism.
- n limbajele de programare orientate pe obiecte polimorfismul este caracterizat prin fraza o
singur interfa, metode multiple. Polimorfismul n POO (engl. polymorphism) este capacitatea
obiectului de a selecta, din mai multe metode metoda corect, n dependen de tipul de date, primite n
mesaj, sau, altfel spus, posibilitatea de a denumi la fel diferite aciuni la diferite niveluri ale ierarhiei de
clase. Polimorfismul simplific programarea, deoarece selectarea aciunii necesare se realizeaz automat.
- Dm un exemplu de polimorfism din matematic. Una i aceeai expresie a+b va fi interpretat n
conformitate cu tipul operanzilor operaiei de adunare. Dac a i b sunt dou numere, expresia dat
reprezint suma a dou numere, dac a i b sunt dou matrice rezultatul va fi suma matricelor, dac a i
b sunt doi vectori expresia reprezint vectorul rezultant etc.
- n limbajele de programare orientate pe obiecte se folosesc mai multe mecanisme prin care se
realizeaz principiul de polimorfism. Cele mai principale sunt suprascrierea i suprancrcarea funciilor
i operatorilor, posibilitatea de a specifica valorile implicite pentru parametri, regulile de atribuire ntre
obiecte ale claselor descendente, funciile virtuale.
- 2. Clase i obiecte n C++.
- n forma cea mai general o clas poate fi descris astfel:
-

class [nume_clasa]
{
//membrii clasei
};

- unde membrii clasei sunt de dou tipuri:


- - date membre;

- - funcii membre.
- Cu ajutorul datelor membre pot fi descrise atributele care caracterizeaz proprietile obiectelor
reale, modelate n cadrul programelor create. Funciile membre descriu funcionalitile claselor de
obiecte din domeniul de problem.
- Forma general de descriere a unei clase este urmtoarea:
-

class [nume_clasa] //identificator


{
[specificator_de_acces_a1:]
lista_membri_1;
specificator_de_acces_a2:
lista_membri_2;
- - - - - - - - - - specificator_de_acces_aN:
lista_membri_N;
} [lista_obiecte] ;
[descrierea_funciilor_membre_si_prietene]

- unde nume_clasa un identificator, numele clasei,


- lista_obiecte variabile de tip clas, sau obiecte ale clasei descrise.
- n aceast form de declarare a unei clase, componentele incluse n paranteze ptrate sunt
opionale (pot fi omise).
- Componentele din descriere specificator_de_acces_a1, specificator_de_acces_a2, ...,
specificator_de_acces_aN, se numesc specificatori de acces i indic gradul de acces la membrii
clasei. Fiecare membru al clasei se afl sub aciunea unui specificator de acces. Specificatorii de acces
sunt descrii prin cuvintele cheie private, protected i public (rom.: acces privat, protejat, public),
urmate de semnul :. Urmtorul tabel descrie gradul de acces permis de fiecare specificator:
-

private
protect
ed
public

Funcii
membre ale
clasei date

+
+

Func
ii
prie
tene

+
+

Funcii
membre ale
claselor
derivate

Func
ii
exter
ne

- Dup cum se vede, declaraia ncepe cu cuvntul-cheie class, dup care urmeaz denumirea
clasei. Denumirea clasei este orice identificator creat de ctre programator conform sintaxei limbajului
C++ i care este liber n momentul declarrii. Apoi, urmeaz corpul clasei luat n acolade { i }. Corpul
poate fi i vid. Corpul clasei poate conine declarri de cmpuri (date) membre, declarri de prototipuri
ale funciilor membre i ale funciilor prietene, definiri de funcii membre i de funcii prietene, declarri
de clase prietene. Declararea cmpurilor i a funciilor membre se repartizeaz pe seciuni n dependen
de regimul dorit de accesare a lor. Acest regim este stabilit prin utilizarea a trei specificatori de acces:
public, protected, private. Ordinea seciunilor, precum i numrul de repetri sunt arbitrare.
Dac la nceputul corpului de declarare a clasei, pentru un set de cmpuri i funcii membre specificatorul
de acces nu este indicat, implicit, pentru cmpurile i funciile membre din aceast seciune (prima
seciune) va fi stabilit regimul de accesare private (privat) cel mai dur regim de accesare din cele
trei. Cmpurile i funciile membre private sunt accesibile numai din interiorul acestei clase, adic acces
la ele au numai funciile membre ale sale, sau funciile de tip prieten (friend) al acestei clase. Regimul

de accesare protected (protejat) este puin mai liber n comparaie cu regimul private. El stabilete
c cmpurile i funciile membre ale clasei definite vor fi accesibile i din interiorul claselor derivate ale
clasei definite. Specificatorul de acces public este cel mai liber din cei trei. El admite accesarea
cmpurilor i a funciilor membre ale acestei seciuni din orice loc al programului unde va fi vizibil
obiectul concret al clasei definite. Aa o accesare este asigurat prin intermediul numelui acestui obiect.
- n C++ declararea claselor este similar cu declararea structurilor. n cazul structurilor se folosete
cuvntul-cheie struct n loc de class. O deosebire const n aceea c n cazul structurilor, implicit, va
fi stabilit regimul de accesare public i nu private, cum a fost descris mai sus pentru clase.
- Imediat dup descrierea corpului clasei (acolada de nchidere) putem crea un set de obiecte ale
acestei clase. ns, acest lucru nu este obligatoriu. Obiectele pot fi create i mai trziu n program.
- Dup caracterul ';' urmeaz definiiile (realizrile) funciilor membre, dac prototipurile lor au
fost declarate n corpul clasei.
- Exemplu 1. Declarm versiunea simplificat a clasei pentru reprezentarea fraciilor fr semn, cu
numrtor i numitor numere naturale (numere raionale pozitive). Vom numi aceast clas fractie:
-

#include <conio.h>
#include <iostream.h>
class fractie
{
protected:
unsigned int numarat;
unsigned int numit;
};
void main()
{
fractie fr1; // crem un obiect numit fr1, reprezentantul clasei
// fractie
fractie fr2; // mai crem un obiect numit fr2, reprezentantul aceleiai
// clase
fr1 = fr2;
// atribuim obiectului fr1 obiectul fr2
char c; cin >> c; // se ateapt apsarea oricrei taste
}

- De fapt, conform sintaxei, o versiune minimal a clasei fractie este:


- class fractie
-{
- };
- ns, fiind perfect valid din punctul de vedere al sintaxei, ea nu este interesant din punctul de
vedere al semanticii, de aceea nici nu va fi examinat.
- Clasa fractie conine dou cmpuri: numarat i numit pentru pstrarea, respectiv, a
numrtorului i a numitorului ce alctuiesc o fracie raional fr semn. Ambele cmpuri sunt de tipul
unsigned int i au unul i acelai tip de acces protected. Dac nlturm linia:
protected:
- din programul de mai sus, atunci, implicit, cmpurile numarat i numit vor avea tipul de acces
private. ns, n ambele cazuri, cu obiectele create ale clasei fractie putem doar s atribuim un
obiect altuia, cum este artat n funcia main(). Operatorul de atribuire este implicit realizat cu orice
clas declarat. El presupune copierea datelor (valoarea numrtorului i numitorului) din a doua fracie

n prima. Nu se tie ce va fi copiat n fr1 n exemplul nostru, fiindc nici n cmpurile obiectului fr1,
nici n cmpurile obiectului fr2 nu a fost nscris nici o valoare pn la aplicarea operaiei de atribuire.
Iniializarea nu a fost prevzut n versiunea clasei de mai sus. Mai mult ca att, nici nu exist
posibilitatea de a nscrie cumva valori n cmpurile obiectelor fr1 i fr2 (din cauza c sunt protejate).
Unicul lucru care poate fi afirmat este c dup atribuire, coninuturile cmpurilor obiectelor fr1 i fr2
vor fi identice.
- Din cauza c cmpurile clasei sunt protejate, orice ncercare de accesare, cum ar fi, de exemplu:
fr1.numarat = 2;
fr2.numit = 3;
- vor fi respinse de ctre compilator, fiind interzise (se va semnala o eroare).
- n versiunea propus a clasei fractie nu putem iniializa cmpurile obiectelor create, nu putem
afia obiectele, nu putem efectua calcule asupra lor etc. Aceste aciuni se pot realiza cu ajutorul funciilor
membre (metodelor) ale clasei.
- Exemplu 2. De alctuit un program n care este descris clasa carte. n baza acestei clasei sunt
create obiecte, care sunt apoi utilizate, apelnd la metodele clasei initializare i afisare.
-

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
class carte
{
char* autor;
char* denumire;
int an_ed;
public:
void initializare (char* a, char* d, int ae);
void afisare();
void nimicire();
};
void carte::initializare(char* a, char* d, int ae)
{
autor=(char*)malloc(srtlen(a)+1);
strcpy(autor,a);
denumire=(char*)malloc(strlen(d)+1);
strcpy(denumire,d);
an_ed=ae;
}
void carte::afisare()
{
printf(Autorul cartii: %s\n,autor);
printf(Denumirea cartii: %s\n,denumire);
printf(Anul editarii: %d\n,an_ed);
}
void carte::nimicire()
{
free (autor);
free (denumire);
}
void main ()
{
class carte em, an;
em.initializare(M.Eminescu,Opere complete,2002);
an.initializare(A.Demidovici,Analiza matematica,1960);
em.afisare();
an.afisare();

em.nimicire();
an.numicire();
}

- n rezultatul ndeplinirii acestui program se va afia pe ecran:


-

Autorul cartii: M.Eminescu


Denumirea cartii: Opere complete
Anul editarii: 2002
Autorul cartii: A.Demidovici
Denumirea cartii: Analiza matematica
Anul editarii: 1960

- 3. Constructori i destructori.
- La crearea oricrui obiect va fi apelat obligatoriu o funcie membr special constructorul
clasei. Dac acest lucru nu va fi fcut direct de ctre programator, el va fi fcut implicit de ctre
compilator. La distrugerea oricrui obiect va fi apelat obligatoriu o alt funcie membr special
destructorul clasei.
- Definiie 1. Constructorul este funcia membr special a clasei, destinat pentru iniializarea
fiecrui obiect al acesteia. Iniializarea se face imediat dup crearea obiectului (imediat dup declararea
variabilei de tip clas), adic dup ce el a fost declarat i lui i s-a alocat memorie.
- Definiie 2. Destructorul este funcia membr special a clasei, destinat pentru efectuarea unor
operaii adugtoare la distrugerea fiecrui obiect al clasei, cnd expir termenul lui de via. (De
exemplu, eliberarea memoriei alocate obiectului, nchiderea sau tergerea unor fiiere etc. Deci,
destructorul are funcia de a elimina toate urmele obiectului la distrugerea lui)
n procesul lucrului cu obiectele create ale unor clase, pot aprea situaii cnd obiectele nu
sunt pregtite pentru operaiile efectuate. Exemple care ar descrie astfel de situaii ar putea fi: ncercarea
de a afia informaia despre proprietile unui obiect, el ne-fiind iniializat; operaii cu membrii neiniializai ai unui obiect. Dac uneori astfel de operaii genereaz erori nesemnificative, n unele cazuri
urmrile por fi mai grave.
Pentru a evita, astfel de situaii, n cadrul claselor sunt utilizate nite funcii membre cu
destinaie special constructorii. Constructorul este o funcie membr care are menirea de a pregti
obiectele create pentru operaiile ulterioare. De aceea la procesul de creare a oricrui obiect particip i
constructorul clasei.
Exemple de aciuni puse n responsabilitatea constructorilor pot fi urmtoarele:
- iniializarea cmpurilor;
- alocarea de memorie i iniializarea cmpurilor de tip pointer;
- deschiderea unor fiiere;
- etc.
- Constructorul unei clase are urmtoarele particulariti:
- numele constructorului trebuie s fie identic cu numele clasei (obligatoriu);
- pentru o clas se pot defini mai muli constructori, fiecare iniializnd obiectele n moduri
diferite. Constructorii trebuie s se deosebeasc ntre ei prin numrul de argumente sau tipul
argumentelor;
- constructorul nu returneaz nici o valoare i deci, spre deosebire de celelalte funcii, i lipsete
din antet sau prototip descrierea tipului valorii returnate;
- constructorul poate fi apelat explicit ca o funcie membr obinuit. Dac nu se apeleaz nici un
constructor la crearea unui obiect, sistemul singur va apela unul din ei, i anume constructorul
implicit.
-

- Este prezentat urmtoarea schem sintactic, care descrie utilizarea constructorilor:


-

class nume_clasa
{
...
nume_clasa([tip1 param1,...,tipN paramN]);
...
};
//---------------------nume_clasa :: nume_clasa([tip1 param1,...,tipN paramN])
{
//instructiuni
}

- Fiind o funcie membr, constructorul poate fi implementat att n interiorul clasei, ct i n


exteriorul ei.
- Exist urmtoarele tipuri de constructori:
1) constructorul implicit;
2) constructori cu parametri;
3) constructorul de copiere;
- O clas poate s nu aib nici un constructor, poate avea unul sau mai muli constructori. Chiar
dac programatorul nu definete n interiorul clasei nici un constructor, sistemul va crea automat un
constructor constructorul fr parametri, care se numete constructor implicit. Totodat, constructorul
implicit poate fi definit i de ctre programator.
- Tradiional, constructorul cu parametri atribuie valorile argumentelor sale cmpurilor obiectului
care se creeaz cu acest constructor. Constructori cu parametri pot fi definii mai muli i ei, avnd toi
acelai nume, trebuie neaprat s se deosebeasc ntre ei prin numrul de argumente sau prin tipul
argumentelor.
- Constructorul de copiere permite construirea unui obiect nou n baza unui obiect existent. Dac
programatorul nu definete un constructor de copiere, sistemul va genera un constructor de copiere
automat. n urma iniializrii cu ajutorul constructorului de copiere, se obin dou obiecte identice.
O alt categorie de erori posibile n procesul de prelucrare a obiectelor ar putea fi neeliberarea de memorie, legat de careva obiect, atunci cnd obiectul i termin existena sa, ne-eliberarea
altor resurse, ce in de obiect. n asemenea situaii are loc o risip de resurse, deoarece este pierdut
complet controlul asupra lor, odat cu lichidarea obiectului. Astfel de situaii pot fi ocolite, utiliznd nite
funcii membre cu destinaie special, numite destructori. Destructorul este o funcie membru care are
menirea de a efectua o serie de operaii n contextul distrugerii obiectului. De aceea la procesul de
distrugere a oricrui obiect particip i destructorul clasei. Exemple de aciuni puse n responsabilitatea
destructorilor pot fi urmtoarele:
eliberarea memoriei asociate cu obiectul;
nchiderea unor fiiere etc.
- Destructorul unei clasei are urmtoarele particulariti:
numele destructorului este identic cu numele clasei, fiind precedat de simbolul ~;
similar cu constructorul, destructorul nu returneaz nici o valoare;
destructorul nu are parametri;
destructorul nu poate fi apelat explicit dup modelul unei funcii membre obinuite. Apelarea lui are loc
automat atunci, cnd obiectul ajunge la sfritul domeniului su de scop.
- Este prezentat urmtoarea schem sintactic, care descrie utilizarea destructorilor:
-

class nume_clasa
{

...
~nume_clasa();
...
};
//---------------------nume_clasa :: ~nume_clasa()
{
//instructiuni
}

- Clasa poate i s nu aib destructor. n acest caz, este creat un destructor implicit, care de fapt nu
efectueaz anumite operaii n procesul de distrugere a obiectului. Clasa poate avea cel mult un destructor
definit de programator.
- Destructorul se execut n ordine invers executrii constructorului. Deci la sfritul lucrului unei
funcii, obiectele create n ea se distrug n ordinea invers dect cea n care ele au fost create.
- Exemplu. S se defineasc clasa Angajat, obiectele creia ar conine informaia despre angajaii
unei ntreprinderi (de exemplu, numele, anul naterii, funcia, salariu). S se defineasc pentru aceast
clas trei tipuri de constructori i destructorul. S se defineasc funciile membre pentru a) Setarea
numelui angajatului, b) Accesarea numelui angajatului, c) Modificarea salariului angajatului, d) Setarea
salariului unui angajat ca media aritmetic dintre salariile altor doi angajai, e) Afiarea informaiei despre
angajat la ecran.
- Programul:
-

#include<stdio.h>
#include<conio.h>
#include<string.h>
#include<stdlib.h>
class Angajat
{
// membrii privati
private:
char* Nume;
char* Functia;
int An;
double Salariu;
// membrii publici
public:
Angajat(); // constructor implicit
Angajat(char* N,char* F,int A,double S);
// constructor cu parametri
Angajat(char* N,int A); // constructor cu parametri
Angajat(double S); // constructor cu parametri
Angajat(Angajat& X); // constructor de copiere
~Angajat(); // destructorul
void SetNume(char* N);
char* GetNume();
int ModificaSalariu(double S);
void SalariuMediu(Angajat& X,Angajat& Y);
void Afisare();
};

Angajat::Angajat()
{
Nume=(char*)malloc(sizeof("Necunoscut")+1);
Functia=(char*)malloc(sizeof("-")+1);
strcpy(Nume,"Necunoscut");
strcpy(Functia,"-");
An=0;
Salariu=0;
}
Angajat::Angajat(char* N,char* F,int A,double S)
{
Nume=(char*)malloc(sizeof(N)+1);
Functia=(char*)malloc(sizeof(F)+1);
strcpy(Nume,N);
strcpy(Functia,F);
An=A;
Salariu=S;
}
Angajat::Angajat(char* N,int A)
{
Nume=(char*)malloc(sizeof(N)+1);
Functia=(char*)malloc(sizeof("-")+1);
strcpy(Nume,N);
strcpy(Functia,"-");
An=A;
Salariu=0;
}
Angajat::Angajat(double S)
{
Nume=(char*)malloc(sizeof("Necunoscut")+1);
Functia=(char*)malloc(sizeof("-")+1);
strcpy(Nume,"Necunoscut");
strcpy(Functia,"-");
An=0;
Salariu=S;
}
Angajat::Angajat(Angajat& X)
{
Nume=(char*)malloc(sizeof(X.Nume)+1);
Functia=(char*)malloc(sizeof(X.Functia)+1);
strcpy(Nume,X.Nume);
strcpy(Functia,X.Functia);
An=X.An;
Salariu=X.Salariu;
}
Angajat::~Angajat()
{
printf("Obiectul (%s) a fost distrus.\n",Nume);
free(Nume);
free(Functia);
}
void Angajat::SetNume(char* N)
{
free(Nume);
Nume=(char*)malloc(sizeof(N)+1);
strcpy(Nume,N);

}
char* Angajat::GetNume()
{
return Nume;
}
// Daca se poate modifica, metoda modifica
// si returneaza valoarea 1(Adevar)
// Daca nu se poate - nu modifica
// si returneaza 0(Fals)
int Angajat::ModificaSalariu(double S)
{
if(Salariu+S < 0) return 0;
Salariu=Salariu+S;
return 1;
}
void Angajat::SalariuMediu(Angajat& X,Angajat& Y)
{
Salariu=(X.Salariu+Y.Salariu)/2;
}
void Angajat::Afisare()
{
printf("Nume: %s | Functie: %s | Anul nasterii: %4i | Salariu:
%g\n",Nume,Functia,An,Salariu);
}
void main()
{
clrscr();
// obiect creat cu constructorul implicit
Angajat a;
a.Afisare();
a.SetNume("Petru Moraru");
a.Afisare();
printf("Numele angajatului (a): %s\n",a.GetNume());
// Urmatoarele doua instructini comentate sunt
// interzise, deoarece datele membre sunt private:
// printf("%s",a.Nume); // a.Nume is not accesible
// a.An=1993;
// a.An is not accesible
// obiect creat cu 1-ul constructor cu parametri
Angajat b("Ion Padure","Manager",1992,1125.75);
b.Afisare();
// obiect creat cu al 2-lea constructor cu parametri
Angajat c("Dan Balan",1984);
c.Afisare();
// obiect creat cu al 3-lea constructor cu parametri
Angajat d(756.45);
d.Afisare();
// obiect creat cu constructorul de copiere
Angajat e(b);
e.Afisare();
c.SalariuMediu(b,d);
c.Afisare();

- getch();
- }

- Rezultat:
-

Nume: Necunoscut | Functie: - | Anul nasterii:


0 | Salariu: 0
Nume: Petru Moraru | Functie: - | Anul nasterii:
0 | Salariu: 0
Numele angajatului (a): Petru Moraru
Nume: Ion Padure | Functie: Manager | Anul nasterii: 1992 | Salariu: 1125.75
Nume: Dan Balan | Functie: - | Anul nasterii: 1984 | Salariu: 0
Nume: Necunoscut | Functie: - | Anul nasterii:
0 | Salariu: 756.45
Nume: Ion Padure | Functie: Manager | Anul nasterii: 1992 | Salariu: 1125.75
Nume: Dan Balan | Functie: - | Anul nasterii: 1984 | Salariu: 941.1
Obiectul (Ion Padure) a fost distrus.
Obiectul (Necunoscut) a fost distrus.
Obiectul (Dan Balan) a fost distrus.
Obiectul (Ion Padure) a fost distrus.
Obiectul (Petru Moraru) a fost distrus.

- Analiza programului principal main pe blocuri:


-

1)
// obiect creat cu constructorul implicit
Angajat a;
a.Afisare();
a.SetNume("Petru Moraru");
a.Afisare();
printf("Numele angajatului (a): %s\n",a.GetNume());
Se afieaz:
Nume: Necunoscut | Functie: - | Anul nasterii:
Nume: Petru Moraru | Functie: - | Anul nasterii:
Numele angajatului (a): Petru Moraru
2)
//
//
//
//

0 | Salariu: 0
0 | Salariu: 0

Urmatoarele doua instructini comentate sunt


interzise, deoarece datele membre sunt private:
printf("%s",a.Nume); // a.Nume is not accesible
a.An=1993;
// a.An is not accesible

3)
// obiect creat cu 1-ul constructor cu parametri
Angajat b("Ion Padure","Manager",1992,1125.75);
b.Afisare();
Se afieaz:
Nume: Ion Padure | Functie: Manager | Anul nasterii: 1992 | Salariu: 1125.75
4)
// obiect creat cu al 2-lea constructor cu parametri
Angajat c("Dan Balan",1984);
c.Afisare();
Se afieaz:
Nume: Dan Balan | Functie: - | Anul nasterii: 1984 | Salariu: 0
5)
// obiect creat cu al 3-lea constructor cu parametri
Angajat d(756.45);
d.Afisare();

Se afieaz:
Nume: Necunoscut | Functie: - | Anul nasterii:

0 | Salariu: 756.45

6)
// obiect creat cu constructorul de copiere
Angajat e(b);
e.Afisare();
Se afieaz:
Nume: Ion Padure | Functie: Manager | Anul nasterii: 1992 | Salariu: 1125.75
7)
c.SalariuMediu(b,d);
c.Afisare();

- Se afieaz:
-

Nume: Dan Balan | Functie: - | Anul nasterii: 1984 | Salariu: 941.1


8) Aceste mesaje apar dup ce se termin lucrul programului.
Destructorii se apeleaz automat de ctre sistem pentru
fiecare obiect creat din program, n ordine invers:
Obiectul
Obiectul
Obiectul
Obiectul
Obiectul

(Ion Padure) a fost distrus.


(Necunoscut) a fost distrus.
(Dan Balan) a fost distrus.
(Ion Padure) a fost distrus.
(Petru Moraru) a fost distrus.

- 4. Operaii de intrare/ieire a informaiei.


- n limbajul C++ sunt admise toate operaiile de intrare/ieire caracteristice limbajului C.
Suplimentar, n limbajul C++ au fost definite operaii noi de intrare/ieire care sunt create n stilul
programrii orientate pe obiecte. Au fost create o serie de clase orientate la intrarea/ieirea informaiei. n
baza acestor clase sunt definite obiecte care, la rndul su, includ operaii ce permit intrarea/ieirea
informaiei. O astfel de abordare i stil de realizare, are avantajul de a putea fi extins pentru necesitile
unei clase concrete.
- Pentru ieirea sau afiarea informaiei, este definit obiectul cout, iar pentru intrarea sau citirea
informaiei, este definit obiectul cin. Pentru afiarea mesajelor despre erori sunt definite obiectele cerr i
clog. Fiecare dintre obiecte poate utiliza att operatorii, ct i funciile caracteristice clasei din care este
instaniat el. n contextul obiectelor cout, cerr i clog este utilizat operatorul <<, numit operator de
inserie sau scriere, iar n contextul obiectului cin este utilizat operatorul >>, numit operator de extragere
sau citire. O form general de utilizare a operatorului << ar fi urmtoarea:
-

cout<<exp1<<exp2<<...<<expN;

unde exp1, exp2, , expN (N1) sunt expresiile a cror valori sunt afiate. Expresiile
concatenate ntr-o expresie lung pot fi afiate separat:

cout<<exp1;
cout<<exp2;
. . .
cout<<expN;

Un exemplu de afiare a valorilor unor variabile:

int i=-27;
char c=a;
char s[]=Un sir;
long l=100000;

cout<<i;
cout<<c;
cout<<s;
cout<<l;

Rezultatul afirii:

-27aUn sir100000

Desigur c scriind:

cout<<i<<c<<s<<l;

va fi obinut acelai rezultat al afirii.

cin>>var1>>var2>>...>>varN;

unde var1, var2, , varN (N1) sunt variabilele n care sunt citite valori.

cin>>var1;
cin>>var2;
. . .
cin>>varN;

Un exemplu de utilizare a obiectului cin:

Pentru operatorul de citire >> este valabil urmtoarea form general:

Acelai lucru poate fi realizat i n expresii separate:

unsigned u;
float f;
long double ld;
cin >> u >> f >> ld;

- Delimitatori ai valorilor introduse sunt spaiile albe , \t, \n.


- Pentru a putea utiliza n programe obiectele i operatorii respectivi, este inclus fiierul antet
<iostream.h>, unde sunt efectuate descrierile necesare referitoare la aceste obiecte.
- Obiectele cout i cin permit, de asemenea, i operaii de formatare care sunt realizate prin
intermediul unor construcii numite manipulatori. Un manipulator schimb starea fluxului pregtindu-l
pentru careva operaii de formatare. Pentru a putea folosi manipulatorii, este necesar includerea fiierului
antet <iomanip.h>. n caz general, un manipulator acioneaz asupra fluxului, acionnd asupra
obiectului implicat n operaiile de intrare/ieire prin intermediul operatorului corespunztor:
-

cout << manipulator[ << . . .];

sau

cin >> manipulator[ >> . . .];

unde punctele de suspensie . . . nseamn o posibil continuare a construciei.

- Fluxul de intrare are mai puini manipulatori dect cel de ieire. Pentru ignorarea spaiilor albe la
citirea informaiei, este utilizat manipulatorul ws. n exemplul urmtor:
-

double d;
cin >> ws >> d;

sunt ignorate spaiile albe n procesul citirii valorii n variabila d.

- n continuare, vor fi caracterizai manipulatorii fluxului de ieire. Pentru a trece din rnd nou la
ieirea (afiarea) informaiei, este utilizat manipulatorul endl. Din exemplul ce urmeaz se poate vedea
c acest manipulator poate fi modelat afind secvena de dirijare \n:
-

int i=10;
float f=1.2;
char s[]=Un exemplu, c=A;

cout<<i<<endl<<f<<endl<<c<<\n<<s<<endl;

Rezultatul afirii:

10
1.2
A
Un exemplu

- n continuare urmeaz un grup de manipulatori care se refer doar la tipuri de date ntregi:
- oct permite fixarea sistemului de numeraie octal;
- dec permite fixarea sistemului de numeraie zecimal;
- hex permite fixarea sistemului de numeraie hexazecimal.
- Oricare dintre aceti manipulatori i fixeaz aciunea pn la anularea ei de un alt manipulator din
acest grup.
-

int i=10;
cout<<i<< <<oct<<i<< <<hex<<i<< <<dec<<i<<endl;

Rezultatul afirii:

10 12 a 10

- Manipulatorii descrii anterior sunt manipulatori ne-parametrizai. Exist o serie de manipulatori


parametrizai, a cror descriere urmeaz n continuare. Ei sunt realizai sub form de funcii, deci, modul
lor de utilizare este asemntor cu apelarea unor funcii.
- Un manipulator a crui aciune este echivalent cu aciunea manipulatorilor oct, dec, hex este
manipulatorul setbase. Modul lui de utilizare este
-

setbase(baza)

unde parametrul baza poate lua valorile 8, 10 sau 16, permind respectiv fixarea sistemului de
numeraie octal, zecimal sau hexazecimal. Va fi rescris exemplul anterior utiliznd setbase:

int i=10;
cout<<i<< <<setbase(8)<<i<< <<setbase(16)<<i<< <<setbase(10)<<i<<endl;

Rezultatul afirii:

10 12 a 10

- Dac este necesar fixarea limii cmpului de afiare, este utilizat manipulatorul setw. Forma
general este:
-

setw(latime)

unde parametrul latime reprezint o expresie ntreag care dicteaz limea cmpului de afiare.
Limea cmpului este fixat doar pentru o singur afiare, dup care se revine la valoarea
implicit egal cu unu.

Un exemplu de utilizare a acestui manipulator:

int i=10, lat=5;


char c=A;
cout<<setw(3)<<i<<setw(lat)<<c<<endl;

Rezultatul afirii (fiecare celul reprezint o poziie pe ecran):

- - - - - - - 10
A

- Din exemplul precedent se vede c dac valoarea afiat are mai puine simboluri dect numrul
de poziii ale cmpului de afiare, atunci poziiile rmase sunt completate cu spaii . Acesta este
caracterul implicit de completare. ns este posibil fixarea altui caracter de completare, utiliznd
manipulatorul setfill. Forma general este:
-

setfill(caracter_completare)

unde parametrul caracter_completare reprezint o expresie, care ofer caracterul de


completare. Caracterul de completare este fixat pn la o nou fixare. n continuare este re-scris
exemplul precedent, utiliznd i manipulatorul setfill:

int i=10, lat=5;


char c=A;
cout<<setfill(.);
cout<<setw(3)<<i<<setw(lat)<<c<<endl;

Rezultatul afirii:

- - - - - - - .10....A

- Pentru a fixa exactitatea de afiare a valorilor cu virgul mobil este utilizat manipulatorul
setprecision. Forma general de utilizare este urmtoarea:
-

setprecision(exactitate)

unde parametrul exactitate este o expresie ntreag, care dicteaz numrul de cifre semnificative
dup virgul. Exactitatea este fixat pentru toate afirile, pn la o nou fixare. Dac parametrul
exactitate ia valoarea 0, atunci se revine la exactitatea implicit egal cu 6 cifre dup virgul.
Iat un exemplu:

float f=1.2;
double d=-15.4563283432;
int i;

Rezultatul afirii:

1.2
1.2
1.2
1.2
1.2
1.2
1.2

cout << f << << d << endl;


for(i=0;i<6;i++)
cout<<setprecision(i)<<f<< <<d<<endl;
-15.456328
-15.456328
-15.5
-15.46
-15.456
-15.4563
-15.45633

- Un manipulator cu o gam larg de aciuni de formatare este manipulatorul setiosflags. Forma


general de aplicare a acestui manipulator este:
-

setiosflags(fanioane)

unde parametrul fanioane permite fixarea unor proprieti, numite fanioane. Fiecare fanion este
responsabil de o anumit aciune. Fanioanele, utilizate cu manipulatorul setiosflags sunt
descrise sub form de enumerare n cadrul clasei ios. De aceea denumirile fanioanelor sunt
precedate de denumirea clasei ios i operatorul rezoluiei.
- Tabelul urmtor descrie aciunile, ce in de fiecare fanion:

1
2
-

3
4

- Aciune
caracteristic

Nume
fanion

ios::skipw
s
ios::left

ios::right

ios::inter

ignorarea spaiilor
albe
alinierea
informaiei n
partea stng a
cmpului
alinierea
informaiei n
partea dreapt a
cmpului
alinierea

nal

informaiei n
ambele pri dac
este posibil

5
6
-

ios::showp
os
ios::showb
ase

forarea semnului

baza sistemului de
numeraie

7
-

ios::showp
oint

forarea punctului
pentru numerele cu
virgul mobil

8
-

ios::fixed

afiarea numerelor
reale n format cu
punct fix

9
-

ios::scien
tific

afiarea numerelor
reale n format
tiinific

ios::hex

afiarea numerelor
ntregi n format
hexazecimal

ios::oct

afiarea numerelor
ntregi n format
octal

ios::dec

afiarea numerelor
ntregi n format
zecimal

ios::stdio

golirea fluxul
stdout i stderr

ios::upper
case

scrierea numerelor
hexazecimale cu
litere majuscule

ios::unitb
uf

golirea tuturor
fluxurilor de date
dupa citirea datelor

1
1
1
1
-

1
-

Iat o serie de exemple ce descriu aciunile manipulatorului setiosflags.


Alinierea stnga sau dreapta:

int i=10;
cout<<setiosflags(ios::left)<<setw(5)<<i
<<setfill(-)<<setiosflags(ios::right)
<<setw(5)<<i<<endl;

Rezultatul afirii:

- - - - - - - - - 10
---10

Forarea semnului i a punctului:

float f=20;
cout<<f<< <<setiosflags(ios::showpos)<<f<<
<<setiosflags(ios::showpoint)<<f<< <<endl;

Rezultatul afirii:

- - - - - - - - - - - - - - - - - 20 +20 +20.000000

- Manipulatorul setiosflags permite combinarea mai multor fanioane concomitent. Pentru


aceasta este utilizat operatorul | (SAU). Iat un exemplu:
-

long l=0x49;
cout << setiosflags(ios::showbase | ios::hex);
cout << setw(6) << l << setiosflags(ios::internal)
<< setw(6) << l << endl;

Rezultatul afirii:

- - - - - - - - - - - 0x490x
49

- Pentru a restabili fanioanele la valorile implicite, este utilizat manipulatorul resetiosflags,


care acioneaz similar cu manipulatorul setiosflags. n exemplul urmtor este restabilit fanionul
ios::showpoint:
-

double d=-320;
cout <<setiosflags(ios::showpoint) << d <<
<<resetiosflags(ios::showpoint) << d <<endl;

Rezultatul afirii:

- - - - - - - - - - - - - - - - 3 2 0 . 0 0 0 0 0 0
- 3 2 0

- Clasele orientate la intrarea/ieirea informaiei au pe lng operatori i o serie de funcii cu aciuni


specifice, utile n procesul de intrare/ieire a informaiei. Astfel, funciile corespunztoare pot fi utilizate
cu obiectele cout i cin.
- Pentru a citi un caracter din fluxul de intrare, este utilizat funcia get(). Forma de utilizare este
urmtoarea
-

cin.get(caracter);

unde parametrul caracter este variabila n care este citit valoarea. De exemplu:

char c;
cin.get(c);

- Operatorul de citire >> a obiectului cin nu permite un lucru eficient cu iruri de caractere. De
exemplu, dac se dorete a citi numele i prenumele unei persoane ntr-o variabil, atunci o astfel de
soluie
-

char persoana[100];
cin >> persoana;

nu este corect, fiindc n variabila persoana va fi citit doar numele persoanei, prenumele fiind
ignorat, fiindc spaiul joac rolul de trecere la prelucrarea urmtorului cmp de intrare. Pentru a
putea citi propoziii, va fi utilizat funcia getline(), care are urmtoarele forme:

getline(char *sir, int lung_max);


getline(char *sir, int lung_max, char caracter_oprire);

unde parametrul sir este irul n care se citete, parametrul lung_max este lungimea maximal
a informaiei citite, iar parametrul caracter_oprire reprezint caracterul la ntlnirea cruia

se va ntrerupe procesul de citire. Pentru prima variant a funciei, caracterul de oprire este \n,
generat de tasta Enter. Astfel sarcina propus anterior poate fi soluionat aa:
-

char persoana[100];
cin.getline(persoana, sizeof(persoana));

- Dac este necesar de a afla cte caractere au fost citite n fluxul neformatat este aplicat funcia
gcount().
-

char nuA[50];
int i;
cin.getline(nuA, sizeof(nuA), a);
i=gcount();
cout << i << endl;

Fragmentul anterior afieaz cte simboluri au fost citite n variabila nuA pn la ntlnirea
primului simbol a.

- Desigur c i obiectul cout are o serie de funcii utile. Funcia width() are dou variante. Una
permite citirea limii curente, iar alta permite fixarea limii curente. Iat forma lor general:
-

int width();

int width(latime);

unde latime este expresia a crei valoare va fi luat drept baz pentru fixarea limii cmpului
de afiare. De exemplu:

float f=10.25;
cout.width(8);
cout<<f<<endl;

Rezultatul afirii:

- - - - - - - 10.25

- Dou forme similare are i funcia precision(), care se refer la exactitatea de afiare a
valorilor cu virgul mobil: varianta fr parametri returneaz exactitatea curent, iar varianta cu
parametru fixeaz exactitatea necesar. De exemplu
-

double d=-114.361782;
cout.precision(4);
cout<<d<<endl;

Rezultatul afirii:

- - - - - - - - -114.3618

- Pentru a determina caracterul de umplere a poziiilor ne-acoperite de simbolurile valorii afiate


poate fi utilizat funcia fill() fr parametri. De exemplu, fragmentul ce urmeaz:
-

char cu;
cu=cout.fill();
cout << cu << endl;

va afia caracterul de umplere curent cu, determinat utiliznd funcia fill(). Pentru a fixa
caracterul de umplere, este utilizat, de asemenea, funcia fill(caracter_umplere) cu
parametri, avnd ca parametru o expresie ce ofer caracterul de umplere. De exemplu, n cele ce
urmeaz va fi fixat caracterul de umplere *.

long l=-249793;
cout.fill(*);
cout.width(10);
cout<<l<<endl;

Rezultatul afirii:

- - - - - - - - - ***-249793

- Pentru a putea lucra cu fanioanele caracterizate anterior, poate fi utilizat funcia flags(), care
are o variant fr parametri i o variant cu parametri.
-

int fnv = cout.flags();


int fn = 0x1234;
cout.flags(fn);
cout << 239 << endl;
cout.flags(fnv);

n acest exemplu sunt utilizate ambele variante, care permit fixarea valorii vechi a fanioanelor i
revenirea la aceast valoare dup afiarea valorii ntregi 239.
- Exemplu. De alctuit un program n care este modelat un cuprins de carte.

#include<iostream.h>
#include<iomanip.h>
#include<string.h>
#define latimePagina 65
void main()
{
char *paragrafe[]={Cuprins,Introducere,
Clase si obiecte,
Constructor.Destructor};
int pagini[]={3,10,12};
int i, lung;
-

lung=strlen(paragrafe[0]);
cout << setw((latimePagina+lung)/2); //Centrare
cout << paragrafe[0] << endl;
//Cuprins
cout << setfill(.);
for(i=0; i<3; i++)
{
-

cout << paragrafe[i+1];


lung=strlen(paragrafe[i+1]);
cout << setw(latimePagina-lung);
cout << pagini[i] << endl;

Rezultatul afirii:

Cuprins
Introducere.....................................................3
Clase si obiecte...............................................10
Constructor.Destructor.........................................12

- 5. Motenire simpl.
- Obiectele din domeniul de problem nu sunt statice unul fa de altul, ci inter-acioneaz ntre ele.
Vom spune c obiectele din cadrul unei probleme se afl n anumite relaii. Vom evidenia o serie de
relaii dintre obiecte:
1. relaii de asociere, cnd un obiect transmite un mesaj altui obiect;
2. relaii de agregare, cnd un obiect este parte component a altui obiect;
3. relaii de tip client-server, cnd un obiect folosete serviciile oferite de alt obiect;
4. relaii de motenire, cnd un obiect transmite o serie de proprieti i operaii altui obiect.
- Fiindc obiectele de acelai tip formeaz o clas de obiecte, se poate vorbi i despre relaii ntre
clase, generalizndu-le pe cele enumerate anterior la nivel de clase.
- Utiliznd relaia de motenire n procesul programrii se obin aplicaii eficiente din punctul de
vedere al mrimii codului obinut i al lipsei de erori. De exemplu, trebuie de realizat un sistem de

gestiune n cadrul sistemului de fiiere. n domeniul de problem pot fi evideniate o serie de obiecte
caracteristice. Ca exemplu ar putea fi evideniate obiectele Program i Document. Dac ar fi proiectate
tipuri abstracte de date corespunztoare acestor obiecte, evideniind proprietile i operaiile
caracteristice acestor obiecte, s-ar putea propune urmtoarele clase:
- Doc
- Progra
ume
m
nt
- Num
- Nume
e
- Dimensi
- Dim
une
ensiu
- Data
ne
- TipSiste
- Data
mOperar
- Tip
e
- Copi
- Copiere
ere
- Nimicire
- Nimi
- Redenu
cire
mire
- Rede
- Lansare
numi
re
- Afi
are
- Impr
imar
e
- Analiznd clasele propuse, se poate observa c exist o serie de proprieti i o serie de operaii
comune pentru ambele tipuri de obiecte. Definind funciile membre ale ambelor clase, vom avea o serie
de funcii din diferite clase realizate identic. Astfel, se obine o dublare de cod. O cale mai eficient ar fi
posibilitatea de evideniere i implementare a unei clase generale, care ar putea transmite caracteristicile
sale altor clase mai concretizate. Astfel n cazul exemplului de mai sus s-ar putea evidenia obiectul
general numit Fisier, care va conine caracteristicile comune ale obiectelor ce in de sistemul de fiiere:
- Fisie
r
- Num
e
- Dim
ensi
une
- Data
- Copi
- Do
- Progra
ere
cu
m
- Nimi
me
cire
nt
- Rede
- Tip
- numi
TipSiste
remOperar
e
- Afi
- Lansare
sar
e
- Im
pri
mar
e

- Transmiterea de proprieti i funcionaliti dintr-o clas numit clas de baz ntr-o clas numit
clas derivat se numete motenire simpl. Procesul de transmitere se numete derivare.
- La nivel de diagram, motenirea simpl are urmtoarea reprezentare:
cl_baza
cl_der1
cl_der2
...
cl_derN
- La nivel de descriere n limbajul C++, avem urmtoarea form general:
-

class cl_baza
{
.
<proprietati si metode cl_baza>
.
};

unde elementele modificator_de_mostenire1, , modificator_de_mostenireN


reprezint modificatorii de motenire prin care clasa de baz este implicat n procesul de
derivare. Modificatorii de motenire pot fi unul dintre cuvintele-cheie:

private
protected
public

class cl_der1:[modificator_de_mostenire1] cl_baza


{
.
<proprietati si metode cl_der1>
.
};
...
class cl_derN:[modificator_de_mostenireN] cl_baza
{
.
<proprietati si metode cl_derN>
.
};

Dac modificatorul de motenire nu este prezent n descriere, se consider implicit


modificatorul private.
- Urmnd schema general, pentru exemplul anterior, s-ar obine urmtoarea descriere:
-

class Fisier
-

char Nume[256];
unsigned long Dimensiune;
char Data[20];
public:
void Nimicire();
void Copiere(char *numeFisier);
void Redenumire(char *numeNou);

};

//--------------class Document : public Fisier


-

char Tip[15];
public:
void Afisare();
void Imprimare();
-

};

//--------------class Program : public Fisier


-

char TipSistemOperare[25];
public:
void Lansare();
-

};

- Un obiect creat n baza clasei derivate are o serie de caracteristici dependente de clasa de baz. De
aceea, n procesul de creare a acestui obiect, va participa att constructorul clasei de baz, ct i
constructorul clasei derivate. Acest fapt este vizibil i la nivel de descriere general a constructorului
clasei derivate:
-

cl_der::cl_der(param_cl_der):cl_baza(param_cl_baza)
{
.
-

//instructiuni
.
}

- De obicei, lista parametrilor clasei de baz este o submulime a listei parametrilor clasei derivate.
- Fiindc procesul de creare a unui obiect n baza clasei derivate depinde de doi constructori, este
bine a ti ordinea execuiei lor: mai nti va lucra constructorul clasei de baza, iar apoi va lucra
constructorul clasei derivate.
- Ordinea de execuie a destructorilor este invers: mai nti lucreaz destructorul din clasa derivat,
iar apoi lucreaz destructorul din clasa de baz.
- A fost menionat c o clas derivat are o serie de caracteristici care vin din clasa de baz (le
motenete). Vom examina tipul de acces la aceti membri motenii n clasa derivat. Combinaia dintre
modificatorii de acces ai membrilor clasei de baz i modificatorul de motenire determin tipul de acces
la membrii clasei derivate.
- Tabelul de mai jos descrie combinaiile posibile i tipul de acces obinut ca rezultat:
privat -

private
inaccesibil

protec -

private

protected

protected

public -

private

protected

public

Modificator de acces (n
e
clasa de baz) ted
-

Modificator de motenire
protected
public
inaccesibil
inaccesibil

Exemplu. De alctuit un program n care sunt implementate clasa punct i clasa cerc, care este
derivat din clasa punct.

class punct
{
protected:
int x, y;
int visibil;
public:

punct(int x1, int y1);


void afisare();
void stingere();
void miscare(int xn, int yn);
};
class cerc : public punct
{
int r;
public:
cerc(int x, int y, int r);
void afisare_c();
void stingere_c();
void miscare_c(int xn, int yn);
};
//-----------------------punct :: punct(int x1, int y1)
{
x=x1;
y=y1;
}
//-----------------------void punct :: afisare()
{
if (visibil == 0)
{
putpixel(x, y, getcolor() );
visibil=1;
}
}
//-----------------------void punct :: stingere()
{
if (visibil==1)
{
putpixel(x, y, getbkcolor() );
visibil=0;
}
}
//-----------------------void punct :: miscare(int xn, int yn)
{
stingere();
x=xn;
y=yn ;
afisare();
}
//-----------------------cerc :: cerc(int x, int y, int r1): punct (x,y)
{ r=r1; }
//-----------------------void cerc :: afisare_c()
{
if (visibil ==0)
{
circle(x,y,r);
visibil=1;
}
}
//------------------------

void cerc :: stingere_c()


{
int c=getcolor();
if (visibil == 1)
{
setcolor(getbkcolor());
circle(x,y,r);
setcolor(c);
visibil=0;
}
}
//-----------------------void cerc :: miscare_c(int xn, int yn)
{
stingere_c();
x=xn;
y=yn;
afisare_c();
}

Declararea claselor i implementarea lor vor fi memorate n fiierul cerc.hpp.


Programul principal n care sunt utilizate obiecte create n baza clasei cerc va fi scris n fiierul
cerc.cpp:
-

//cerc.cpp
#include<iostream.h>
#include<conio.h>
#include<graphics.h>
#include cerc.hpp
void main()
{
int dr = DETECT, rdr, er;
initgraph(&dr, &rdr, );
er=graphresult();
if (er==grOk)
{
cerc c1(100, 100, 20);
punct p(150, 270);
p.afisare();
c1.afisare_c();
c1.miscare_c(200,300);
getch();
}
else
cout << Eroare de initializare a regimului grafic.\n;
closegraph();
}

- 6. Motenire multipl
- Transmiterea de proprieti i funcionaliti dintr-o serie de clase numite clase de baz ntr-o clas
numit clas derivat se numete motenire multipl. Procesul de transmitere se numete derivare.
- Diagrama ce urmeaz descrie schema general a motenirii multiple:
cl_baza1
cl_baza2
...
cl_bazaN
cl_der

La nivel de descriere n expresii ale limbajului, avem urmtoarea schem:

class cl_baza1
{
.
<date si
.
};
class cl_baza2
{
.
<date si
.
};
. . .
class cl_bazaN
{
.
<date si
.
};
class cl_der :
{
.
<date si
.
};

unde cl_baza1, cl_baza2, , cl_bazaN sunt clasele de baz, cl_der este clasa derivat,
mod_m1, mod_m2, ..., mod_mN sunt modificatorii de motenire care introduc clasele de baz n
procesul de derivare. Ca i n cazul motenirii singulare modificatori de motenire pot fi unul
dintre cuvintele-cheie: public, protected, private. Dac o oarecare clas de baz nu are
scris explicit modificator de motenire, se consider implicit modificatorul private.

functii membre 1>

functii membre 2>

functii membre N>

[mod_m1]cl_baza1, ..., [mod_mN]cl_bazaN

functii membre Der>

- Un obiect creat n baza clasei derivate va avea o serie de caracteristici dependente de clasele de
baz din care vin, ceea ce este vizibil i din modul general de descriere a constructorului clasei derivate:
-

cl_der::cl_der(param_der)[:cl_baza1(param_b1)],...
[,cl_bazaN(param_bN)]
{
.
//instructiuni
.
}

- ntruct, la crearea unui obiect n baza unei clase derivate conlucreaz mai muli constructori, va fi
evideniat ordinea de execuie a lor. Mai nti sunt executai constructorii claselor de baz n ordinea n
care apar ei n descrierea clasei derivate. Dup ce constructorii claselor de baz i-au terminat lucrul, este
executat i constructorul clasei derivate. Destructorii sunt executai n ordine strict invers ordinii de
execuie a constructorilor.
- Pentru a caracteriza interaciunea dintre constructorii clasei derivate i constructorii claselor de
baz, concretiznd astfel descrierea general a constructorilor clasei derivate vor fi evideniate
urmtoarele patru situaii posibile:

1)

Exist mcar un constructor n clasa derivat i constructori mcar n unele clase de baz. n acest
caz constructorii claselor de baz pot s lipseasc din descrierea constructorului clasei derivate doar
cnd sunt fr parametri sau se reduc la constructori fr parametri.
2)
Exist mcar un constructor n clasa derivat i nu exist constructori n clasele de baz. n acest
caz, fiindc constructorii implicii nu fac careva iniializri, ar putea fi deschis accesul spre unii membri
ai claselor de baz pentru a putea fi iniializai din constructorul clasei derivate.
3)
Nu exist constructori n clasa derivat, dar exist constructori mcar n unele clase de baz. n
acest caz clasele de baz care au constructori trebuie s aib constructori fr parametri sau constructori
care se reduc la constructori fr parametri.
4)
Nu exist constructori n clasa derivat i nu exist constructori n nici o clas de baz.
- Lista parametrilor fictivi a constructorului clasei derivate adeseori acoper necesitatea de
parametri ai constructorilor claselor de baz, dar aceasta nu este o cerin strict.
- Unii membri ai claselor de baz pot fi suprancrcai (suprascrii) n procesul derivrii. Existena
n clasa derivat a unor membri cu aceleai caracteristici ca i n clasa de baz va fi numit
suprancrcare. n cazul datelor membre caracteristicile vor nsemna:
- numele membrului;
- tipul membrului.
-n cazul funciilor membre caracteristicile vor nsemna prototipul funciei membru, adic:
- numele membrului;
- tipul returnat;
- tipul i numrul parametrilor fictivi ai funciei.
- Este posibil utilizarea membrilor suprancrcai ai claselor de baz n cadrul funciilor membre
ale clasei derivate. Pentru a putea accesa un membru suprancrcat al unei clase de baz, numele lui este
prefixat de numele clasei conectat prin operatorul rezoluiei:
-

nume_cl_baza::nume_membru

- O situaie similar are loc i atunci cnd n, cel puin, dou clase de baz sunt membri cu aceleai
caracteristici. Pentru a putea accesa careva dintre aceti membri n cadrul funciilor membre ale clasei
derivate, numele membrului este, de asemenea, prefixat de numele clasei conectat prin operatorul
rezoluiei.
- Exemplu. n prezent, o serie de cri sunt dotate cu un disc coninnd coninutul crii n varianta
electronic. Exemplul ce urmeaz se va referi la o astfel de situaie. Diagrama de motenire este
urmtoarea:
Carte
Disc
c
Suport informational
Realizare. De alctuit un program n care sunt implementate clasele carte, disc i
supInfo, care este derivat din clasele carte i disc.
-

class carte
{
-

char *autor;
char *titlu;
int an;

public:
-

carte(char *au, char *t, int a);


carte();
~carte();
void afisare();

};
class disc
{
char *tip;
int capacitate;
public:
disc(char *t, int c);
~disc();
void afisare();
};
class supInfo: public carte, public disc
{
float pret;
public:
supInfo(char *au, char *tt, int a, char *t,
int c, float p);
supInfo(char *t, int c, float p);
void afisare();
};
//===================
carte::carte(char *au, char *t, int a)
{
autor=(char *)malloc(strlen(au)+1);
titlu=(char *)malloc(strlen(t)+1);
strcpy(autor, au);
strcpy(titlu, t);
an=a;
}
//------------------carte::carte()
{
char sir[100];
cout<<Autorul: ;
cin.getline(sir, 100);
autor=(char *)malloc(cin.gcount());
strcpy(autor, sir);
cout<<Titlul: ;
cin.getline(sir, 100);
titlu=(char *)malloc(cin.gcount());
strcpy(titlu, sir);
cout<<Anul: ;
cin>>an;
}
//------------------carte::~carte()
{
free(autor);
free(titlu);
}
//------------------void carte::afisare()
{
cout<<titlu<< de
<<autor<< a aparut in
<<an<<endl;
}
//===================
disc::disc(char *t, int c)
{
tip=(char *)malloc(strlen(t)+1);
strcpy(tip, t);

capacitate=c;
}
//------------------disc::~disc()
{
free(tip);
}
//------------------void disc::afisare()
{
cout<< Are un disc << tipul
<< de capacitatea
<< capacitate << endl;
}
//===================
supInfo::supInfo(char *au, char *tt, int a,
char *t, int c, float p):
-

{
pret=p;
}
//------------------supInfo::supInfo(char *t, int c, float p):
-

carte(au,tt,a), disc(t,c)

disc(t,c)

{
pret=p;
}
//------------------void supInfo::afisare()
{
carte::afisare();
disc::afisare();
cout<<Si are un pret de
<<pret<< lei<<endl;
}

- Declararea claselor i implementarea lor va fi memorat n fiierul supinfo.hpp. Programul


principal n care sunt utilizate obiecte create n baza clasei supInfo va fi scris n fiierul
supinfo.cpp:
-

#include<iostream.h>
#include<string.h>
#include<stdlib.h>
#include supinfo.hpp
void main()
{
supInfo cpp(Kris Jansa, Succes cu C++,
2000, CD-ROM, 700, 200);
supInfo pr(CD-ROM,700,300);
cpp.afisare();
pr.afisare();
}

- 7. Motenirea pe mai multe niveluri. Clase virtuale


-

- Atunci cnd o clas transmite parametri sau funcionaliti altei clase care, la rndul su, se
consider clas de baz pentru o alt ierarhie de motenire, vom spune c avem motenire pe mai multe
niveluri.
Cea mai simpl diagram care exemplific motenirea pe mai multe niveluri este
urmtoarea:
- Schema de motenire pe mai multe niveluri se reduce la o aplicare consecutiv a schemei de
motenire pe un singur nivel. Schemele de motenire pe mai multe niveluri pot fi reprezentate prin grafuri
cu mai multe niveluri. Diagramele reprezentate prin grafuri care nu conin cicluri nu prezint careva
dificulti de realizare. Dac ns grafurile au cicluri, apar unele probleme. Cea mai simpl diagram de
acest fel este de urmtorul tip:
A
-

Pentru o astfel de diagram exist i interpretri care descriu situaii reale:

Persoana

Student

Asistent

Doctorand

La realizarea unei astfel de scheme, membrii din clasa Persoana vor ajunge n clasa
Doctornad pe dou ci: una prin clasa Asistent i cealalt prin clasa Student. Deci clasa
Doctorand va avea dou exemplare pentru fiecare membru venit din clasa Persoana. Generaliznd
cele spuse anterior, s-ar putea afirma c dac dintr-un vrf ierarhic superior al diagramei exist mai multe
drumuri ctre un alt vrf ierarhic inferior, atunci fiecare membru al vrfului ierarhic superior va ajunge n
vrful ierarhic inferior de attea ori cte drumuri exist ntre aceste vrfuri.

- Uneori, n situaia apariiei mai multor membri care vin n aceeai clas, apar situaii de conflict.
S-ar putea evita astfel de situaii, prin utilizarea claselor virtuale la motenire. Pentru a declara o clas
virtual la motenire n schema de motenire se utilizeaz cuvntul-cheie virtual naintea clasei date.
Clasele virtuale sunt protejate de dublri ale membrilor.
- n continuare, vor fi exemplificate ideile expuse anterior utiliznd diagrama precedent care
descrie relaia dintre clasele A, B, C, D.
-

#include<iostream.h>
class A
{
protected:
int ia;
public:
A(int i){ia=i;}
};
//------------class B:public A
{
protected:
int ib;
public:
B(int ia, int i) : A(ia){ ib=i; }
};
//------------class C:public A
{
protected:
int ic;
public:
C(int ia, int i) : A(ia){ ic=i; }
};
//------------class D:public B, public C
{
int id;
public:
D(int ia, int ib, int ic, int i) :
-

B(ia, ib),C(ia, ic){ id=i; }


void afisare()
{
cout<<ia=<<ia<<ib=<<ib<<ic=<<ic
<<id=<<id<<endl;
}

};
//==============
void main()
{
D d(7,17,27,37);
d.afisare();
}

- Exemplul anterior are o problem: funcia membru afisare(), apelat prin intermediul
obiectului d, va genera o eroare din motiv c membrul ia nu poate fi afiat, deoarece n clasa D exist
doi membri cu numele ia. Va trebui schimbat modul de afiare dup cum urmeaz:
-

void afisare()
{
cout<<ia=<<B::ia<<ib=<<ib<<ic=<<ic
<<id=<<id<<endl;
}

afind membrul ia ce vine prin clasa B. Dup aceast schimbare, programul va putea fi lansat,
dar clasa D va continua s aib doi membri cu numele ia. Pentru a scpa de acest neajuns, clasa
A va fi declarat clas virtual.
- Declarnd clasa A clas virtual, va fi fcut o schimbare mic, dar esenial a exemplului. Iat ce
se obine:
-

#include<iostream.h>
class A
{
protected:
int ia;
public:
A(int i){ia=i;}
};
//------------class B:public virtual A
{
protected:
int ib;
public:
B(int ia, int i) : A(ia){ ib=i; }
};
//------------class C:virtual public A
{
protected:
int ic;
public:
C(int ia, int i) : A(ia){ ic=i; }
};
//------------class D:public B, public C
{
int id;
public:
D(int ia, int ib, int ic, int i) :
B(ia, ib),C(ia, ic){ id=i; }
void afisare()
{
cout<<ia=<<ia<<ib=<<ib<<ic=<<ic
<<id=<<id<<endl;
}
};
//==============
void main()
{
D d(7,17,27,37);
d.afisare();
}

- Ordinea de execuie a constructorilor n scheme de motenire ce conin clase virtuale este


urmtoarea: sunt evideniate clasele virtuale i sunt executai constructorii lor n ordinea n care sunt
ntlnii n descrierea clasei derivate. Dup ce i termin lucrul constructorii virtuali, vor lucra
constructorii ne-virtuali ai claselor ne-virtuale, tot n ordinea apariiei n schema de motenire.
- Membrii transmii din clasa de baz n clasa derivat au tendina de a-i micora gradul de acces
din punctul de vedere al clasei derivate. n scheme de motenire pe mai multe niveluri aceast micorare
poate fi i mai accentuat, existnd posibilitate de existen a membrilor din clasa de baz, care ajung
inaccesibili ntr-o oarecare clas derivat de la anumit nivel. Exist posibilitatea de a mri gradul de acces
al membrilor transmii dintr-o clas de baz ntr-o clas derivat. Pentru a mri gradul de acces al unui

membru transmis dintr-o clas de baz ntr-o clas derivat, numele membrului precedat de numele clasei
i operatorul rezoluiei sunt plasate n raza de aciune a modificatorului de acces necesar.
-

modificator_acces:
. . .
nume_cl::nume_mem;

- Trebuie de inut cont c gradul de acces poate fi mrit readucndu-l la acelai grad pe care la avut
n clasa de baz. Nu sunt permise gradaii de acces intermediare.
- De exemplu, fie clasa derivata, care este derivat din clasa baza:
-

class baza
{
public:
-

};
//-------class derivata : private baza
{
-

int id;

public:
-

int ib;

baza::ib;

};

- Membrul ib n clasa derivata va avea gradul de acces private i l readucem la gradul de


acces public, pe care l avea n clasa baza. Nu este posibil atribuirea gradului de acces intermediar
protected.
- 8. Specificul utilizrii funciilor n C++
- Caracteristicile impuse funciilor de limbajul C sunt, n mare parte, respectate i n cadrul
limbajului C++. Dar exist i o serie de caracteristici noi. Un mecanism important este posibilitatea de
suprancrcare a funciilor. Acest mecanism permite o eficien sporit la nivel de elaborare i ntreinere
a programelor.
- Funciile care urmeaz permit sumarea elementelor unui tablou de tip float i elementelor unui
tablou de tip int.
-

float suma1(float *tf, int n_e)


{
float s=0;
for (int i=0; i<n_e; i++)
s+=tf[i];
return s;
}
int suma2(int *ti, int n_e)
{
int s=0;
for (int i=0; i<n_e; i++)
s+=ti[i];
return s;
}

- Crearea de funcii cu denumiri diferite, dar care realizeaz algoritmi similari pentru diferite tipuri
ale parametrilor fictivi, poate genera probleme la nivel de apelare, fiindc trebuie apelat funcia corect
pentru combinaia dat de parametri reali. Este necesar atenie din partea programatorului, fiindc
apelarea funciei suma1() - pentru un tablou cu elemente ntregi, sau a funciei suma2() - pentru
un tablou cu elemente reale, va genera rezultate incorecte. O posibil simplificare pentru astfel de situaii

ar fi crearea de funcii cu acelai nume i transmiterea responsabilitii de determinare a variantei corecte


a funciei apelate n seama compilatorului. Exemplul anterior ar putea fi re-scris astfel:
-

float suma(float *tf, int n_e)


{
float s=0;
for (int i=0; i<n_e; i++)
s+=tf[i];
return s;
}
int suma(int *ti, int n_e)
{
int s=0;
for (int i=0; i<n_e; i++)
s+=ti[i];
return s;
}

- Crearea unor funcii ce au acelai nume va purta denumirea de suprancrcare a funciilor. Pentru a
putea fi suprancrcate, funciile trebuie s satisfac urmtoarele restricii:
s aib numr diferit de parametri fictivi;
dac numrul de parametri fictivi este acelai, tipul parametrilor fictivi trebuie s difere mcar ntr-o
singur poziie;
tipul returnat de funcie nu se ia n consideraie n procesul de suprancrcare.
- Compilatorul de fapt nu lucreaz cu numele fizice ale funciilor, ci creeaz nite denumiri noi,
numite signaturi ale funciilor. De exemplu, funciile
-

int produs(int i, int j)


{
...
}

float produs(float i, float j)


{
...
}

ar putea avea respectiv urmtoarele signaturi:

produs_ii

produs_ff

- Din cte se vede, signaturile funciilor conin informaie i despre tipul parametrilor fictivi, ceea ce
este foarte util la determinarea variantei corecte a funciei. Deci funciile cu acelai nume, adic
suprancrcate, au totui signaturi diferite. Doar aa este posibil o apelare a variantei corecte pentru
combinaia corespunztoare de parametri reali. Varianta corect a funciei este determinat din
confruntarea tipurilor parametrilor fictivi cu tipurile parametrilor reali. n fragmentul ce urmeaz sunt
apelate funciile cu nume suma(), definite anterior.
-

void main()
{
float tf[5]={1.2,4,3,3.4,0};
int ti[3]={1,2,7};
cout<<suma(tf,5)<<" "<<suma(ti,3);
}

- Desigur c pentru tabloul tf va fi apelat prima funcie, iar pentru tabloul ti va fi apelat
funcia a doua.
- n limbajele care admit suprancrcarea funciilor, un nume de funcie poate fi legat cu o mulime
de algoritmi posibili (poate fi suprancrcat de multe ori). Nu exist cerine stricte referitor la algoritmii
ce in de un nume de funcie i totui este de dorit ca algoritmii realizai s fie similari ca idee.
- Avantajele suprancrcrii funciilor sunt urmtoarele:

simplitatea de elaborare a algoritmilor, avnd un mecanism mai simplu de interaciune cu funciile;


simplitatea de ntreinere a codului elaborat, dat fiind o nelegere mai simpl a ideilor codificate.
- Mecanismul de suprancrcare a funciilor este i un generator posibil de erori, care de regul sunt
numite ambiguiti. Cel mai simplu este a descrie posibilele ambiguiti, prezentnd un exemplu. Fie
funcia f1(), definit cum urmeaz:
-

void f1(float f)
{
. . .
}
//----void f1(double d)
{
. . .
}

- n continuare este prezentat un fragment de cod n care sunt efectuate o serie de apelri ale funciei
date:
-

int i=7;
float f=1.2;
double d=-3.12;
. . .
f1(f);
f1(d);
f1(2.3);
f1(i);

- Prima apelare se refer la prima variant a funciei, avnd parametrul real de tip float.
Urmtoarele dou se refer la cea de-a doua variant, avnd parametrul real de tip double. n schimb,
ultima apelare va genera o ambiguitate, fiindc nu e clar care variant trebuie s fie apelat pentru un
parametru real de tip int.
- Deoarece constructorul unei clase este n ultim instan o funcie, lui i se poate aplica
mecanismul de suprancrcare i deci existena claselor care au mai muli constructori se datoreaz anume
posibilitii de suprancrcare. Drept rezultat o clas poate avea mai muli constructori. De asemenea, o
clas poate avea i metode suprancrcate.
- Un alt mecanism important este posibilitatea fixrii de valori implicite (predefinite) ale
parametrilor fictivi ai funciei se utilizeaz atunci, cnd setul de valori reale transmise la apelarea
funciei are o serie de poziii n care pot fi repetri ale valorilor. De exemplu, funcia afisareData()
-

void afisareData(int zi, int luna, int an)


{
-

cout<<zi <<. <<luna <<. <<an << endl;

poate fi apelat pentru afiarea unor zile de natere. Pentru studenii dintr-o grup exist o mare
probabilitate c valorile reale din poziia ce corespunde anului de natere vor fi egale. Pentru
valorile ce in de luna naterii, ansele de egalitate vor fi mai mici, dar tot exist. Fixarea valorilor
implicite duce la simplificarea expresiei de apelare a funciei.
- Procesul de fixare a valorilor implicite este ghidat de o serie de reguli:
- fixarea valorilor implicite pentru parametrii fictivi se face parcurgnd lista parametrilor fictivi de la
dreapta spre stnga;
- parametrii fictivi cu valori implicite formeaz o mulime compact, adic nu exist parametri fr valori
printre parametrii cu valori.
- Fixarea valorilor implicite poate fi fcut n dou circumstane:
- n antet, la definirea funciei;
- n prototipul funciei.
- Nu este permis fixarea valorilor i n antet, i n prototip. n schimb dac n antet nu sunt fixate
valori implicite, atunci diferite prototipuri declarate local pot avea diferite seturi de valori implicite. De
exemplu, funcia afisareData()cu valori implicite definite n antet la definirea funciei.
-

void afisareData(int zi,int luna=11,int an=2007)

{
-

cout <<zi <<. <<luna <<. <<an << endl;

- Exemplul ce urmeaz se refer la fixarea valorilor implicite n prototipul funciei. n dou funcii
diferite sunt declarate prototipurile funciei afisareData(), dar cu seturi diferite de valori implicite.
-

void afisare2000()
{
void afisareData(int zi, int luna, int an=2000);
. . .
}

void afisare1990()
{
void afisareData(int zi,int luna=1,int an=1990);
-

. . .

- La apelarea unei funcii parametrii reali transmii n funcie sunt parcuri de la stnga spre dreapta,
legndu-se cu parametrul fictiv corespunztor i transmindu-i valoarea sa. Valorile implicite ale
parametrilor fictivi sunt substituite de valorile parametrilor reali. Dac parametri reali sunt mai puini
dect parametri fictivi, atunci parametrii fictivi rmai ne-legai vor avea ca valori valorile implicite, dac
le au. n caz c nu le au, va fi generat eroare din insuficien de parametri reali. De exemplu, dac va fi
apelat funcia afisareData() din funcia afisare1990()
-

afisareData(7, 10, 2004);


afisareData(7, 10);

atunci cea de-a doua apelare va fi echivalent cu urmtoarea:

afisareData(7, 10, 1990);

unde n calitate de al treilea parametru real este luat valoarea implicit.


Mecanismul de suprancrcare a funciilor i fixarea valorilor parametrilor implicii pot s duc la
generare de ambiguiti. n continuare, prin intermediul exemplului care urmeaz, va fi prezentat
o posibil ambiguitate.

void f(int i1, int i2=0, int i3=0)


{
-

. . .

}
void main()
{
-

. . .

}
void f(int i)
{

. . .
f(1, 2, 3);
f(1, 2);
f(1);
. . .

//a)
//b)
//c)

- Funcia f() este suprancrcat, avnd dou variante: cu trei parametri i cu un parametru. n
funcia main() sunt trei variante de apel al funciei f(). Varianta a) va apela funcia f() cu trei
parametri. Varianta b), de asemenea, va apela funcia f() cu trei parametri, valoarea parametrului al
treilea fiind valoarea implicit. Pe cnd varianta c) - genereaz ambiguitate fiindc ar fi bun de apel i
funcia f() cu trei parametri, avnd dou valori implicite, dar i cea cu un singur parametru.
- Funciile membre ale unei clase, de asemenea, pot avea fixate valori implicite pentru parametrii
fictivi. Modul de fixare a valorilor implicite ale parametrilor fictivi pentru funciile membre nu se
deosebete de modul descris pentru funciile libere.

- Fiindc constructorii unei clase sunt de asemenea funcii, rezult c parametrii fictivi ai
constructorilor pot de asemenea avea fixate valori implicite. Un constructor cu parametri, dar care are
valori implicite pentru toi parametrii fictivi, este echivalent cu un constructor fr parametri. Acest enun
poate fi generalizat n felul urmtor: constructorul care are valori implicite pentru k parametri fictiv este
echivalent cu un constructor cu (n-k) parametri, unde n este numrul de parametri fictivi ai
constructorului. Fie clasa cl1 avnd constructor fr parametri
-

class cl1
{
-

public:
-

cl1();
.

};

i clasa cl2 avnd constructor cu un parametru, dar cu valoare implicit.

class cl2
{

cl1 ob1;

public:
-

.
cl2(int i=10);
.

};
cl2 ob21(1);
cl2 ob22;

- Din cele spuse anterior, clasa cl2 poate crea obiecte, utiliznd forma cu parametri i forma fr
parametri a constructorului.
- 9. Definirea i utilizarea referinelor
- Toate programele utilizate pn n prezent utilizau doar dou tipuri de variabile i anume variabile
obinuite i variabile de tip adres sau pointeri. Limbajul C++ mai adaug la aceste tipuri i variabile de
tip referin. O variabil de tip referin este precedat la definire de simbolul ampersend (&). Iat cum
este definit o referin,
-

tip & nume_var_ref=nume_var;

unde nume_var_ref este un identificator, care reprezint numele variabilei de tip referin, iar
nume_var este numele unei variabile, care trebuie s aib o definire anterioar referinei.
Aceast definire poate fi generalizat pentru cazul mai multor variabile, dup cum urmeaz:

tip &nume_var_ref1=nume_var1, . . .,
&nume_var_refk=nume_vark;

- Pentru a observa unele proprieti ale referinelor este propus un program n care sunt definite
referine i sunt efectuate o serie de operaii att asupra variabilelor simple ct i asupra variabilelor de tip
referin.
-

#include <iostream.h>
void main()
{
-

int i=10, j=20;


int &ri=i, &rj=j;
cout <<i<< <<ri<<endl;
i=j;
cout <<i<< <<ri<<endl;

Programul anterior va produce urmtoarele afiri:

10 10
20 20
40 40

ri+=rj;
cout <<i<< <<ri<<endl;

- Se observ c orice transformare efectuat asupra variabilei i se rsfrnge asupra referinei


corespunztoare ri i de asemenea orice transformare efectuat asupra referinei ri se rsfrnge asupra
variabilei corespunztoare i. De aici este tras concluzia c o referin este ca un nume nou pentru
variabila cu care este legat, adic ar fi ca un alias sau porecl. Crearea unui nume nou pentru o variabil
nu este un mare avantaj fiindc ar putea doar s genereze dificultate de a urmri ideea programului i ar
putea fi i o surs de erori.
- Totui referinele sunt de un real folos n cadrul funciilor i anume n calitate de parametri fictivi
i ca valoare returnat de funcie. Sunt cunoscute deja metodele de transmitere a parametrilor n funcie
prin valoare i prin adres. La aceste metode se mai altur i metoda de transmitere a parametrilor prin
referin. Pentru o comparare a acestor metode vor fi realizate trei funcii menite s schimbe cu locurile
valorile aflate n dou variabile.
- 1) Transmitere prin
- 2) Transmitere prin
- 3) Transmitere prin
valoare
adres
referin
-

void schimb1(
int a, int b)
{
int t=a;
a=b;
b=t;
}

void schimb2(
int*a, int*b)
{
int t=*a;
*a=*b;
*b=t;
}

void schimb3(
int&a, int&b)
{
int t=a;
a=b;
b=t;
}

Aceste funcii vor fi apelate n funcia main(), cu scopul realizrii operaiei de schimb a
valorilor variabilelor.

#include<iostream.h>
void main()
{
-

int a=30, b=60;


int &ra=a, &rb=b;
schimb1(a,b);
cout<<a<< <<b<<endl;
schimb2(&a,&b);
cout<<a<< <<b<<endl;
schimb3(ra,rb);
cout<<a<< <<b<<endl;

Programul dat va produce urmtoarele afiri:

30 60
60 30
30 60

- Urmrind afirile produse se poate trage concluzia c prima funcie face un schimb local, care nu
afecteaz nicicum variabilele a i b, iar funciile a doua i a treia realizeaz un schimb care afecteaz
valorile variabilelor a i b, precum i era necesar. Deci transmiterea parametrilor prin adres i prin
referin sunt asemntoare prin faptul c schimbarea valorii n cadrul funciei a unui parametru din lista
parametrilor fictivi se produce neaprat i asupra parametrului real transmis n funcie n locul acestui
parametru fictiv. Aadar parametrii fictivi de tip referin sunt asemntori cu parametrii de tip pointer.
Adic referinele sunt un fel de adrese.
- Pe de alt parte comparnd funciile se poate observa c modul de prelucrare a referinelor n
cadrul funciei este asemntor cu modul de prelucrare a variabilelor obinuite, adic nu sunt necesare

careva simboluri adugtoare ataate variabilelor ca n cazul pointerilor (*). Deci parametrii de tip
referin sunt asemntori att cu parametrii de tip adres ct i cu parametrii de tip valoare.
- n programul anterior funcia schimb3() a fost apelat utiliznd variabilele de tip referin ra i
rb, corespunztoare variabilelor a i b:
-

schimb3(ra,rb);

Poate fi realizat un apel echivalent al funciei, utiliznd direct variabilele a i b dup cum
urmeaz.

schimb3(a,b);

- Un exemplu interesant de funcie este o funcie care are un parametru fictiv de tip referin i
returneaz o valoare de tip referin. Fie urmtorul tip de funcie:
-

int patrat(int i)
{
-

i=i*i;
return i;

Aceast funcie se comport bine n partea dreapt a unei egaliti returnnd ptratul unui numr.
De exemplu:

int k=12;
j=patrat(k);

Plasarea unei astfel de funcii n partea stng a egalitii ar genera o eroare.

patrat(k)=j;

n continuare funcia anterioar este schimbat puin, utiliznd referine, i este obinut
urmtoarea funcie:

int& patrat(int&i)
{
-

//eroare

i=i*i;
return i;

- Plasat n partea dreapt a egalitii ea de asemenea returneaz ptratul unei valori, ins ea capt
proprietatea neateptat de a putea fi plasat i n partea stng a egalitii:
-

j=25;
patrat(k)=j;

//corect

Drept rezultat valoarea 25 din variabila j va fi plasat n variabila k. Astfel de proprietate poate
fi de un real folos n procesul de suprancrcare a unor operatori.
- Exist o serie de restricii de utilizare a referinelor:
1) Referina trebuie iniializat la definire i rmne neschimbat pe parcursul execuiei programului;
2) Nu poate fi determinat adresa unei referine;
3) Referinele pot fi comparate, dar nu la nivel de adrese, ci la nivel de valori asociate cu ele.
- n continuare va fi menionat o posibil eroare legat de referine i care poart denumirea de
pierdere a referinei. O astfel de eroare se produce atunci cnd tipul referinei nu coincide cu tipul
variabilei cu care este asociat referina:
-

tip1 var;
tip2 &r=var;
tip1tip2;

n baza ideii anterioare este propus urmtorul exemplu:

int i=10;
int &ri=i;
long j=10;
int &rj=j;
cout <<i<< <<ri<<endl;
cout <<j<< <<rj<<endl;
i++;
j++;

cout <<i<< <<ri<<endl;


cout <<j<< <<rj<<endl;

Fragmentul dat va produce urmtoarele afiri:

10
10
11
11

10
10
11
10

De aici se observ c variabilele i i ri, fiind definite corect, sunt asociate i se schimb sincron,
pe cnd valorile variabilelor j i rj arat c ele de fapt nu sunt asociate, deci referina rj nu este
legat corect de variabila j.
- 10. Tablouri de obiecte. Pointeri i referine la obiecte. Pointeri la membrii clasei
- Deoarece clasele sunt tipuri de date abstracte, n baza lor pot fi create tablouri de obiecte ntr-un
mod similar cu crearea tablourilor n baza unor tipuri predefinite. Pentru tipuri predefinite exist dou
descrieri care permit creare de tablouri, i anume, fr iniializarea elementelor:
-

tip_pr nume_tab[dim];

i cu iniializarea elementelor:

tip_pr nume_tab[dim]={val1, val2, ..., valk};

- Cu toate c descrierile pentru tablouri de obiecte sunt asemntoare, totui pentru cazul claselor,
trebuie aduse unele concretizri. Vor fi distinse trei posibiliti, care corespund tipului constructorului din
clas.
- a) Dac clasa are fie constructor implicit, fie constructor fr parametri, fie constructorul poate fi
redus la un constructor fr parametri, atunci pentru crearea unui tablou de obiecte va fi utilizat o
descriere echivalent cu descrierea fr iniializare:
-

nume_cl nume_to[dim];

unde nume_cl este numele clasei n baza creia este creat tabloul de obiecte, nume_to
reprezint denumirea tabloului de obiecte i dim este dimensiunea tabloului.
- b) Dac clasa are un constructor cu un parametru sau constructorul poate fi redus la un constructor
cu un parametru, atunci pentru crearea unui tablou de obiecte va fi utilizat o descriere echivalent cu
descrierea cu iniializare:
-

nume_cl nume_to[dim]={val1, val2, ..., valk};

unde val1, val2, , valk reprezint valorile n baza crora lucreaz constructorul, crend
obiectele.
- c) Dac ns constructorul are mai mult de un parametru fictiv, se obine cazul general de descriere
a tabloului de obiecte
-

nume_cl nume_to[N]={nume_cl(pr11,,pr1k),,
nume_cl(prN1,,prNk)};

De exemplu, dac considerm c clasa carte are constructorul

carte(char *autor, char *titlu, int an);

atunci un tablou cu cinci obiecte va fi definit astfel

carte colectie[5]={
carte(H.Schildt,C++ manual complet,1997),
carte(L.Negresu,Limbajele C i C++,2000),
carte(B.Stroustrup,The C++ Programming Language,1997),
carte(K.Jamsa,Succes cu C++,1997),
carte(D.Somnea,Initiere in C++,2006)
};

- Cazul constructorului cu un singur parametru, de asemenea, se ncadreaz n cazul general, scriind


astfel:
-

nume_cl nume_to[dim]={nume_cl(val1),nume_cl(val2)
,,nume_cl(valk)};

dar desigur c aceast variant este mai greoaie ca varianta b).


- Utilizarea denumirii constructorului pentru descrierea fiecrui obiect genereaz o cale anevoioas
de creare, mai ales, cnd este necesar crearea unui tablou cu un numr mare de obiecte. Cnd se

intenioneaz a lucra cu tablouri mari de obiecte, atunci, de regul, clasa n baza creia se creeaz
tablourile va trebui s conin i un constructor fr parametri sau unul care se reduce la un constructor
fr parametri, pentru a evita descrieri pentru fiecare element al tabloului. De exemplu, considernd c
clasa punct are un constructor cu valori implicite
-

punct(int x=0, int y=0);

este posibil de a defini un tablou cu o sut de elemente utiliznd urmtoarea descriere:

punct puncte[100];

- Accesarea unui element al tabloului de obiecte se realizeaz utiliznd un indice ntreg


-

nume_to[ind]

iar pentru a accesa un membru al unui element al tabloului de obiecte este utilizat urmtoarea
expresie

nume_to[ind].nume_membru

indicele ind satisfcnd condiia

0 ind dim-1

unde dim este dimensiunea tabloului de obiecte.


- Deoarece obiectele sunt variabile create n baza unor clase, exist posibilitatea de a accesa nu doar
coninutul lor, ci i adresele lor. Operarea cu adresele obiectelor se face prin intermediul pointerilor la
obiecte. Un pointer la obiecte de un anumit tip este definit astfel:
-

nume_cl *nume_var_po;

unde nume_cl determin tipul obiectelor cu care poate opera pointerul dat. Operatorii admisibili
n contextul pointerilor sunt admisibili i n contextul pointerilor la obiecte.
- 1) operatorul = care permite plasarea unei adrese ntr-un pointer

nume_var_po = expresie;

unde valoarea expresiei din dreapt a operatorului de atribuire = reprezint o adres. Adeseori
aceast expresie este exprimat prin adresa unui obiect, utiliznd operatorul &

nume_var_po = &nume_ob;

sau prin adresa de nceput al unui tablou de obiecte

nume_var_po = nume_to;

sau prin valoare din alt pointer

nume_var_po = nume_var_po1;

- 2) avnd ntr-un pointer adresa unui obiect, poate fi realizat accesul la un membru al obiectului
prin operatorul ->
-

nume_var_po -> nume_membru

- 3) avnd ntr-un pointer adresa unui obiect, poate fi citit coninutul obiectului prin operatorul *
-

nume_ob = *nume_var_po;

- 4) dac ntr-un pointer este adresa unui obiect, atunci incrementnd valoarea dat cu operatorul ++
n pointer se va obine adresa obiectului urmtor:
-

nume_var_po++;

- 5) dac ntr-un pointer este adresa unui obiect, atunci decrementnd valoarea dat cu operatorul -n pointer se va obine adresa obiectului anterior:
-

nume_var_po--;

- 6) dac ntr-un pointer este adresa unui obiect, atunci incrementnd valoarea dat cu o valoare
natural n, utiliznd operatorul += n pointer se va obine adresa obiectului al n-lea urmtor:
-

nume_var_po+=n;

- 7) dac ntr-un pointer este adresa unui obiect, atunci decrementnd valoare dat cu o valoare
natural n, utiliznd operatorul -= n pointer se va obine adresa obiectului al n-lea anterior:
-

nume_var_po-=n;

- Operatorii aritmetici anteriori, fiind aplicai unui pointer, schimb valoarea din pointer. Pot fi
aplicai operatorii + i - fr a schimba valoarea din pointer
-

nume_var_po + n

sau

nume_var_po - n

I.
II.

obinnd adresa obiectului al n-lea urmtor sau anterior.


- n contextul pointerilor la obiecte pot fi aplicai, de asemenea, i operatorii relaionali >, <, >=, <=,
==, !=.
- La aplicarea operatorului de atribuire are loc controlul riguros al tipului pointerului din partea
stng a operatorului de atribuire i tipul expresiei din partea dreapt. Operaia de atribuire este efectuat
doar la satisfacerea corespondenei pointerilor. Corespondena pointerilor este satisfcut n urmtoarele
condiii:
pointerul din stnga operatorului de atribuire este de acelai tip ca i valoarea din partea dreapt;
dac pointerul din stnga i valoarea din dreapta sunt de tipuri diferite, atunci sunt admise doar tipuri
inrudite, adic clas de baz clas derivat. Tipul din stnga operatorului de atribuire este de tip clas
de baz, iar tipul din dreapta este de tip clas derivat.
- De exemplu, dac se presupune existena unor obiecte i pointeri de tip clas de baz i de tip
clas derivat:
-

cl_baza *pob, ob;


cl_d *pod, od;

atunci vor fi valabile urmtoarele atribuiri:

pob = &ob;
pod = &od;
pob = pod;

i vor fi inadmisibile urmtoarele:

pod = pob;
pod = &ob;

// corect
// corect
// corect
// incorect
// incorect

- n contextul claselor i al obiectelor, exist un pointer predefinit numit this, care indic adresa
obiectului curent. Obiectul curent este obiectul care efectueaz operaia de apel a unei funcii. La
implementarea unei clase n orice funcie membr a clasei, care nu este static, poate fi utilizat pointerul
this. Funciile statice nu se bucur de aceast proprietate. De exemplu, constructorul clasei punct
poate fi realizat n felul urmtor:
-

punct(int x=0, int y=0)


{
this->x=x;
this->y=y;
}

- Specificul acestui constructor const n faptul c numele parametrilor fictivi x i y coincid cu


numele membrilor clasei x i y. Pentru a face delimitare ntre parametrii fictivi i membrii clasei,
numele membrilor clasei sunt precedate de pointerul this conectat prin operatorul ->.
- Orice membru al clasei curente poate fi accesat prin intermediul pointerului this, precednd
numele membrului cu pointerul this. Dac nu sunt confuzii de nume, ca n cazul constructorului
precedent, pointerul this poate fi omis fr careva pierderi de sens.
- Totui, sunt situaii cnd nu poate fi ocolit utilizarea pointerului this. De exemplu, pointerul
this va fi utilizat dac funcia membru returneaz adresa obiectului curent, cum este artat n descrierea
ce urmeaz:
-

nume_cl *nume_fm(lista_pf)
{
. . .
return this;
}

Va fi o situaie similar dac funcia membru returneaz obiectul curent, ceea ce este reprezentat
n urmtoarea descriere:

nume_cl nume_fm(lista_pf)
{
. . .
return *this;
}

Dac funcia membru utilizeaz obiectul curent pentru anumite procesri, va fi, de asemenea,
necesar utilizarea pointerului this:

tip_r nume_fm(lista_pf)
{
. . .
nume_cl ob = *this;
. . .
}

- n procesul elaborrii de algoritmi pot fi utilizate i referine la obiecte. Definirea unei referine de
tip obiect poate fi realizat astfel:
-

nume_cl &nume_vr = nume_ob;

unde nume_vr este numele variabilei de tip referin, iar nume_ob este denumirea obiectului
n baza cruia este definit referina.
- Referinele la obiecte se folosesc des anume n calitate de parametri fictivi ai unor funcii i n
calitate de valoare returnat a funciei. Referinele la obiecte ne dau eficien din punct de vedere a
utilizrii memoriei. Referinele se utilizeaz des n constructorii de copiere, n procesul de suprancrcare
a operatorilor, mai ales a celor de intrare/ieire.
- O noiune nou n contextul claselor sunt pointerii la membrii clasei. Ei nu sunt nite adrese, ci
mai degrab nite deplasamente n raport cu originea clasei. Fiindc membrii clasei sunt de dou tipuri,
adic date membre i funcii membre, de dou tipuri vor fi i pointerii la membrii clasei.
a) Pointeri la date membre.
- Un pointer la date membre poate fi definit utiliznd urmtoarea descriere:
-

tip nume_cl::*nume_pmd;

unde nume_pmd este identificatorul care reprezint numele pointerului, nume_cl este numele
clasei a crui membru va fi legat cu pointerul, iar tip descrie tipul pointerului. Tipul pointerului
coincide cu tipul membrului cu care va fi legat. A defini un pointer nu nseamn a-l i iniializa.
Pentru iniializare este utilizat operatorul & n felul urmtor:

nume_pmd=&nume_cl::nume_md;

- Pentru a accesa un cmp al unui obiect folosind pointer la membrul clasei este utilizat operatorul

.*
-

nume_ob.*nume_pmd

unde nume_ob este obiectul al crui membru este accesat, iar nume_pmd este pointer la
membrul dat al clasei.
- Dac este utilizat nu numele unui obiect, ci un pointer la obiect, atunci operatorul .* va fi
nlocuit cu operatorul ->* dup cum urmeaz:
-

nume_po->*nume_pmd

Pentru a exemplifica utilizarea pointerilor la membrii clasei de tip date, va fi rescris clasa
punct, declarnd toi membrii publici:

class punct
{
public:
int x;
int y;
punct(int x=0, int y=0);
void afisare();
void stingere();
void miscare(int xn, int yn);
};

Acum poate fi definit un pointer la datele membre x i y ai clasei punct:

int punct::*pxy;
//definire pointer la membru
pxy=&punct::x;
//initializare
punct p(100,200),*pp; //definire obiect, pointer
pp = &p;
p.*pxy=150; //schimbare valoarea din 100 in 150

pxy=&punct::y;
//schimbare valoare pointer
pp->*pxy=150; //schimbare valoarea din 200 in 150

b) Pointeri la funcii membre.


- ntruct descrierea unei funcii const din mai multe elemente, tot aa i definirea pointerilor la
funcii membre va fi diferit de cazul datelor membre, realizndu-se astfel:
-

tip (nume_cl::*nume_pmf)([tip1 pf1[, tip2 pf2


[, ,tipk pfk]...]]);

unde nume_pmf este numele pointerului la funcii membre, dup el urmnd descrierea
parametrilor fictivi.
- Iniializarea pointerului la funcii membre este similar cu iniializarea pointerului la date membre:
nume_pmf=&nume_cl::nume_mf;

unde nume_mf este numele funciei membre.


- Apelarea funciei membre prin intermediul pointerului la funcia membr este realizat prin
intermediul operatorului .* astfel
-

(nume_ob.*nume_pmf)(pr1, pr2, ,prk)

Exemplificarea va fi fcut tot n baza clasei punct.

void (punct::*pas)();
pas=&punct::afisare;
punct p(100,200);
(p.*pas)();
//apelarea functiei afisare
pas=&punct::stingere;
punct p(100,200);
(pas.*pas)();
//apelarea functiei stingere

- Dac mai multe funcii membre au acelai prototip, atunci putem avea un pointer de tip pointer la
membrul clasei care poate fi folosit cu toate funciile respective.
- 11. Dirijarea dinamic a memoriei
- Toate posibilitile de dirijare dinamic a memoriei existente n limbajul C sunt valabile i n
limbajul C++. Este vorba de funciile malloc(), calloc(), realloc(), free(). Iat prototipurile
acestor funcii:
-

void
void
void
void

*malloc(size_t dim_loc);
*calloc(size_t num_el, size_t dim_el);
*realloc(void *adr_loc, size_t dim_nou);
free(void *adr_loc);

- Limbajul C++ mai are i posibiliti proprii ncorporate n limbaj sub forma de operatori care
permit, de asemenea, dirijarea dinamic a memoriei.
- Pentru alocare de memorie este utilizat operatorul new. Modul de utilizare a operatorului new
poate fi descris astfel:
-

tip *adr_locatie;
. . .
adr_locatie=new tip; //aloca memorie de tipul dat

unde adr_locatie este un identificator care reprezint numele pointerului n care este plasat
adresa locaiei de memorie alocat, utiliznd operatorul new, iar tip determin la ce tip de date
va fi atribuit locaia dat de memorie. Dac operatorul new se execut cu succes, valoarea din
pointerul adr_locatie va fi adresa locaiei de memorie alocat, n caz contrar, n pointer va fi
plasat o valoare nul, care poate fi exprimat prin constanta simbolic NULL. Iat un exemplu:

int *adri;
adri=new int;

- n procesul alocrii de memorie n locaia dat poate fi plasat i o valoare iniial. Modul de
aplicare a operatorului new se schimb puin dup cum urmeaz:
-

tip *adr_locatie;
. . .
adr_locatie=new tip(val); // alocare cu initializare

- De exemplu:
-

float *Pi;
Pi = new float(3.14);

ceea ce este echivalent cu:

float *Pi;
Pi= new float;
*Pi = 3.14;

- Ideile expuse anterior sunt aplicabile integral tipurilor predefinite de date. n cazul tipurilor
abstracte de date (clase) definite de programator trebuie de inut cont c la crearea oricrui obiect
particip i constructorul clasei. De aceea n continuare este propus o descriere care generalizeaz
descrierile anterioare, obinnd o descriere aplicabil n contextul claselor:
-

nume_cl *adr_obiect;
adr_obiect = new nume_cl[(lista_par_reali)];

unde lista_par_reali reprezint parametrii reali transmii constructorului pentru a putea


ndeplini aciunile de creare a obiectului i din cte se vede este un element opional. Lista
parametrilor reali poate lipsi atunci cnd clasa nu are constructor, sau are constructor fr
parametri, sau constructorul poate fi redus la un constructor fr parametri. De exemplu, lund ca
baz clasa punct, poate fi adus urmtorul exemplu:

punct *pp;
pp=new punct(112,37); //adresa obiect in pointer pp
pp->afisare();

- Pentru a elibera spaiul alocat pentru o variabil de anumit tip, este utilizat operatorul delete.
Forma de aplicare a operatorului este urmtoarea:
-

delete adr_locatie;

unde variabila adr_locatie conine adresa locaiei de memorie alocat cu ajutorul


operatorului new. Dac este eliberat spaiul alocat pentru un obiect, va fi apelat mai nti
destructorul clasei respective. Pentru a elibera memoria alocat, n exemplul referitor la clasa
punct va fi scris astfel:

delete pp;

- n afar de posibilitatea de alocare a spaiului pentru o singur variabil de anumit tip, exist
posibilitatea de a aloca spaiu de memorie pentru un tablou de elemente de anumit tip. n acest scop, va fi
utilizat urmtoarea descriere:
-

tip *adr_locatie;
. . .
adr_locatie = new tip[dim]; //alocare memorie tablou

unde adr_locatie va conine adresa spaiului alocat tabloului de elemente. Ca exemplu va fi


alocat spaiu pentru 10 elemente de tip double:

double *adrd;
adrd = new double[10];

- Vom meniona faptul c la alocarea de memorie pentru tablouri nu poate fi efectuat concomitent
i iniializarea elementelor, ca n cazul unei singure variabile. De acest fapt trebuie de inut cont mai ales
la crearea tablourilor de obiecte, fiindc crearea unui obiect apelnd un constructor cu un anumit set de
parametri reali ar nsemna o iniializare a obiectului. i fiindc la crearea tablourilor nu este posibil
apelarea unui constructor cu parametri, clasa respectiv neaprat va trebui s conin un constructor fr

parametri sau un constructor care se reduce la un constructor fr parametri. Pentru clasa punct este
posibil crearea unui tablou de 20 obiecte:
-

punct *tabp;
tabp = new punct[20];

- Eliberarea spaiului de memorie ocupat de un tablou de elemente se realizeaz cu ajutorul


operatorului delete[] scriind astfel:
-

delete[] adr_locatie;

I.
II.
III.

unde adr_locatie conine adresa tabloului de elemente.


- n cazul tipurilor de date simple, rezultatul aciunii operatorului delete i al operatorului
delete[] poate fi acelai, ns n cazul tablourilor de obiecte sunt cazuri cnd rezultatul aciunilor lor
nu sunt echivalente. La execuia operatorului delete[] pentru un tablou de obiecte va fi apelat
destructorul pentru fiecare element al tabloului, ceea ce va genera o eliberare corect a memoriei ocupate.
La execuia operatorului delete pentru un tablou de obiecte va fi apelat destructorul doar pentru
primul element al tabloului i deci poate fi generat o eliberare incomplet a resurselor folosite de obiecte.
- Operatorii new i delete au o serie de avantaje n comparaie cu funciile de dirijare dinamic
a memoriei:
operatorii new i delete pot fi suprancrcai schimbnd aciunea lor n cadrul unor clase;
n procesul de alocare a memoriei nu sunt necesare anumite conversii ale tipurilor de date;
nu sunt necesare anumite calcule preliminare pentru a determina dimensiunea fragmentului de memorie.
- Iat cum este alocat memoria pentru un tablou de 10 elemente de tip int cu ajutorul funciei
malloc()
-

int *ai;
ai = (int *)malloc(sizeof(int)*10);

n continuare, pentru comparaie, este efectuat acelai lucru utiliznd operatorul new:

int *ai;
ai = new int[10];

- Vom remarca faptul c dac se folosesc funciile malloc(), calloc(), realloc() pentru
alocare de memorie, atunci se utilizeaz doar funcia free() pentru eliberarea memoriei, iar dac este
folosit operatorul new pentru alocare, atunci eliberarea memoriei se face doar cu operatorul delete.
Nu se permite combinarea de diferite mecanisme la nivel de alocare i eliberare a memoriei.
- 12. Constructor de copiere
- Orice clas chiar dac nu are un constructor de copiere definit explicit, are unul care este un
constructor de copiere creat implicit de ctre compilator. Constructorul de copiere implicit face copierea
binar cu binar a informaiei dintr-un obiect crend un obiect nou. Dar un astfel de mecanism de obinere a
obiectelor-copii poate duce la erori grave. Acest fapt se poate ntmpla cnd clasa, n baza creia sunt
ob1 puncteaz la careva fragmente de memorie alocate dinamic. Dac vor
create obiecte, conine pointeri care
fi create copii ale unui obiect de acest tip prin metoda constructorului de copiere implicit, atunci vor
Memorie dinamic asociat obiectului
exista mai multe obiecte
pointer care au asociate acelai fragment de memorie:
ob2
pointer
-

- Dac unul dintre aceste obiecte va fi nimicit, fragmentul de memorie asociat va fi eliberat i atunci
obiectele rmase vor arta la un fragment de memorie inexistent. Dac vor fi efectuate operaii n cadrul
acestui fragment de memorie, ele vor genera erori.
- Constructorul de copiere este apelat n urmtoarele situaii posibile:
1.
Definirea unei variabile de tip obiect, iniializnd-o n baza unui obiect existent;
2.
Transmiterea de obiecte n calitate de parametri reali ai unei funcii care are parametri fictivi de
tip obiect. Fiecare obiect va genera o copie, creat cu ajutorul constructorului de copiere.
3.
Funciile care returneaz o valoare de tip obiect n momentul returnrii valorii creeaz o copie a
obiectului.
- Erori de tipul celei descrise anterior pot fi obinute uor, de exemplu, la transmiterea unui obiect n
calitate de parametru real la apelarea unei funcii. Va fi creat un obiect-copie, care la terminarea funciei
va fi nimicit. Astfel, obiectul transmis n calitate de parametru real va fi afectat serios.
- Pentru a evita asemenea erori trebuie de definit n cadrul clasei un constructor de copiere, care ar
schimba scenariul de lucru al constructorului de copiere implicit. i anume, obiectul-copie ar trebui s-i
aib propria memorie asociat, care conine informaie similar cu memoria asociat obiectului, n baza
cruia este creat copia:
ob1
Memorie dinamic asociat obiectului
pointer
ob2
Memorie dinamic asociat obiectului
pointer
- Un constructor de copiere are un parametru fictiv de tip referin i este descris n felul urmtor:
-

class nume_cl
{
. . .
nume_cl(nume_cl &ob);
. . .
};

iar implementarea lui poate fi descris astfel:

nume_cl::nume_cl(nume_cl &ob)
{
// instructiuni
}

- Exemplu:
-

class tablou

{
-

public:
-

dim=d;
for(int i=0;i<dim;i++) date[i]=val;
}
else

dim=0;
}
//----------------tablou::tablou(tablou &t)
{
-

dim=t.dim;
date=new int[dim];
for(int i=0;i<dim;i++) date[i]=t.date[i];

}
//----------------tabou::~tablou()
{
delete[] date;
}
//----------------void tablou::modificaTablou()
{
date[0]=date[0]+11;
// mareste primul element cu 11
}
//----------------void tablou::afisare()
{
for(int i=0;i<dim;i++) cout<<date[i]<< ;
cout<<endl;
}
//----------------void main()
{
-

date=new int[d];
if(date!=NULL)
{

tablou(int d, int val);


tablou(tablou &t);
~tablou();
void modificaTablou();
void afisare();

};
//----------------tablou::talbou(int d, int val)
{
-

int *date;
int dim;

tablou t1(15,0), t2(25,1);


t1.afisare();
t2.afisare();
tablou t3(t2);
t3.modificaTablou();
t3.afisare();

- Deoarece, n acest exemplu, este creat un constructor de copiere explicit (corect), orice schimbri
asupra obiectului t3 nu afecteaz obiectul t2 i invers.
- 13. Funcii prietene i clase prietene
- Clasele sunt proiectate n corespundere cu principiile caracteristice tipurilor abstracte de date. Ele
au o serie de proprieti, care, de regul, sunt nchise pentru lumea exterioar i o serie de operaii care
determin funcionalitatea tipului dat i care permit i accesul la proprietile tipului dat. Atunci cnd este
necesar un proces rapid de prelucrare, apelarea multor funcii ncetinete procesul de execuie i pentru
sistemele de execuie n timp real nu este efectiv.
- Dac ar fi proiectate clasele vector i matrice, ar putea fi necesar i operaia produs
matrice-vector. Dac nu este logic definirea acestei operaii ca operaie intern a uneia dintre clase,
atunci ea va fi definit ca o funcie extern. Schematic aceast situaie poate fi reprezentat astfel:
-

class vector
{
. . .
int el_v[100];
int dim;
. . .
public:
. . .
int element_v (int ind);
void sc_elem_v (int ind, int v_n);
int dimensiune_v();
. . .
};
//-------------class matrice
{
. . .
int el_m[100][100];
int dim;
. . .
public:
. . .
int element_m (int ind1, int ind2);
void sc_elem_m (int ind1, int ind2, int v_n);
int dimensiune_m();
. . .
};
//-------------//functie externa care realizeaza
//produsul matrice-vector
vector prod_m_v (matrice m, vector v)
{
vector vr;
int i, j, s;
. . .
for (i=0;i<v.dimensiune_v();i++)
{
s=0;
for(j=0;j<v.dimensiune_v();j++)
s+=m.element_m(i,j) * v.element_v(j);
vr.sc_elem_v(i,s);
}
. . .
return vr;
}

- Din definirea funciei prod_m_v() se poate vedea c ea conine foarte multe apeluri de funcie,
ceea ce sigur mrete timpul de execuie.
- Deoarece n unele situaii poate fi necesar un acces mai rapid la unele resurse ale clasei, s-a
convenit a permite, n unele situaii, unor funcii externe acces direct la membrii privai sau protejai ai
clasei date. Astfel de funcii sunt numite funcii prietene ale clasei date. Pentru ca o funcie s fie
recunoscut drept funcie prieten, ea trebuie s fie declarat prieten n interiorul clasei. Descrierea este
nfptuit cu ajutorul cuvntului-cheie friend. Nu are importan locul de plasare a descrierii.
Descrierea corespunztoare poate fi plasat n cmpul de aciune a oricrui modificator de acces:
-

class nume_cl
{
. . .
friend tipr nume_func_pr(lista_par_fictivi);
. . .
};

- Definirea funciei se face n mod obinuit ca orice alt funcie extern clasei. Modificatorul
friend nu este plasat n antetul funciei la definire:
-

tipr nume_func_pr(lista_par_fictivi)
{
. . .
//instructiuni
. . .
}

- Dac descrierea
-

friend vector prod_m_v (matrice m, vector v);

va fi plasat n declarrile claselor vector i matrice, atunci funcia prod_m_v() va


deveni prieten a claselor date. Dac este declarat drept funcie prieten, atunci poate fi simplificat
modul ei de realizare obinndu-se acces direct la toi membrii claselor. Dup ce o declarm funcie
prieten, ea va avea urmtoarea realizare:
-

//functie externa care realizeaza


//produsul matrice-vector
vector prod_m_v (matrice m, vector v)
{
vector vr;
int i, j;
. . .
for (i=0; i<v.dimensiune_v(); i++)
{
vr.el_v[i] = 0;
for (j=0; j<v.dimensiune_v(); j++)
vr.el_v[i] += m.el_m[i][j] * v.el_v[j];
}
. . .
return vr;
}

- O funcie prieten unei clase poate fi funcie membr a altei clase. La declararea ei ca funcie
prieten numele ei este precedat de numele clasei din care face parte:
-

class nume_cl
{
. . .
friend tipr nume_clasa::nume_fp(lista_pf);
. . .
};

- Atunci cnd toate funciile membre ale unei clase sunt prietene a altei clase, este obinut noiunea
de prietenie a claselor, adic prima clas este prieten cu cea de-a doua.
-

class B
{

. . .
// membri
. . .
};
class A
{
. . .
friend class B;
. . .
};

- Relaia de prietenie n cadrul claselor nu este una comutativ. Adic dac clasa A este prieten cu
B, aceasta nu nseamn c clasa B este prieten cu A.
- Tot aa ea nu este una tranzitiv. Dac clasa A este prieten cu B i clasa B este prieten cu C,
aceasta nu nseamn c clasa A este prieten cu C.
- 14. Suprancrcarea operatorilor
- Un tip de date este caracterizat nu doar prin domeniul de valori admisibile, dar i printr-o serie de
operaii care sunt permise asupra lor, numit funcionalitatea tipului. n cadrul tipurilor predefinite
operaiile sunt exprimate prin operatori, ceea ce nu este valabil pentru tipurile abstracte de date create de
programator. n cadrul tipurilor abstracte de date funcionalitatea este exprimat prin funcii membre. ns
expresiile scrise n baz de operatori sunt mai simple i pot fi mai uor citite, comparativ cu cele scrise n
baz de funcii. De exemplu, expresia:
-

C+A*B

exprimat prin operatori este mult mai simpl dect expresia:

C.Adunare(A.Produs(B))

exprimat prin funcii.


- Pentru a extinde posibilitatea de utilizare a unui operator n contextul unor obiecte, este necesar de
a realiza procesul de suprancrcare a operatorului dat n cadrul clasei. O suprancrcare a unui operator n
interiorul unei clase nseamn a-i da un anumit sens operatorului vizat n cadrul clasei date, pstrndu-i
sensul sau semnificaia pentru celelalte tipuri de date.
- Suprancrcarea operatorilor este legat de suprancrcarea funciilor, deoarece procedeul de
suprancrcare a operatorilor se realizeaz n baza funciilor.
- Suprancrcarea operatorilor se face prin urmtoarele dou metode:
- suprancrcarea ca funcii membre;
- suprancrcarea ca funcii prietene.
- Unii operatori pot fi suprancrcai prin ambele metode, iar alii pot fi suprancrcai doar printr-o
singur metod. De exemplu, operatorul aditiv + poate fi suprancrcat prin ambele metode. Pe cnd
operatorul indice [] poate fi suprancrcat doar prin metoda funciilor membre, iar operatorul de
extragere >> poate fi suprancrcat doar prin metoda funciilor prietene.
- n procesul de suprancrcare a operatorilor sunt respectate o serie de restricii:
suprancrcarea nu poate schimba aritatea operatorului, adic dac operatorul este unar sau binar, atunci
dup suprancrcare el va fi respectiv tot unar sau binar;
suprancrcarea nu poate schimba precedena operatorilor;
funciile prin intermediul crora se face suprancrcarea nu pot avea valori implicite.
- Pot fi suprancrcai toi operatorii cu excepia urmtorilor patru:
?: operatorul condiional ternar;
:: operatorul rezoluiei;
. operatorul punct de acces la membrii clasei;
.* operatorul de acces la membrii clasei prin intermediul pointerilor la membrii clasei.
- n continuare va fi caracterizat procedeul de suprancrcare a operatorilor prin metoda funciilor
membre. Particularitatea principal este faptul c funcia membr, prin intermediul creia va fi efectuat
suprancrcarea unui operator, va fi o funcie cu un nume special format prin utilizarea cuvntului-cheie

operator. n rest este o funcie membr obinuit i deci va fi descris i definit ca i oricare alt
funcie membr a clasei:
-

class nume_cl //orice operator supraincarcabil


{
. . .
tip_r operator (lista_pf);
. . .
}
. . .
tip_r nume_cl :: operator (lista_pf)
{
//instructiuni
}
. . .

unde este numele operatorului suprancrcat, iar lista_pf reprezint lista parametrilor
fictivi ai funciei. Lista parametrilor fictivi conine, de regul, un parametru pentru operatorii
binari sau este vid pentru operatori unari. Acest moment se datoreaz faptului c orice funcie
membr ne-static are acces la pointerul intern this. Astfel, pentru un operator binar primul
operand este obiectul care apeleaz funcia membru, iar al doilea operand este reprezentat de
parametrul care substituie parametrul fictiv. Pentru un operator unar operandul este obiectul care
apeleaz funcia membru. Prin pointerul this exist acces anume la obiectul care apeleaz
funcia membru.

Exemplu. De alctuit un program n care este implementat clasa complex, definind


operatorii caracteristici numerelor complexe prin suprancrcarea operatorilor prin metoda funciilor
membre.
-

#include <iostream.h>
#include <iomanip.h>
class complex
{
float r,i;
public:
complex (float r1=0, float i1=0)
{ r=r1; i=i1; }
complex operator + (complex);
complex operator - (complex);
complex operator * (complex);
complex operator / (complex);
void afisare ();
};
complex complex :: operator + (complex c)
{
complex s;
s.r = r + c.r;
s.i = i + c.i;
return s;
}
complex complex :: operator - (complex c)
{
complex s;
s.r = r - c.r;
s.i = i - c.i;
return s;
}

complex complex :: operator * (complex c)


{
complex p;
p.r = r*c.r i*c.i;
p.i = r*c.i + i*c.r;
return p;
}
complex complex :: operator / (complex c)
{
complex r;
r.r = (c.r*r + c.i*i)/(c.r*c.r + c.i*c.i);
r.i = (c.r*i r*c.i)/(c.r*c.r + c.i*c.i);
return r ;
}
void complex :: afisare ()
{
cout << r << setiosflafs(ios :: showpos)
<< i << "i"
<< resetiosflags (ios :: showpos);
}
void main ()
{
-

complex c1(7, -2.3), c2(3, 4.9);


complex s, d, i, c;
s = c1. operator + (c2);
s = c1 + c2;
d = c1 - c2;
i = c1 * c2;
c = c1 / c2;
s.afisare ();
d.afisare ();
i.afisare ();
c.afisare ();

- 15. Suprancrcarea operatorilor prin funcii prietene


- Suprancrcarea prin intermediul funciilor prietene este necesar n unele situaii pentru eficien,
iar n unele situaii concrete este unica metod de suprancrcare a operatorilor. Considerm urmtoarea
problem: n clasa complex este suprancrcat operatorul + ce ofer posibilitatea de a aduna un numr
complex cu un numr real cu virgul mobil, rezultatul fiind, desigur, un numr complex. Suprancrcarea
este efectuat prin metoda funciilor membre. O descriere schematic a acestei situaii urmeaz n
continuare:
-

class complex
{
. . .
complex operator + (float f)
{
. . .
}
. . .
};
. . .
complex c1(5,7), r;
// c1+f

float f=2.7 ;
. . .
r= c1+f;

- Utilizarea operatorului + pentru calcularea lui c1+f este corect, dar apare urmtoarea
ntrebare: e posibil oare a utiliza o expresie de felul f+c1? Din punct de vedere matematic sunt expresii
echivalente, dar din punctul de vedere al programrii, va fi generat o eroare din motiv c variabila f
este de tip real cu virgul mobil i nu are definit adunarea cu un numr complex. Deci, trebuie de
definit o funcie care ar putea calcula expresiile de tipul f+c1. Dar suprancrcarea direct a operatorului
+ prin metoda funciilor membre este imposibil. Astfel, se va recurge la aplicarea metodei de
suprancrcare prin metoda funciilor prietene. n asemenea situaii, se obine mai mult eficien, putnd
utiliza n expresii proprietatea de comutativitate.
- A fost menionat deja faptul c unii operatori pot fi suprancrcai doar prin metoda funciilor
prietene. Ca exemplu pot fi adui operatorii de inserie i extragere (intrarea/ieirea informaiei) care
accept suprancrcarea doar prin metoda funciilor prietene.
- Pentru a realiza suprancrcarea unui operator prin metoda funciilor prietene, se realizeaz
urmtoarea schem general:
-

class nume_cl
{
. . .
friend tip_r operator (lista_pf);
. . .
};
tip_r operator (lista_pf)
{
. . .
}

unde este numele operatorului suprancrcat, iar lista_pf reprezint lista parametrilor
fictivi ai funciei. Fiindc o funcie prieten nu are acces la pointerul this, lista parametrilor
fictivi conine de regul doi parametri pentru operatorii binari sau un parametru fictiv pentru
operatori unari.

Exemplu. De alctuit un program n care este implementat clasa complex, definind


operatorii caracteristici numerelor complexe prin suprancrcarea operatorilor prin metoda funciilor
prietene.
-

#include <iostream.h>
#include <iomanip.h>
class complex
{
float r, i;
public:
complex (float r1=0, float i1=0)
{ r=r1; i=i1; }
friend complex operator +(complex a,complex b);
friend complex operator *(complex a,complex b);
friend complex operator +=(complex &a,complex b);
friend complex operator +(complex c,float f);
friend complex operator +(float f,complex c);
void afisare ();
};
//-----------------complex operator + (complex a, complex b)
{
complex c;
c.r = a.r + b.r ;
c.i = a.i + b.i ;
return c ;

}
//-----------------complex operator + (complex a, float f)
{
complex c;
c.r = a.r + f;
c.i = a.i;
return c;
}
//-----------------complex operator + (float f, complex a)
{
complex c;
c = a + f;
return c;
}
//-----------------complex operator += (complex &a, complex b)
{
a.r += b.r ;
a.i += b.i;
return a;
}
//-----------------complex operator * (complex a, complex b)
{
complex p;
p.r = a.r * b.r a.i * b.i;
p.i = a.r * b.i + a.i * b.r;
return p;
}
//-----------------void complex :: afisare ()
{
cout << r << setiosflafs (ios :: showpos)
<< i << "i"
<< resetiosflags (ios :: showpos);
}
//==================
void main ()
{
complex a(2,4), b(-3, 7.2), s, p;
float f1=2.7;
s=f1+a;
s.afisare();
(a*b+f1).afisare();
b+=a;
b.afisare();
}

- 16. Suprancrcarea unor operatori speciali


- Pe lng operatori tradiionali, cum ar fi, de exemplu, +, -, *, / pot fi suprancrcai i o serie de
operatori speciali unii avnd i anumite particulariti.
a) operatorii de incrementare i decrementare
- Fcnd referin la operatorii de incrementare i decrementare, trebuie de inut cont c fiecare
dintre ei are dou forme: prefix (++i sau --i) i postfix (i++ sau i--). De aceea, dac se dorete o
utilizare flexibil a acestor operatori, este necesar de a suprancrca att forma prefix, ct i forma postfix.
Aceti operatori pot fi suprancrcai prin ambele metode: prin funcii membre i prin funcii prietene.

Deoarece operatorii sunt unari, rezult c funciile care realizeaz suprancrcarea fie c nu au parametri
fictivi pentru metoda funciilor membre, fie au un parametru fictiv pentru metoda funciilor prietene.
Pentru a obine suprancrcare i pentru cealalt form, funcia care realizeaz suprancrcarea va fi dotat
cu un parametru formal ne-utilizabil. Deci, vorbind despre suprancrcarea operatorului de incrementare
prin metoda funciilor membre, ntr-o clas oarecare vor exista descrise prototipurile:
-

class nume_cl
{
. . .
tip_r operator ++ ();
tip_r operator ++ (int i);
. . .
};

// forma prefix
// forma postfix

- ntr-un mod similar va fi fcut descrierea pentru operatorul de decrementare, dac este necesar
acest lucru.
- Pentru clasa concret complex, vor fi urmtoarele prototipuri:
-

class complex
{
. . .
complex operator ++ ();
complex operator ++ (int i);
. . .
};

- Prototipurilor acestea le corespund urmtoarele funcii membre, care sunt incluse n descrierea
clasei complex:
- - forma prefix:
-

complex complex :: operator ++()


{
r++;
return *this;
}

- - forma postfix:
-

complex complex :: operator ++(int i)


{
complex a= *this;
r++ ;
return a;
}

- La suprancrcarea operatorului de incrementare prin metoda funciilor prietene, ntr-o clas


oarecare vor exista descrise prototipurile:
-

class nume_cl
{
. . .
// forma prefix
friend tip_r operator ++ (nume_cl &ob);
// forma postfix
friend tip_r operator ++ (nume_cl &ob, int i);
. . .
};

- ntruct operatorul de incrementare schimb obiectul asupra cruia acioneaz, parametrii fictivi
de tip obiect sunt transmii prin referin, pentru ca transformarea lor n cadrul funciei s fie vizibil i n
exteriorul funciei.
- Cu referire la clasa complex, pot fi fcute urmtoarele descrieri:
- - descrierea prototipurilor:
-

class complex
{
. . .
friend complex operator ++(complex &c);

friend complex operator ++(complex &c, int i);


. . .
};

- - forma prefix:
-

complex operator ++(complex &c)


{
c.r++;
return c;
}

- - forma postfix:
-

complex operator ++(complex &c, int i)


{
complex a = c;
c.r++;
return a;
}

- Suprancrcarea operatorului de decrementare se realizeaz ntr-un mod similar.


b) operatorii de inserie i extragere
- Suprancrcarea operatorilor de inserie (<<) i extragere (>>) n cadrul unei clase permite ca
operaiile de afiare i citire a obiectelor s fie efectuate ntr-un mod similar ca i a datelor de tipuri
predefinite, adic:
-

cout << nume_obiect;

sau, respectiv:

cin >> nume_obiect;

- Suprancrcarea operatorilor de inserie (<<) i extragere (>>) n cadrul unei clase poate fi
realizat doar prin metoda funciilor prietene. Fiindc aceti operatori inter-acioneaz cu fluxurile de
intrare i de ieire, funciile care realizeaz suprancrcarea au un parametru fictiv, care reprezint un
obiect de tip flux. Operatorul de inserie << este suprancrcat pentru tipurile de date predefinite n clasa
ostream, iar operatorul de extragere >> este suprancrcat pentru tipurile predefinite n clasa
istream. De aceea parametrii fictivi care reprezint obiectul de tip flux vor fi anume de aceste tipuri.
Clasa n care are loc suprancrcarea operatorilor de inserie i extragere va conine urmtoarele
prototipuri de funcii:
-

class nume_cl
{
. . .
friend ostream & operator <<(ostream &nume_flux, nume_cl ob);
friend istream & operator >>(istream &nume_flux, nume_cl &ob);
. . .
}

- Primul parametru fictiv al ambelor funcii reprezint fluxul de intrare sau de ieire, el fiind
transmis prin referin, fiindc fluxul n procesul operaiei de intrare sau de ieire se schimb i aceast
schimbare trebuie s fie vizibil i n exteriorul funciei. Al doilea parametru fictiv reprezint obiectul,
care va fi afiat sau care va fi iniializat cu date citite de la dispozitive de intrare. Pentru ca valoarea
obiectului schimbat n procesul iniializrii s fie vizibil n exteriorul funciei n care se produce
schimbarea, parametrul este transmis prin referin. n cazul afirii, obiectul este transmis prin valoare,
fiindc la afiare obiectul nu-i schimb valoarea. Ambele funcii returneaz referina la fluxul
corespunztor prelucrat. Implementarea operatorilor va avea forma:
-

ostream & operator <<(ostream &nume_flux, nume_cl ob)


{
.
. // instructiuni
.
}

istream & operator >> (istream &nume_flux, nume_cl &ob)


{
.
. // instructiuni
.
}

- Vom suprancrca aceti operatori pentru clasa complex:


-

class complex
{
. . .
friend ostream & operator << (ostream &fluxIes, complex c);
friend istream & operator >> (istream &fluxIn, complex &c);
. . .
};
ostream & operator << (ostream & fluxIes, complex c)
{
if (c.i == 0)
fluxIes << a.r ;
else
if (c.r == 0)
fluxIes << c.i << "i" ;
else
fluxIes << c.r << setiosflags(ios::showpos)
<< c.i << "i"
<< resetiosflags(ios::showpos);
return fluxIes;
}
istream & operator >> (istream &fluxIn, complex &c)
{
fluxIn >> c.r ;
fluxIn >> c.i ;
return fluxIn;
}

c) operatorul indice
- Suprancrcarea operatorului indice ([]) este realizat atunci, cnd se dorete accesul la datele ce
formeaz coninutul unui obiect dup modelul accesului la elementele unui tablou, adic:
-

nume_ob[indice]

- Operatorul indice poate fi suprancrcat doar prin metoda funciilor membre. Acest operator este
unul binar, de aceea lista parametrilor fictivi este format dintr-un singur parametru, i anume indicele.
Trebuie de inut cont de faptul c operatorul indice poate fi utilizat n expresii att n partea stng, ct i
n partea dreapt a operatorului de atribuire. Suprancrcarea operatorului este realizat printr-o funcie,
iar o funcie poate fi plasat n partea stng a operatorului de atribuire doar cnd returneaz o valoare de
tip referin. De aceea funcia care realizeaz suprancrcarea operatorului indice va trebui s returneze o
valoare de tip referin. Pentru descrierea funciei va fi utilizat n caz general urmtorul prototip:
-

class nume_cl
{
. . .
tipr & operator [](tip indice)
. . .
}

- Tipul utilizat la descrierea parametrului indice este deseori tipul int, dar nu sunt excluse n
descriere i alte tipuri de date, obinnd astfel un indice generalizat. Din ideea c tipul parametrului
indice poate fi descris prin diferite tipuri de date rezult c o clas poate avea mai multe
suprancrcri ale operatorului [].

Exemplu. De alctuit un program n care este implementat clasa vector, realiznd


suprancrcarea operatorului indice pentru accesul la componentele vectorului.

#include<iostream.h>
#include<conio.h>
class vector
{
int dim;
float *componente;
public:
vector (int d)
-

componente=new float[d];
if(componente) dim=d; else dim=0;
for(int i=0; i<dim; i++) componente[i]=0;
}
~vector ()
-

}
float & operator[] (int ind)
{
return componente[ind];
}
friend ostream & operator <<(ostream & fluxIes, vector v);
};
//-----ostream & operator <<(ostream &fluxIes, vector v)
{
fluxIes<<(;
for(int i=0; i<v.dim-1; i++) fluxIes<<v.componente[i]<<,;
-

{
delete [] componente;

if(v.dim>=1) fluxIes<<v.componente[i];
fluxIes<<);
return fluxIes;

}
//-------void main()
{
vector v1(3), v2(7);
v1[0]=3.4;
v1[1]=2.1;
v1[2]=-3.2;
v2[0]=v1[0]+v1[1];
v2[2]=v1[0]-5.6;
v2[3]=v2[2]+v1[1];
cout << v1 << endl << v2 << endl;
getch();
}

d) operatorul funcie
- Suprancrcarea operatorului funcie (()) este realizat atunci, cnd se dorete accesul la
coninutul unui obiect, efectund anumite prelucrri sau transformri. Accesul la coninutul obiectului
este efectuat dup modelul apelrii unei funcii, adic:
-

nume_ob(lista_parametri_reali)

- Operatorul funcie poate fi suprancrcat doar prin metoda funciilor membre. Acest operator este
unul binar, ns lista parametrilor fictivi poate fi format, de la caz la caz, dintr-un numr diferit de

parametri tot aa precum i lista de parametri a diferitelor funcii poate consta din numr diferit de
parametri. Din acest fapt rezult c o clas poate avea mai multe suprancrcri ale operatorului (),
deosebindu-se dup numrul de parametri sau tipurile parametrilor fictivi. n contextul clasei complex sar putea realiza dou variante de suprancrcare a operatorului funcie cu urmtoarele sensuri:
- una ar calcula modulul numrului complex;
- alta ar modifica partea real i partea imaginar a numrului complex.
- Pentru aceasta n clasa complex vor fi introduse urmtoarele prototipuri de funcii:
-

class complex
{
. . .
float operator ()();
complex operator ()(float r, float i);
. . .
};

Funciile membre corespunztoare vor fi implementate dup cum urmeaz:


- funcia de calculare a modulului:

float complex :: operator ()()


{
return sqrt(r*r+i*i);
}

complex complex :: operator ()(float r, float i)


{
this->r = r;
this->i = i;
return *this;
}

O aplicare a acestor operatori poate fi urmtoarea:

. . .
void main()
{
complex c(1.3,-2.5);
cout << c() << endl;
c(3.7,2.8);

funcia de modificare a prii reale i a prii imaginare a numrului:

// modulul numar complex


//schimbare numar

e) operatorii new i delete


- Operatorii new i delete pot fi suprancrcai pentru o anumit clas, dar pot fi suprancrcai
i la nivel global. Fiind suprancrcai la nivelul unei clase, i-i schimb specificul aciunii doar n raport
cu clasa dat, iar la nivel exterior i pstreaz modul de executare. La nivel de clas operatorii dai pot fi
suprancrcai doar prin metoda funciilor membre. De asemenea, trebuie de inut cont de faptul c
operatorii dai au dou forme, una pentru variabile simple new, delete i alta pentru tablouri new[],
delete[]. Pentru a realiza suprancrcarea, n clas sunt incluse urmtoarele prototipuri:
-

class nume_cl
{
. . .
void *operator new (size_t dim);
void operator delete (void *adr);
void *operator new[] (size_t dim);
void operator delete[] (void *adr);
. . .
}

- Cea mai simpl form de suprancrcare ar putea fi oferit de urmtoarele implementri ale
funciilor:
-

void *nume_cl :: operator new (size_t dim)


{

return malloc (dim);


}

void nume_cl :: operator delete (void *adr)


{
free (adr);
}

void *nume_cl :: operator new[] (size_t dim)


{
return malloc (dim);
}

void nume_cl :: operator delete[] (void *adr)


{
free (adr);
}

- (nu prea are sens s suprancrcm aceti doi operatori)


f) operatorul virgul
- Operatorul virgul (,) poate fi suprancrcat att prin metoda funciilor prietene, ct i prin
metoda funciilor membre. Este un operator binar i nu prezint vre-o excepie de la regulile generale n
ceea ce privete numrul de parametri fictivi ai funciilor ce realizeaz suprancrcarea: deci un parametru
fictiv la suprancrcare prin metoda funciilor membre i doi parametri la suprancrcare prin metoda
funciilor prietene. Pentru a exemplifica, se va face referire la clasa complex. n aceast clas, vor fi
introduse urmtoarele prototipuri de funcii:
-

class complex
{
. . .
complex operator ,(complex c);
friend complex operator ,(complex c, float f);
. . .
};

n continuare, urmeaz o implementare a acestor funcii:


- funcia membru

complex complex :: operator , (complex c)


{
return c;
}

complex operator , (complex c, float f)


{
complex cf(f,0);
return cf;
}

funcia prieten

- Ambele funcii realizeaz logica operatorului virgul, adic valoarea oferit de operator este
valoarea celui de al doilea operand, valoarea primului operand fiind ignorat.
g) operatorul de conversie
- Este cunoscut faptul c aciunea de trecere a unor date de un anumit tip la alt tip de date reprezint
esena operatorului de conversie, care este un operator unar. Acest operator, de asemenea, poate fi supus
aciunii de suprancrcare n cadrul unei clase. Ideea suprancrcrii acestui operator n cadrul clasei date
va fi de a transforma obiectele obinute n baza clasei date la alt tip de date. Suprancrcarea operatorului
de conversie poate fi fcut doar prin metoda funciilor membri. Funcia care realizeaz suprancrcarea

nu are specificat tipul valorii returnate, cu toate c dup logic ea totui returneaz o valoare. n schimb,
numele operatorului este chiar tipul valorii returnate. Deci clasa, care realizeaz una sau mai multe
conversii, va conine unul sau mai multe prototipuri de urmtorul fel:
-

class nume_cl
{
. . .
operator tip ();
. . .
}

- Exemplu. De alctuit un program n care este implementat clasa lungime, realiznd


suprancrcarea unui operator de conversie.
-

#include<iostream.h>
#include<conio.h>
class lungime
{
int metri;
int centimetri;
public:
lungime (int m, int c)
-

metri=m;
centimetri=c;
}
operator float ()
-

{
return (metri+centimetri/100.0);
}

};
//==========
void main()
{
lungime L1(3, 20), L2(7, 86);
cout << float(L1) << << float(L2) << endl;
getch();
}

- Din exemplu se vede, c n cadrul clasei lungime a fost efectuat suprancrcarea operatorului
de conversie la tipul float i drept rezultat lungimile L1 i L2 vor fi afiate sub forma 3.2 i
7.86.
- 17. Funcii-ablon i clase-ablon
- Funciile-ablon sunt concepute pentru a uura procesul de creare a unor funcii care realizeaz
algoritmi similari, deosebindu-se doar prin tipul datelor prelucrate.
- S considerm urmtorul exemplu. S se realizeze dou funcii: una permite gsirea elementului
maximal al unui tablou de numere ntregi, iar alta permite gsirea elementului maximal al unui tablou de
numere cu virgul mobil.
- Presupunnd c tabloul de numere ntregi este de tip int, iar tabloul de numere cu virgul mobil
este de tip float, sunt propuse urmtoarele funcii:
- pentru tabloul de numere ntregi:
-

int el_max(int *tb, int n_el)

float el_max (float *tb, int n_el)


{
float max=tb[0];
for (int i=1; i< n_el ; i++)
if (max < tb[i]) max=tb[i];
return max;
}

int max=tb[0];
for (int i=1; i< n_el ; i++)
if (max < tb[i]) max=tb[i];
return max;
}

pentru tabloul de numere cu virgul mobil:

- Din compararea acestor funcii se poate observ c ele sunt identice prin setul de instruciuni
utilizate i se deosebesc doar prin tipurile unor variabile locale sau ale unor parametri fictivi. Pentru a
descrie mai simplu astfel de algoritmi se utilizeaz funciile-ablon. Limbajul C++ are ncorporat n el
mecanismul funciilor-ablon. Ele se mai numesc i funcii parametrizate sau funcii generice. Prin
intermediul unei singure descrieri a funciei-ablon este realizat, de fapt, descrierea unei mulimi de
funcii, cte una pentru diferite combinaii de tipuri de date. O funcie-ablon este descris n felul
urmtor:
-

template
<class Ta1, class Ta2, . . ., class Tak>
[tip_r] nume_functie([lista_par_fictivi])
{
. . .
//instructiune
. . .
}

unde template este cuvntul-cheie; Ta1, Ta2, , Tak sunt o serie de tipuri abstracte de
date, iar celelalte elemente ale funciei sunt cunoscute din descrierea funciilor obinuite. Tipurile
abstracte de date pot fi utilizate ca descriptori pentru descrierea parametrilor fictivi sau a
variabilelor locale. Tipul valorii returnate de funcie poate fi la necesitate descris cu ajutorul unuia
dintre tipurile abstracte. Revenind la exemplul precedent, n conformitate cu descrierea general
este obinut urmtoarea funcie-ablon:

template
<class Tip>
Tip el_max (Tip *tb, int n_el)
{
Tip max;
max=tb[0];
for (int i=1 ; i < n_el ; i++)
if (max < tb[i]) max =tb[i];
return max;
}

- Din funcia-ablon este obinut funcia real necesar. Funciile reale se obin la etapa compilrii
prin nlocuirea tipurilor abstracte de date prin tipuri reale de date. Un procedeu corect cere i existena
unui prototip care descrie funcia real necesar. Dac funcia-ablon este descris mai sus de locul
utilizrii, atunci prototipul funciei reale poate s lipseasc. Iat forme de utilizare a funciei-ablon din
exemplul anterior:
-

. . .
void main()
{

int el_max(int * , int);


float el_max(float *, int);
int ti[4]={-240,4,330,7};
float tf[3]={1.2, -3.47, 27.5};
double td[4]={0,1.12,2.45,3.27};
cout << el_max(ti, 4)<< endl;
cout << el_max(tf, 3)<< endl;
cout << el_max(td, 4)<< endl;
}

- Nu totdeauna algoritmul descris de funcia-ablon este acel care poate fi aplicat absolut pentru
toate tipurile reale de date utilizate n program. Unele date de anumite tipuri pot necesita algoritmi
diferii. n asemenea situaii, pe lng funcii-ablon sunt admise i funcii obinuite, care au acelai nume
cu funciile-ablon i corespund algoritmilor diferii. Dac exist i funcii-ablon i funcii obinuite cu
acelai nume, prioritare sunt funciile obinuite elaborate pentru unele combinaii de date de anumite
tipuri concrete. De exemplu, suma a dou variabile de tip char ar putea nsemna un ir de caractere
constnd din valorile acestor variabile, ceea ce se deosebete desigur de suma a dou variabile numerice.
Pentru variabile numerice de tip int, short, long, float etc., este realizat o funcie-ablon, iar
pentru variabile de tip char este propus o funcie obinuit, care realizeaz algoritmul necesar:
- variabile numerice:
-

template
<class Ta>
T suma (Ta x, Ta y)
{
return (x+y);

char* suma (char x, char y)


{
-

variabile de tip char:

char *sir;
sir = new char[3];
sir[0] = x;
sir[1] = y;
sir[2] = \0;
return

sir;

- Trebuie de subliniat faptul c funciile-ablon pot fi suprancrcate tot aa ca i funciile obinuite.


- Dac pn acum au fost propuse exemple de funcii-ablon care conineau doar un singur tip
abstract, n continuare este prezentat un exemplu coninnd mai multe tipuri abstracte.
Exemplu. De alctuit o funcie-ablon care permite compararea unei valori cu suma
elementelor unui tablou.
-

template
<class Ta1, class Ta2>
int compar_suma (Ta1 *tablou, Ta2 val, int numEl)
{
int i;
Ta1 suma=0;
for(i=0; i<numEl; i++)
suma+=tablou[i];
return (val-suma);

- Un alt mecanism ce uureaz mult procesul de proiectare i implementare a claselor este


mecanismul claselor-ablon. Clasele-ablon permit crearea unei mulimi de clase asemntoare dup

funcionalitate care se deosebesc doar dup tipul datelor prelucrate. Toate funciile membre ale unei claseablon sunt funcii-ablon. Pentru a descrie o clas-ablon, este utilizat urmtoarea schem general:
-

template
<class Ta1, class Ta2, , class Tak>
class nume_cl_sb
{
. . .
// membri ai clasei
. . .
};

unde Ta1, Ta2, ., Tak sunt tipuri abstracte de date, care pot servi la descrierea tipurilor datemembre, a tipurilor valorilor returnate de funciile-membre, a tipurilor parametrilor fictivi, a
tipurilor variabilelor locale ale funciilor membre. Pentru descrierea funciilor membre ale unei
clase-ablon este utilizat modelul ce urmeaz:

template
<class Ta1, class Ta2, , class Tak>
tip_r nume_cl_sb<Ta1, Ta2, , Tak>::nume_fn(lista_pf)
{
. . .
//instructiuni
. . .
}

unde nume_fn este o oarecare funcie-membr, iar lista_pf reprezint lista parametrilor
fictivi. Pentru a crea obiecte n baza unei clase-ablon, mai nti tipurile abstracte sunt nlocuite cu
tipuri concrete de date, obinnd o clas care prelucreaz date de tipuri concrete, iar apoi este
aplicat n mod obinuit constructorul clasei pentru a finaliza aciunea:

nume_cl_sb<Tc1, Tc2, , Tck> nume_ob(lista_pr);

unde Tc1, Tc2, , Tck sunt tipuri concrete de date, nume_ob reprezint numele obiectului
creat, iar lista_pr este lista parametrilor reali.

Exemplu. De alctuit un program n care este realizat clasa-ablon stiva, crend mai
apoi stive concrete de diferite tipuri.
-

template
<class T>
class stiva
{
T *stv ; // stiva
int dim; // dimensiunea stivei
int vst; // varful stivei
public:
stiva(int d);
~stiva();
T push (T el);
T pop ( );
-

friend ostream & operator << (ostream & fl, stiva st);

};
//-----------template
<class T>
stiva <T> :: stiva (int d)
{
stv=new T[d];
if(stv==NULL)
dim=0;
else
dim=d;

vst=0;
}
//-----------template
<class T>
stiva <T> :: ~stiva ()
{
if(stv !=NULL)
delete [] stv;
}
//-----------template
<class T>
T stiva <T> :: push (T el)
{
if (vst<dim)
{
stv[vst]=el;
vst ++;
return el;
}
else
return 1;
}
//-----------template
<class T>
T stiva <T> :: pop ( )
{
if (vst>0)
{
vst -- ;
return stv[vst];
}
else
return 1;
}
//-----------template
<class T>
ostream & operator <<(ostream &fl, stiva <T> st)
{
for (int i=0; i<st.vst; i++)
fl << st.stv[i] << ;
return fl;
}

- Memornd implementarea clasei n fiierul stiva_p.hpp, includem acest fiier n programul


principal, efectund o serie de operaii cu diferite tipuri de stive:
-

#include <iostream.h>
#include "stiva_p.hpp"
void main ()
{
stiva<int> si(10);
stiva<long> sl(15);
stiva<float> sf(20) ;
for (int i=0; i<10; i++)
{
si.push(i);
sl.push(i+100000);

sf.push(float i/10.0);
}
cout << si << endl;
cout << sl << endl;
cout << sf << endl;
si.pop() ;
cout << si << endl ;
}

- Remarc: Stiva generic realizat este orientat la descrierea stivelor pentru tipuri numerice de
date.
- 18. Realizarea conceptului de polimorfism
- Un concept important n programarea orientat pe obiecte este noiunea de polimorfism. Cuvntul
polimorfism provine de la mbinarea a dou rdcini polys ce nseamn numeros i morphe, ce
nseamn form. n contextul programrii polimorfism nseamn o singur interfa mai multe
forme de realizare. O form de polimorfism ar fi suprancrcarea funciilor i a operatorilor. n acest caz,
interfaa este numele funciei sau al operatorului, iar formele realizate sunt algoritmii diferii descrii de
fiecare funcie n parte.
- O form mai complex de polimorfism este obinut utiliznd conceptul de motenire i funcii
virtuale. n acest mecanism, sunt implicate cel puin dou tipuri de date:
- tipul ce corespunde clasei de baz;
- tipurile ce corespund claselor derivate.
- n tipul de baz sunt definite o serie de funcii membre virtuale. O funcie virtual are proprietatea
de a putea fi schimbat n cadrul tipurilor derivate din clasa de baz, schimbndu-i sensul dup necesitate
n clasa derivat. O funcie virtual este introdus prin intermediul cuvntului-cheie virtual:
-

virtual tipr nume_fv(lista_pf);

- Pentru ca s fie realizat conceptul de polimorfism, n clasa derivat prototipul funciei trebuie s
fie identic cu prototipul descris n clasa de baz. La cea mai mic deosebire dintre prototipuri
polimorfismul nu va fi realizat. Va fi descris schema ce genereaz polimorfism, pornind de la urmtoarea
schem de motenire:
cl_baza
cl_der1
cl_der2
...
cl_derk
- Clasa de baz conine o serie de funcii virtuale:
-

class cl_baza
{
. . .
virtual tipr1 nume_fv1(lista_pf1);
. . .
virtual tiprn nume_fvn(lista_pfn);
. . .
};
class cl_der1 : public cl_baza
{
. . .
[virtual tipr1 nume_fv1(lista_pf1);]
. . .
[virtual tiprn nume_fvn(lista_pfn);]
. . .
};

. . .
class cl_derk : public cl_baza
{
. . .
[virtual tipr1 nume_fv1(lista_pf1);]
. . .
[virtual tiprn nume_fvn(lista_pfn);]
. . .
};

- Funciile virtuale din clasa de baz cl_baza pot fi implementate n clasele derivate,
schimbndu-le dac este necesar sensul. Totodat, exist posibilitatea de a pstra implementarea din clasa
de baz i n unele clase derivate. n acest caz, pur i simplu nu sunt implementate funciile virtuale n
clasele date.
-

tipr1 cl_baza :: nume_fv1


{
.
.
//realizare01
.
}
. . .
tiprn cl_baza :: nume_fvn
{
.
.
//realizare0n
.
}
//-------------tipr1 cl_der1 :: nume_fv1
{
.
.
//realizare11
.
}
. . .
tiprn cl_der1 :: nume_fvn
{
.
.
//realizare1n
.
}
//-------------. . .
//-------------tipr1 cl_derk :: nume_fv1
{
.
.
//realizarek1
.
}
. . .
tiprn cl_derk :: nume_fvn
{
.
.
//realizarekn
.
}
//--------------

(lista_pf1)

(lista_pfn)

(lista_pf1)

(lista_pfn)

(lista_pf1)

(lista_pfn)

- Dup implementarea funciilor-membre pot fi generate obiectele necesare. Pentru a beneficia de


polimorfism, trebuie de creat un pointer de tip clasa de baz. ncrcnd n acest pointer adresa unui

obiect, va fi posibil ca prin el s apelm funcii membre. n acest pointer pot fi ncrcate adrese de obiecte
de tip clas de baz sau obiecte de tipul uneia dintre clasele derivate. Apelnd prin intermediul acestui
pointer funciile virtuale, va fi obinut efectul de polimorfism. Interfaa n cazul acesta va fi numele
pointerului n combinaie cu numele funciei virtuale, iar formele vor fi realizrile funciei virtuale, fie
realizarea din clasa de baz, fie realizrile din clasele derivate. Dac pointerul conine adresa unui obiect
de tipul clasei de baz, atunci la apelarea unei funcii virtuale va fi apelat anume funcia realizat n clasa
de baz. Dac ns pointerul conine adresa unui obiect de tipul unei clase derivate, atunci la apelarea unei
funcii virtuale va fi apelat funcia realizat anume n clasa derivat. Deci pointerul este sensibil la tipul
obiectului a crui adres o conine, determinnd i apelnd funcia care corespunde obiectului dat. n
continuare, sunt reprezentate schematic ideile expuse mai sus:
-

cl_baza *p_baza, ob_baza(. . .);


cl_der1 ob_der1(. . .);
cl_der2 ob_der2(. . .);
. . .
cl_derk ob_derk(. . .);
p_baza=&ob_baza;
p_baza->nume_fv1(lista_pr); //va
p_baza=&ob_der1;
p_baza->nume_fv1(lista_pr); //va
p_baza=&ob_der2;
p_baza->nume_fv1(lista_pr); //va
. . .
p_baza=&ob_derk;
p_baza->nume_fv1(lista_pr); //va

fi apelata realizare01
fi apelata realizare11
fi apelata realizare21

fi apelata realizarek1

- Fixnd funciile virtuale ale unei clase, trebuie s inem cont de urmtoarele reguli:
- constructorii unei clase nu pot fi nicidecum funcii virtuale;
- destructorul unei clase poate fi virtual.
- Este cunoscut faptul c funciile-membri ale unei clase sau, altfel spus, metodele clasei sunt
apelate prin intermediul unui obiect de tipul clasei date sau prin intermediul unui pointer. Desigur c
pentru apelare este necesar o coordonare ntre adresele obiectului sau a pointerului i a metodei, adic
crearea unei legturi corecte. Legarea unei metode de un obiect de anumit tip se face n procesul
compilrii i o astfel de legtur se numete legtur static (static binding), fiindc este cunoscut i
obiectul, i metoda apelat. Legarea metodei de pointer n lipsa polimorfismului este efectuat tot n
timpul compilrii dup tipul pointerului i de aceea este, de asemenea, o legtur static. Legarea metodei
de pointer n procesul polimorfismului poate fi efectuat doar n timpul rulrii programului, fiindc
informaia despre tipul pointerului este insuficient, iar pn la rulare nu este cunoscut obiectul a crui
adres va fi ncrcat n pointer i deci nu poate fi determinat corect nici metoda apelat. De aceea o
astfel de legtur se numete legtur dinamic (dynamic binding).
- Pentru a putea determina corect metoda polimorfic apelat, clasele care conin metode virtuale au
un element suplimentar asociat cu ele, i anume un tabel al funciilor virtuale. Cu ajutorul acestui tabel
este calculat corect adresa metodei apelate. Ca rezultat, obiectele create n baza unei clase fr funcii
virtuale ar fi mai mici dect obiectele create n baza unei clase similare, dar care conine funcii virtuale.
Totodat, apelarea metodelor nevirtuale este mai rapid dect apelarea metodelor virtuale.
- 19. Clase abstracte
- Un tip de funcii virtuale speciale sunt funciile virtuale abstracte. Prototipurile acestor funcii se
deosebesc de prototipurile funciilor ne-abstracte prin faptul c se termin cu =0:
-

virtual tipr nume_fv(lista_pf)=0; // functie abstracta

- Funciile abstracte pot fi doar parte component a unor clase. Clasele care conin mcar o funcie
abstract se numesc clase abstracte. Pot exista clase abstracte care conin doar funcii abstracte.
Caracteristica principal a funciilor abstracte const n faptul c ele nu sunt realizate n clasa lor de
declarare. Deci, clasa conine doar descrierea prototipului funciei, fr a fi definit i corpul funciei.
Astfel de funcii se utilizeaz atunci, cnd clas este una foarte abstract i la nivelul dat de abstractizare

este imposibil de a defini algoritmic funcia. Ca rezultat al existenei n clasa abstract a funciilor
nedefinite este interzis de a crea obiecte n baza unei clase abstracte. O clas abstract poate fi folosit
doar ca clas de baz n procesul de motenire. Deci n procesul de generare a unei clase derivate una sau
mai multe clase de baz pot fi i clase abstracte. Dac n clasa derivat obinut sunt definite toate
funciile abstracte motenite din clasele de baz, atunci obinem o clas obinuit n baza creia putem
crea obiecte. Dac ns n clasa derivat obinut rmne nedefinit mcar o funcie abstract, atunci clas
derivat va fi n continuare una abstract. Pot exista situaii cnd n procesul de motenire sunt implicate
doar clase abstracte. Deci, clasele abstracte sunt utilizate ca o interfa comun pentru derivarea
diferitelor clase, care au o legtur cu ea.
- Exemplu. De alctuit un program n care este realizat clasa abstract figura din care sunt
derivate clasele cerc i patrat. Unele operaii cu obiectele de tip cerc i patrat sunt efectuate n baz de
polimorfism. La descrierea claselor cerc i patrat este utilizat clasa ajuttoare punct.
figur
ptrat
cerc
-

class punct
{
int x;
int y;
public:
punct(int x1=0, int y1=0)
{
x=x1; y=y1; }
punct(punct & p)
{
x=p.x; y=p.y;

int getx() {return x;}


int gety() {return y;}
};
//---------class figura
{
public :
virtual void afisare()=0;
virtual void miscare(punct p)=0;
};
//---------class cerc: public figura
{
punct centru;
int raza;
public:
cerc(punct c, int r) : centru(c)
{
raza=r; }
void afisare();
-

};

void miscare(punct p);

//---------class patrat : public figura


{
punct virf ;
int lat;
public:
patrat(punct v, int l) : virf(v)
{
lat=l;
}
void afisare();
-

void miscare(punct p);

};
//---------void cerc :: afisare()
{
circle(centru.getx(), centru.gety(), raza);
}
//---------void cerc :: miscare(punct p)
{
int c=getcolor() ;
setcolor(getbkcolor());
afisare();
centru=p;
setcolor(c);
afisare();
}
//---------void patrat :: afisare()
{
int x=virf.getx();
int y=virf.gety();
line(x, y, x+lat, y);
line(x, y, x, y+lat);
line(x+lat, y, x+lat, y+lat);
line(x, y+lat, x+lat, y+lat);
}
//---------void patrat :: miscare(punct p)
{
int c=getcolor();
setcolor(getbkcolor());
afisare();
virf=p;
setcolor(c);
afisare();
}

Implementarea claselor este memorat n fiierul cerc_pat.hpp, iar programul


principal este memorat n fiierul cerc_pat.cpp, coninutul cruia urmeaz.
-

#include
#include
#include
#include

<iostream.h>
<conio.h>
<graphics.h>
cerc_pat.hpp

void main()
{
int rg=DETECT, srg, er;
figura *pf;
initgraph(&rg, &srg, "");

er=graphresult();
if(er==grOk)
{
punct pc(100,100), pv(10,17);
cerc c1(pc, 20);
patrat p(pv, 200);
pf = &c1;
pf->afisare();
pf = &p;
pf->afisare();
pf->miscare(pc);
getch();
closegraph();
}

- 20. Membrii statici ai clasei


- Atunci cnd este necesar crearea membrilor comuni pentru toate obiectele unei clase se utilizeaz
membri statici ai clasei.
- O proprietate static se obine cu ajutorul cuvntului static plasat naintea membrului dat:
-

static tip nume_membru;

- n interiorul clasei, proprietatea static este doar declarat, iar pentru a o defini (adic a-i aloca
memorie), ea trebuie s fie definit n exteriorul clasei, scriind:
-

tip nume_clasa::nume_membru; //fr iniializare

caz n care membrul dat va fi iniializat cu valoarea zero, sau:

tip nume_clasa::nume_membru=val;

caz n care membrul dat va fi iniializat cu valoarea propus val. Chiar dac membrul static este
de tip privat, definirea lui i iniializarea cu prima valoare se va face n exteriorul clasei.

//cu iniializare

- n afar de proprieti statice, mai exist i funcii membre statice. Ele se declar n interiorul
clasei tot cu ajutorul cuvntului static:

static tip nume_functie(lista_param);

n exteriorul clasei, ea este definit asemntor cu o funcie ne-static:

tip nume_cl::nume_functie(lista_param)
{
. . .
}

- Funciile statice au o serie de restricii n utilizare:


Nu au acces la membrii ne-statici ai clasei, de aceea foarte des funciile statice sunt create pentru a
iniializa membrii statici ai clasei.
n cadrul funciilor de tip static, nu exist acces la pointerul this.
- Modul de apelare a funciilor statice se deosebete puin de apelarea funciilor ne-statice. Ele pot fi
apelate prin metoda obinuit, adic prin intermediul unui obiect:
-

nume_obiect.nume_functie(lista_param)

sau poate fi apelat direct la nivel de clas astfel:

nume_clasa::nume_functie(lista_param)

- Funciile statice sunt deseori aplicate pentru a prelucra membrii statici ai clasei. Un exemplu de
utilizare a membrilor statici poate fi realizarea unui contor al obiectelor create n baza unei clase. n
continuare, este prezentat un exemplu de acest tip.
-

//student.hpp

class student
{
unsigned long id;
static unsigned contor;
public:
student(unsigned long i) { id=i; contor++; }
student(student &s) { id=s.id; contor++; }
~student() { contor--; }
static unsigned GetContor ();
};
unsigned student::GetContor()
{
-

return contor;

}
unsigned student::contor=0;//definirea membrului static
//n exteriorul clasei

Realizarea clasei este nscris n fiierul student.hpp i va fi utilizat n programul


student.cpp care urmeaz.

//student.cpp
#include <iostream.h>
#include student.cpp
void creare_st()
{
student s(1759);
cout << s.GetContor() << endl;
}
void main()
{
student A(529478);
cout << A.GetContor() << endl; //afieaz 1
student B(A);
cout << student::GetContor()<<endl; //afieaz 2
creare_st(); //afieaz 3 i deoarece la ieire
//din funcie obiectul creat se
//distruge la ultima apelare a
//funciei GetContor() se va afia 2
cout << student::GetContor()<<endl; //afieaz 2
}

- O alt aplicare a funciilor statice poate fi realizarea de interfee, atunci cnd diferite platforme au
diferite realizri funcionale de interaciune cu anumite resurse tehnice sau de program i este necesar
crearea de versiuni ale aplicaiei pentru aceste platforme. De aceea se proiecteaz i se realizeaz o
interfa, adic nite funcii cu ajutorul crora va fi programat aplicaia. Pentru a realiza diferite versiuni
pentru diferite platforme, va fi schimbat doar realizarea funciilor din interfa, realizarea aplicaiei
rmnnd neschimbat.
- Ca exemplu ar putea fi luat lucrul cu regimul grafic. Pentru diferite sisteme de operare, exist
diferite funcii ce realizeaz sarcinile de desenare a diferitelor elemente geometrice, de iniializare i de
nchidere a regimului grafic.
- n continuare este propus o clas-interfa EcranGr, n care sunt realizate doar cteva funcii
statice init(), linie(), cerc(), punct(), inchidere() pentru lucru n regim grafic. Pentru a
nu putea crea obiecte n baza clasei EcranGr, constructorul clasei este unul privat. n continuare este
prezentat o versiune a acestei clase.
-

class EcranGr
{
public:
static void init ();
static void linie(int x1, int y1, int x2, int y2);

static void cerc(int x, int y, int r);


static void punct(int x, int y);
static void inchidere();
private:
EcranGr() { };
unsigned static culoare;
};
unsigned EcranGr::culoare=15;
void EcranGr::init()
{
int dr=DETECT, rg;
initgraph (&dr, &rg, );
}

void EcranGr::linie(int x1, int y1, int x2, int y2)


{
line(x1, y1, x2, y2);
}
void EcranGr::cerc(int x, int y, int r)
{
circle(x, y, r);
}
void EcranGr::punct (int x, int y)
{
putpixel (x, y, culoare);
}
void EcranGr::inchidere()
{
closegraph();
}

- Funciile date pot fi aplicate la realizarea algoritmului necesar de desenare.


- 21. Enumerri, uniuni, structuri de date n C++
- Toate aceste noiuni au semnificaii similare cu cele din limbajul C, dar mai au i unele trsturi
caracteristice doar limbajului C++.
- Din cum se tie, o enumerare creeaz nite etichete n spatele crora sunt, de fapt, nite constante
ntregi. Descrierea general a unei enumerri este exprimat astfel:
-

enum
[nume_enum]
[lista_var];

{nume_et1[=val1],

nume_et2[=val2],

nume_etk[=valk]}

- n C++ enumerrile se utilizeaz deseori pentru definirea unor constante utilizate n interiorul unei
clase, de aceea astfel de enumerri sunt descrise n interiorul clasei respective. Dac enumerarea se
utilizeaz doar n interiorul clasei, atunci ea poate fi definit n zona privat a clasei. De exemplu:
-

class student
{
-

enum {obiecte=7, semestre=10};


int note[semestre][obiecte];
. . .

};

- Dac unele etichete vor fi utilizate i n exteriorul clasei, atunci enumerarea este descris n zona
public a clasei. Pentru a utiliza una dintre etichetele enumerrii n exteriorul clasei, se folosete
urmtoarea sintax:
-

nume_cl::nume_et

- Dac se dorete a defini variabile de tip enumerare n baza unei enumerri declarate n interiorul
unei clase, se procedeaz astfel:
-

nume_cl::nume_enumerare var1, var2, ..., vark;

- Exemplificarea se va face n baza clasei ios, n zona public a creia este declarat urmtoarea
enumerare:
-

enum
{
skipws
=
left
=
right
=
internal =
dec
=
oct
=
hex
=
showbase =
showpoint =
uppercase =
showpos
=
scientific=
fixed
=
unitbuf
=
stdio
=
};

0x0001,
0x0002,
0x0004,
0x0008,
0x0010,
0x0020,
0x0040,
0x0080,
0x0100,
0x0200,
0x0400,
0x0800,
0x1000,
0x2000,
0x4000

- n baza acestei enumerri se fixeaz o serie de fanioane utilizate la afiarea informaiei pe ecran,
de exemplu:
-

cout << setiosflags(ios::hex) << 100;

- Forma general de descriere a unei structuri:


-

struct [nume_struct]
{
-

tip1 cimp1;
. . .
tipN cimpN;

} [lista_var];

- n C++ o structur mai poate avea i funcii ncorporate, astfel ea devenind foarte asemntoare cu
o clas. Deosebirile dintre structuri i clase:
a)
Dac n cadrul unei clase nu este folosit modificatorul de acces, se subnelege privat, pe cnd
n cadrul unei structuri se subnelege public.
b)
Dac n procesul motenirii modificatorul de motenire nu este indicat i entitatea derivat este o
structur, atunci se subnelege modificatorul de motenire public, iar n cazul unei clase derivate se
subnelege modificatorul de motenire private.
O uniune este descris ntr-un mod similar cu o structur de date:
-

union [nume_uniune]
{
-

tip1 cimp1;
. . .
tipN cimpN;

} [lista_var];

- n C++ o uniune poate avea i funcii ncorporate, dispunnd astfel i de funcionalitate. Cu


referin la modelul obiectual, uniunile au multe restricii i deosebiri n comparaie cu clasele:
a)
Dac n cadrul unei uniuni nu este folosit modificatorul de acces, se subnelege public.
b)
Uniunile nu pot fi implicate n procesul de motenire nici n rol de entitate de baz, nici n rol de
entitate derivat.
c)
O uniune nu poate avea n calitate de membru un obiect al crui clas are operatorul =
suprancrcat.
- Pot fi declarate uniuni globale statice anonime, ele avnd rolul unor zone de memorie comun
pentru toate cmpurile care intr n componena fiecrei uniuni. Pentru a accesa cmpurile din
componena unor astfel de uniuni, operatorul rezoluiei :: este plasat n faa denumirii cmpului necesar.
Iat un exemplu care ilustreaz cele spuse:
-

#include<iostream.h>
static union
{
-

int i;
long l;

};
void main()
{
::l=0;
::i=90;
cout<< ::l << endl;
}

- ntruct cmpurile i i l ocup aceeai zon de memorie, rezultatul afirii va fi 90.


- 22. Prelucrarea fiierelor n C++
Fluxurile de intrare/ieire n C++ se trateaz n baza unor clase i a unor obiecte special
create pentru asemenea aciuni. Clasele date formeaz o ierarhie de motenire, mbogindu-se de la nivel
la nivel cu noi elemente necesare n procesul de prelucrare a fluxurilor de date. n continuare, este
prezentat o ierarhie a celor mai importante clase.
ios
istream
ostream
streamable
ifstream
ofstream
fstream

- O component a interaciunii cu fluxurile de intrare/ieire o reprezint interaciunea cu fiierele.


Lucrul cu fiierele este organizat n baza claselor ifstream, ofstream i fstream. Clasa
ifstream este destinat operaiei de citire din fiiere. Clasa ofstream este menit pentru operaia
de scriere n fiiere, clasa fstream este destinat att operaiei de citire din fiiere, ct i operaiei de
scriere n fiiere. Pentru a putea utiliza clasele date, este necesar a include fiierul antet fstream.h
care conine descrierile necesare referitoare la clasele date.
- Pentru a accesa un fiier de citire sau scriere, mai nti se creeaz obiecte fie n baza clasei
ifstream, fie a clasei ofstream, fie a clasei fstream:
-

ifstream nume_ob;
ofstream nume_ob;
fstream nume_ob;

unde nume_ob reprezint denumirea obiectului definit fie pentru citire, fie pentru scriere, fie
pentru citire i scriere. Obiectul definit nu este legat de oarecare fiier, de aceea, pentru crearea
legturii cu fiierul necesar, este apelat funcia-membru open(), avnd urmtorul prototip:

void open(const char *nume_f, int mod_des);

unde parametrul nume_f reprezint adresa irului coninnd numele fiierului deschis, iar
mod_des descrie modul de deschidere a fiierului. Modul de deschidere este descris printr-o
serie de constante ntregi, exprimate prin intermediul unei enumerri, care face parte din clasa
ios. De exemplu, n fragmentul ce urmeaz

ifstream cit;
cit.open("L.cpp", ios::in);

este efectuat deschiderea fiierului L.cpp cu modul de deschidere ios::in, adic pentru
citire. n continuare sunt prezentate toate valorile posibile care pot descrie modul de deschidere:
-

Valori
posibile
ale
modului de
deschidere
ios :: in

ios :: out

ios ::
trunc

ios :: ate

ios :: app

ios ::
nocreate

ios ::
noreplace

ios ::
binary

Semantica impus de
valoare

deschiderea fiierului
pentru citire
deschiderea fiierului
pentru scriere
deschiderea fiierului
cu trunchierea
informaiei
deschiderea fiierului
cu plasarea cursorului
la sfritul fiierului
deschiderea fiierului
cu impunerea scrierii
doar la sfritul
fiierului
deschiderea unui fiier
existent, interzicnduse crearea lui, dac
fiierul nu exist
deschiderea fiierului,
interzicndu-se
nlocuirea unui fiier
existent
deschiderea fiierelor
n regim binar

Valorile descrise n tabel pot fi


combinate, obinnd efectul reunirii semanticilor a dou sau a mai multe valori. Combinarea fanioanelor
se face cu ajutorul operaiei de aciune pe binari | (sau).
n continuare sunt prezentate o serie de
exemple care demonstreaz unele dintre proprietile descrise mai sus:
- deschiderea unui fiier pentru scriere
-

ofstream sc;
sc.open("t.txt", ios::out);

fstream cit_sc;
cit_sc.open("a.txt", ios::in | ios::out |
ios::binary);

deschiderea unui fiier pentru citire i scriere n regim binar

Trebuie de subliniat faptul c clasele


ifstream, ofstream, fstream conin mai muli constructori. O variant important de constructor
cu parametri efectueaz crearea obiectului realiznd i deschiderea fiierului asociat cu obiectul dat.
-

ifstream n_ob(const char *nume_f,


int mod_des=ios::in);
ofstream n_ob(const char *nume_f,
int mod_des=ios::out);
fstream n_ob(const char *nume_f, int mod_des);

n cele ce urmeaz, este re-scris


exemplul anterior crend un obiect asociat cu fiierul a.txt i fiind destinat citirii i scrierii n regim
binar:
-

fstream

cit_sc("a.txt", ios::in | ios::out |


ios::binary);

Dup ce fiierul este asociat cu obiectul


necesar, pot fi efectuate o serie de operaii. Sunt, desigur, foarte importante operaiile de citire i operaiile
de scriere. Operaiile date pot fi efectuate att cu ajutorul unor operatori, ct i cu ajutorul unor funcii.
Sunt aplicai operatorii de inserie << i extragere >> utilizai i cu obiectele cout i cin,
caracterizai n unul din paragrafele anterioare. Deci, pentru a face scrierea ntr-un fiier, obiectul asociat
cu fiierul acioneaz operatorul de inserie:
-

nume_ob << date;

obiectul asociat cu fiierul acioneaz operatorul de extragere:


-

nume_ob >> nume_var;

scrise ntr-un fiier cteva valori numerice:


-

De exemplu, n cele ce urmeaz vor fi

ofstream scriu("val.dat", ios::out);


float f=-45.23;
. . .
scriu << f << << 24.3;
. . .

se poate citi din fiier:


-

Pentru a face citirea dintr-un fiier,

La necesitate, ntr-un mod asemntor,

ifstream citire("val.dat", ios::in);


float f;
. . .
citire >> f;
cout << f << endl;
. . .

Una dintre funciile referitoare la


operaiile de citire este funcia getline(), caracterizat n unul dintre primele paragrafe. Ea are
urmtorul prototip:
-

istream & getline(char *sir, int max,


char delim=\n);

cror prototipuri sunt prezentate mai jos:


-

Mai exist o clas de funcii get(), a

int get();
istream & get(char & car);
istream & get(char *sir, int max,
char delim=\n);

Varianta a treia este aproape identic cu


funcia getline(), att doar c la citire ea nu scoate delimitatorul din flux. Primele dou funcii permit
citirea unui caracter din flux.
Iat cteva exemple, utiliznd funciile
prezentate mai sus:
citirea unui sir de caractere cu funcia getline() (delimitator este simbolul _)
-

ifstream cit("date.dat", ios::in);


. . .
char sir[100];
cit.getline(sir, sizeof(sir), '_');

char c;
c=cit.get( );

citirea unui caracter cu funcia get()


citirea unui caracter cu o alt variant a funciei get()

char c;
cit.get(c);

O alt categorie de funcii este


reprezentat de funciile pentru citire/scriere neformatat. Ele permit citirea sau scrierea unui fragment de
date fr oarecare transformare a lor. Prototipurile acestor funcii sunt asemntoare, fiind prezentate n
cele ce urmeaz:
- prototipul funciei de citire
-

int read(char *dom, int num_car);

int write(char *dom, int num_car);

Parametrul dom reprezint adresa domeniului unde este citit sau de unde este scris informaia,
iar parametrul num_car reprezint numrul de caractere respectiv citite sau scrise. Funciile
returneaz numrul de caractere de facto citite sau scrise. Dac valoarea returnat este mai mic
dect valoarea parametrului num_car, atunci fie s-a produs o eroare, fie s-a ajuns la sfritul
fiierului n procesul operaiilor de citire. n exemplul ce urmeaz, datele citite de la tastatur sunt
scrise n fiierul Doc.txt, efectundu-se i controlul unei scrieri corecte n fiier:

char sir[100];
int num;
ofstream sc("Doc.txt", ios :: out);
. . .
cin.getline(sir, sizeof(sir));
num=sc.write(sir, strlen(sir));
if(num < strlen(sir))
cout << Eroare de scriere << endl;

prototipul funciei de scriere

O alt categorie important o formeaz


funciile de control al poziiei curente. Poziia curent este locul unde va fi scris sau de unde se va citi
informaia din fiier. Determinarea poziiei curente poate fi efectuat cu ajutorul funciei tellg()
pentru fluxurile de citire i tellp(), pentru fluxurile de scriere. Iat prototipurile acestor funcii:
-

long tellg();
long tellp();

numeric cuprins ntre 0 i lungumea fiierului minus unu.

Ambele funcii returneaz o valoare

Pentru schimbarea poziiei curente, n


cadrul fluxului este utilizat funcia seekg() pentru fluxurile de citire i seekp() pentru fluxurile de
scriere. Aceste funcii sunt descrise de urmtoarele prototipuri:
-

int seekg(int pozitie, int origine);


int seekp(int pozitie, int origine);

unde parametrul pozitie determin valoarea poziiei curente, iar parametrul origine
determin n raport cu ce este fixat poziia curent i poate lua trei valori. Sensul acestor valori
este descris n urmtorul tabel:

Valori posibile
ale
parametrului
origine
ios :: beg

Semantica impus de valoare

ios :: cur

ios :: end

poziia curent este fixat n raport cu


nceputul fiierului
poziia curent este fixat n raport cu
poziia curent
poziia curent este fixat n raport cu
sfritul fiierului

Pentru a determina starea fluxului de


date, sunt utilizate o serie de funcii care ofer unele informaii necesare. Funcia eof() permite
determinarea faptului dac s-a ajuns sau nu la sfritul fiierului. Funcia fail() permite a determina
dac s-a produs sau nu o eroare nefatal. Funcia bad() permite a determina dac s-a produs sau nu o
eroare fatal. Funcia good() permite a determina dac operaia anterioar aplicat asupra fluxului s-a
efectuat sau nu cu succes. Funciile respective au urmtoarele prototipuri:
-

int
int
int
int

eof();
fail();
bad();
good();

Funciile date returneaz fie valoarea 0,


fie valoarea 1, n dependen de evenimentul produs. Tabelul ce urmeaz descrie valorile returnate de
funcii.
-

Nume
le
func
iei

eof(
)

fail
()

bad(
)

good
()

Valoa
rea
retur
nat
0

Semantica valorii
returnate

nu s-a ajuns la
sfritul
fiierului
s-a ajuns la
sfritul
fiierului
nu s-a produs o
eroare nefatal
s-a produs o
eroare nefatal

nu s-a produs o
eroare fatal
s-a produs o
eroare fatal
s-a produs o
eroare
operaie
efectuat cu
succes

- Urmeaz un exemplu n care sunt utilizate unele dintre ideile descrise anterior.
- Exemplu. De alctuit un program n care sunt realizate operaiile de transcriere a informaiei
dintr-un fiier cu caractere majuscule i cu caractere minuscule. Operaiile sunt realizate prin dou
metode: una utilizeaz dou fluxuri de date, unul de intrare i altul de ieire, iar alta utilizeaz doar un
singur flux, dar de intrare/ieire.
-

#include<fstream.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#include<conio.h>
void fisierMaj(char *numef)
{
-

int numCar;
char linie[20];
ifstream cit(numef);
if(cit.fail())
{
cerr << "Eroare deschidere.";
exit(0);
}
ofstream scr(numef);
if(scr.fail())
{
cerr << "Eroare deschidere.";
exit(0);
}

for(;!cit.eof();)
{
cit.getline(linie,sizeof(linie));
numCar=cit.gcount();
for(int i=0; *(linie+i);
*(linie+i++)=toupper(*(linie+i)));
scr << linie;
if(numCar!=strlen(linie))
scr << \n;
}
scr.close();
cit.close();

}
//----------------void fisierMin(char *numef)
{
-

long pozitie;
int numCar;
char linie[20];

fstream scr(numef,ios::out | ios::in);


if(scr.fail())
{
cerr << "Eroare deschidere.";
exit(0);
}

for(;!scr.eof();)

{
pozitie=scr.tellg();
scr.getline(linie,sizeof(linie));
numCar=scr.gcount();
for(int i=0; *(linie+i);
*(linie+i++)=tolower(*(linie+i)));
scr.seekg(pozitie, ios::beg);
scr << linie;
if(numCar != strlen(linie))
scr << \n;
}
scr.close();

}
//----------------void main(void)
{
-

char nume[]=import.txt;

fisierMin(nume);
fisierMaj(nume);
getch();

- Pentru a realiza scopul propus, au fost alctuite dou funcii fisierMin() i fisierMaj()
pentru a face transcrierea respectiv cu minuscule i cu majuscule. Prima funcie a folosit dou fluxuri, pe
cnd a doua funcie a folosit doar un flux, dar a mai utilizat o variabil n care se memora la citire poziia
curent pentru a ti locul de scriere a informaiei transformate. Prima funcie nu a avut necesitate n o
astfel de variabil.
- 23. Tratarea excepiilor
- Un program elaborat corect nseamn nu doar prelucrri ale datelor corecte, ci i prelucrarea
datelor eronate. Scenariile descrise de programe trebuie s conin fragmente care ar prelucra i date
generatoare de erori. Procesul de prelucrare a posibilelor erori este unul foarte anevoios: trebuie
evideniate situaiile generatoare de erori, iar apoi trebuie de prelucrat fiecare situaie n parte. Situaiile
generatoare de erori sunt, de fapt nite situaii excepionale i de aceea prelucrarea erorilor poate fi numit
i tratare a excepiilor.
- Limbajul C++ are un mecanism propriu de tratare a excepiilor. Acest mecanism se bazeaz pe
conceptul de clas. Deci o excepie este descris de o clas, avnd o serie de proprieti i de operaii
caracteristice. Din punctul de vedere al programrii, a genera o excepie nseamn a crea un obiect cu
anumite proprieti i operaii concrete, care descrie un anumit tip de erori.
- Pentru prelucrarea excepiilor, exist trei construcii specializate: try{}, throw i catch()
{}. Fiecare dintre construciile date i are rolul su specific n evidenierea i prelucrarea excepiilor.
Blocurile de instruciuni suspectate de a putea genera erori sunt plasate ntre acoladele instruciunii try,
adic formeaz corpul ei:
-

try
{
instr_1;
instr_2;
. . .
instr_k;
}

- Dac valorile datelor sunt n contradicie cu modul lor de prelucrare, trebuie de generat o excepie.
Generarea unei excepii este realizat cu ajutorul instruciunii throw. Instruciunea dat are ca parametru
un obiect de tipul ce corespunde excepiei. De aceea poate fi scris

throw obiectExceptie;

unde obiectExceptie este un obiect creat n baza clasei ce corespunde excepiei. Deseori
obiectul dat este creat chiar n cadrul instruciunii throw cum urmeaz n continuare

throw numeExceptie(lista_par);

unde numeExceptie este numele constructorului clasei care descrie excepia, iar lista_par
este lista parametrilor reali utilizai la generarea obiectului-excepie. Deci numeExceptie este
un tip de date, fie predefinit, fie definit. n cazul cnd este un tip predefinit, lista_par const
dintr-o singur valoare i, de regul, este omis din construcie numele tipului predefinit, rmnnd
doar valoarea. De exemplu, poate fi scris

throw int(3);

ceea ce este echivalent cu

throw 3;

Procesul de generare a excepiei se numete lansarea excepiei.


- Pentru a prelucra excepiile lansate, dup blocul try sunt plasate o serie de construcii de tip
catch:

catch(tip [nume_ob])
{
instr_1;
instr_2;
. . .
instr_k;
}

unde tip este un tip de date predefinit sau definit de utilizator, care descrie tipul excepiei, iar
nume_ob este numele obiectului, care reprezint excepia i care poate lipsi. Deci n acest caz
toate excepiile de acest tip vor fi tratate identic. Dac se dorete existena unei construcii catch
care ar prelucra orice tip de excepii, n locul parametrului sunt plasate trei puncte (. . .):

catch(. . .)
set_instructiuni

- n acelai loc al programului pot fi, de regul, plasate mai multe construcii catch, obinnd un
pachet de astfel de construcii. De aceea ntr-un astfel de pachet trebuie respectate o serie de reguli:
exist o singur excepie care se refer la un tip de date anume;
nu are vreo importan ordinea de amplasare a excepiilor care se refer la diferite tipuri de date;
excepia care se refer la toate tipurile de date, adic cea care conine trei puncte (. . .)este plasat
ultima n secvena instruciunilor catch.
- n continuare, este prezentat un exemplu, excepiile fiind de tipuri predefinite de date. Excepiile
sunt lansate n funcia f(). Dup blocul try sunt plasate trei blocuri catch(): unul pentru excepii de
tip int, altul pentru excepii de tip double i ultimul pentru prelucrarea excepiilor de orice alt tip.
-

#include<iostream.h>
void f(int i)
{
-

if(i<0)
throw -1;
if(i>=0 && i<100)
throw 100.0;
if(i>=1000)
throw float(1000);
cout<<i<<endl;

}
//-------void main()
{
-

int i;
cin>>i;

try
{
f(i);
}
catch(int i)
{
cout<<i<<"-exceptie tip int\n";
}
catch(double)
{
cout<<"exceptie tip double\n";
}
catch(...)
{
cout<<"exceptie generala\n";
}

- Fiindc o excepie este n caz general reprezentat printr-o clas, urmeaz un exemplu de excepie
definit pentru a prelucra situaia de inexistena unui fiier deschis pentru anumite operaii.
-

#include<fstream.h>
#include<iostream.h>
#include<conio.h>
class FisierInexistent{};
void main()
{
char numePrenume[100];
try
{
ifstream cit("persoane.txt");
if(cit.fail())
{
throw FisierInexistent();
}
cit.getline(numePrenume,100);
cout<<numePrenume<<endl;
}
catch(FisierInexistent e)
{
cout<<"Eroare de deschidere: fisier inexistent";
}
getch();
}

Dac fiierul deschis nu exist, va fi lansat excepia FisierInexistent i ca rezultat al


prelucrrii excepiei date va fi afiat mesajul Eroare de deschidere: fisier inexistent.
Clasa
care
reprezint
excepia
FisierInexistent este una foarte simpl, ne-coninnd nici un membru. Totui, ea poate fi
proiectat, astfel nct s aib o structur mai complex. Poate conine n calitate de informaie numele
fiierului, poate avea o serie de funcii membru, de exemplu, constructori sau alte funcii implicate n
prelucrarea situaiei excepionale. Exemplul precedent va fi re-scris desfurnd puin clasa
FisierInexistent.
-

#include<fstream.h>
#include<iostream.h>
#include<conio.h>
class FisierInexistent
{

char numeFisier[50];

public:
-

FisierInexistent(char *nume)
{
strcpy(numeFisier, nume);
}
void MesajEroare()
{
cout<<Eroare: fisierul \<<numeFisier
<<\ nu poate fi gasit!<<endl; }
};

void main()
{
-

char numePrenume[100];
char numef[]=persoane.txt;

try
{
ifstream cit(nume);
if(cit.fail())
{
throw FisierInexistent(nume);
}
cit.getline(numePrenume,100);
cout<<numePrenume<<endl;
}
catch(FisierInexistent e)
{
e.MesajEroare();
}
getch();

- Din aceste exemple se vd doar unele caracteristici ale excepiilor. Pentru a putea aprecia puterea
excepiilor, trebuie de inut cont c un algoritm serios poate consta dintr-o ierarhie de apeluri de funcii.
Eroarea poate surveni n orice punct al ierarhiei i n acest caz este necesar o terminare corect a
proceselor lansate. O latur forte a mecanismului excepiilor este anume terminarea corect a proceselor
lansate.
- Este de menionat faptul c construciile de tip try pot fi imbricate, adic incluse unele n altele.
Cea mai simpl ierarhie de imbricare de acest tip ar consta din dou niveluri i ar putea avea urmtoarea
reprezentare generalizat:
-

try
{
//sectorul A inceput
. . .
//sectorul A sfirsit
try
{
//sectorul B inceput
. . .
//sectorul B sfirsit
}
//sectorul C inceput
catch( )
{
. . .
}
. . .

//sectorul C sfirsit
//sectorul D inceput
. . .
//sectorul D sfirsit
}
//sectorul E inceput
catch( )
{
. . .
}
. . .
//sectorul E sfirsit

O astfel de construcie are mai multe scenarii de execuie care depind de locul lansrii excepiei i
de tipul excepiilor interceptate, care formeaz blocul de prelucrare intern i blocul de prelucrare
extern. Dac excepia este lansat n sectorul A sau D, atunci execuia este transferat n sectorul E
unde va fi selectat, dac exist, prelucrarea necesar dup tipul ei. Dac excepia este lansat n
sectorul B, atunci execuia este transferat n sectorul C unde va fi selectat, dac exist,
prelucrarea necesar dup tipul ei, iar apoi se va trece la sectorul D. Dac n sectorul C nu exist
blocul necesar de prelucrare, atunci se va trece la sectorul E, ncercndu-se gsirea blocului
necesar de prelucrare aici. Este interesant faptul c o excepie poate fi lansat i n sectorul C n
cadrul unuia dintre blocurile catch(). Poate fi lansat o excepie nou sau poate fi relansat
excepia veche. O relansare a excepiei poate fi fcut pentru a schimba ntr-un fel scenariul de
prelucrare a ei. La lansarea excepiei n sectorul C execuia va fi direcionat n sectorul E.

- Bibliografie
1)
2)
3)
4)
5)
6)
7)
8)
9)

G.Booch. Object-Oriented Design and Application. 1991.


Kip R. Irvine. Object-Oriented Programming. - Prentice-Hall, 1997.
Schildt Herbert. C++ Manual complet. Bucureti: Teora, 2001.
Jamsa Kris. Succes cu C++. Bucureti: ALL EDUCATIONAL S.A.,1997.
D.M.Popovici, I.M.Popovici, I.Tnase C++.Tehnologia orientat pe obiect. Aplicaii,Teora
1996.
Somnea D., Tutureanu D. Iniiere n C++. Programarea obiect orientat.Teora 1993.
Negrescu Liviu. Limbajele C si C++ pentru nceptori. Limbajul C++ (vol. II). Cluj-Napoca,
2000.
. ++, 3- ./. . ., M.,
, 1999.
.., ... ++. M., ., 2000.

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