Documente Academic
Documente Profesional
Documente Cultură
(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:
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.
- 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
};
- - 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:
-
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
}
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();
}
- 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.
-
class nume_clasa
{
...
nume_clasa([tip1 param1,...,tipN paramN]);
...
};
//---------------------nume_clasa :: nume_clasa([tip1 param1,...,tipN paramN])
{
//instructiuni
}
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:
-
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
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:
-
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;
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;
cin>>var1>>var2>>...>>varN;
unde var1, var2, , varN (N1) sunt variabilele n care sunt citite valori.
cin>>var1;
cin>>var2;
. . .
cin>>varN;
unsigned u;
float f;
long double ld;
cin >> u >> f >> ld;
sau
- 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;
- 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
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.
- - - - - - - 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)
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
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
-
int i=10;
cout<<setiosflags(ios::left)<<setw(5)<<i
<<setfill(-)<<setiosflags(ios::right)
<<setw(5)<<i<<endl;
Rezultatul afirii:
- - - - - - - - - 10
---10
float f=20;
cout<<f<< <<setiosflags(ios::showpos)<<f<<
<<setiosflags(ios::showpoint)<<f<< <<endl;
Rezultatul afirii:
- - - - - - - - - - - - - - - - - 20 +20 +20.000000
long l=0x49;
cout << setiosflags(ios::showbase | ios::hex);
cout << setw(6) << l << setiosflags(ios::internal)
<< setw(6) << l << endl;
Rezultatul afirii:
- - - - - - - - - - - 0x490x
49
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
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:
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
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.
-
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++)
{
-
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>
.
};
private
protected
public
class Fisier
-
char Nume[256];
unsigned long Dimensiune;
char Data[20];
public:
void Nimicire();
void Copiere(char *numeFisier);
void Redenumire(char *numeNou);
};
char Tip[15];
public:
void Afisare();
void Imprimare();
-
};
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:
//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
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.
- 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:
-
};
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;
}
#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();
}
- 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
-
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) :
-
};
//==============
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();
}
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;
};
- 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
- 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
-
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:
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()
-
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.
-
{
-
- 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()
-
. . .
}
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();
.
};
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,
-
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()
{
-
10 10
20 20
40 40
ri+=rj;
cout <<i<< <<ri<<endl;
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()
{
-
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);
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;
int i=10;
int &ri=i;
long j=10;
int &rj=j;
cout <<i<< <<ri<<endl;
cout <<j<< <<rj<<endl;
i++;
j++;
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:
- 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:
-
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)};
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)
};
nume_cl nume_to[dim]={nume_cl(val1),nume_cl(val2)
,,nume_cl(valk)};
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 puncte[100];
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
0 ind dim-1
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;
nume_var_po = nume_to;
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 ->
-
- 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.
pob = &ob;
pod = &od;
pob = pod;
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:
-
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:
-
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);
};
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
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;
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);
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)];
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;
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
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];
delete[] adr_locatie;
I.
II.
III.
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);
. . .
};
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::talbou(int d, int val)
{
-
int *date;
int dim;
- 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
-
- 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
C.Adunare(A.Produs(B))
operator. n rest este o funcie membr obinuit i deci va fi descris i definit ca i oricare alt
funcie membr a clasei:
-
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.
#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;
}
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.
#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();
}
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:
-
- - forma postfix:
-
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);
- - forma prefix:
-
- - forma postfix:
-
sau, respectiv:
- 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:
-
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 [].
#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);
. . .
};
. . .
void main()
{
complex c(1.3,-2.5);
cout << c() << endl;
c(3.7,2.8);
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:
-
class complex
{
. . .
complex operator ,(complex c);
friend complex operator ,(complex c, float f);
. . .
};
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 ();
. . .
}
#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 max=tb[0];
for (int i=1; i< n_el ; i++)
if (max < tb[i]) max=tb[i];
return max;
}
- 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()
{
- 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 *sir;
sir = new char[3];
sir[0] = x;
sir[1] = y;
sir[2] = \0;
return
sir;
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);
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:
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;
}
#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:
-
- 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.
-
(lista_pf1)
(lista_pfn)
(lista_pf1)
(lista_pfn)
(lista_pf1)
(lista_pfn)
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:
-
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:
-
- 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;
};
};
//---------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();
}
#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();
}
- 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=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:
tip nume_cl::nume_functie(lista_param)
{
. . .
}
nume_obiect.nume_functie(lista_param)
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
//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);
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
{
-
};
- 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:
-
- 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:
-
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];
#include<iostream.h>
static union
{
-
int i;
long l;
};
void main()
{
::l=0;
::i=90;
cout<< ::l << endl;
}
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:
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
ofstream sc;
sc.open("t.txt", ios::out);
fstream cit_sc;
cit_sc.open("a.txt", ios::in | ios::out |
ios::binary);
fstream
int get();
istream & get(char & car);
istream & get(char *sir, int max,
char delim=\n);
char c;
c=cit.get( );
char c;
cit.get(c);
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;
long tellg();
long tellp();
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
ios :: cur
ios :: end
int
int
int
int
eof();
fail();
bad();
good();
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];
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);
throw 3;
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();
}
#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)