- •Programare orientată pe obiecte
- •§1. Principiile programării orientate pe obiecte.
- •§3. Constructori şi destructori.
- •8) Aceste mesaje apar după ce se termină lucrul programului.
- •§4. Operaţii de intrare/ieşire a informaţiei.
- •§5. Moştenire simplă.
- •§6. Moştenire multiplă
- •§7. Moştenirea pe mai multe niveluri. Clase virtuale
- •§9. Definirea şi utilizarea referinţelor
- •§10. Tablouri de obiecte. Pointeri şi referinţe la obiecte. Pointeri la membrii clasei
- •§11. Dirijarea dinamică a memoriei
- •§12. Constructor de copiere
- •§13. Funcţii prietene şi clase prietene
- •§14. Supraîncărcarea operatorilor
- •§15. Supraîncărcarea operatorilor prin funcţii prietene
- •§16. Supraîncărcarea unor operatori speciali
- •Operatorii de incrementare şi decrementare
- •Operatorii de inserţie şi extragere
- •Operatorul indice
- •Operatorul funcţie
- •Operatorii new şi delete
- •Operatorul virgulă
- •Operatorul de conversie
- •§17. Funcţii-şablon şi clase-şablon
- •§18. Realizarea conceptului de polimorfism
- •§19. Clase abstracte
- •§20. Membrii statici ai clasei
- •§23. Tratarea excepţiilor
- •Bibliografie
8) Aceste mesaje apar după ce se termină lucrul programului.
Destructorii se apelează automat de către sistem pentru
fiecare obiect creat din program, în ordine inversă:
Obiectul (Ion Padure) a fost distrus.
Obiectul (Necunoscut) a fost distrus.
Obiectul (Dan Balan) a fost distrus.
Obiectul (Ion Padure) a fost distrus.
Obiectul (Petru Moraru) a fost distrus.
§4. Operaţii de intrare/ieşire a informaţiei.
În limbajul C++ sunt admise toate operaţiile de intrare/ieşire caracteristice limbajului C. Suplimentar, în limbajul C++ au fost definite operaţii noi de intrare/ieşire care sunt create în stilul programării orientate pe obiecte. Au fost create o serie de clase orientate la intrarea/ieşirea informaţiei. În baza acestor clase sunt definite obiecte care, la rândul său, includ operaţii ce permit intrarea/ieşirea informaţiei. O astfel de abordare şi stil de realizare, are avantajul de a putea fi extinsă pentru necesităţile unei clase concrete.
Pentru ieşirea sau afişarea informaţiei, este definit obiectul cout, iar pentru intrarea sau citirea informaţiei, este definit obiectul cin. Pentru afişarea mesajelor despre erori sunt definite obiectele cerr şi clog. Fiecare dintre obiecte poate utiliza atât operatorii, cât şi funcţiile caracteristice clasei din care este instanţiat el. În contextul obiectelor cout, cerr şi clog este utilizat operatorul <<, numit operator de inserţie sau scriere, iar în contextul obiectului cin este utilizat operatorul >>, numit operator de extragere sau citire. O formă generală de utilizare a operatorului << ar fi următoarea:
cout<<exp1<<exp2<<...<<expN;
unde exp1, exp2, …, expN (N1) sunt expresiile a căror valori sunt afişate. Expresiile concatenate într-o expresie lungă pot fi afişate separat:
cout<<exp1;
cout<<exp2;
. . .
cout<<expN;
Un exemplu de afişare a valorilor unor variabile:
int i=-27;
char c=’a’;
char s[]=”Un sir”;
long l=100000;
cout<<i;
cout<<c;
cout<<s;
cout<<l;
Rezultatul afişării:
-27aUn sir100000
Desigur că scriind:
cout<<i<<c<<s<<l;
va fi obţinut acelaşi rezultat al afişării.
Pentru operatorul de citire >> este valabilă următoarea formă generală:
cin>>var1>>var2>>...>>varN;
unde var1, var2, …, varN (N1) sunt variabilele în care sunt citite valori.
Acelaşi lucru poate fi realizat şi în expresii separate:
cin>>var1;
cin>>var2;
. . .
cin>>varN;
Un exemplu de utilizare a obiectului cin:
unsigned u;
float f;
long double ld;
cin >> u >> f >> ld;
Delimitatori ai valorilor introduse sunt spaţiile albe ’ ’, ’\t’, ’\n’.
Pentru a putea utiliza în programe obiectele şi operatorii respectivi, este inclus fişierul antet <iostream.h>, unde sunt efectuate descrierile necesare referitoare la aceste obiecte.
Obiectele cout şi cin permit, de asemenea, şi operaţii de formatare care sunt realizate prin intermediul unor construcţii numite manipulatori. Un manipulator schimbă starea fluxului pregătindu-l pentru careva operaţii de formatare. Pentru a putea folosi manipulatorii, este necesară includerea fişierului antet <iomanip.h>. În caz general, un manipulator acţionează asupra fluxului, acţionând asupra obiectului implicat în operaţiile de intrare/ieşire prin intermediul operatorului corespunzător:
cout << manipulator[ << . . .];
sau
cin >> manipulator[ >> . . .];
unde punctele de suspensie ”. . .” înseamnă o posibilă continuare a construcţiei.
Fluxul de intrare are mai puţini manipulatori decât cel de ieşire. Pentru ignorarea spaţiilor albe la citirea informaţiei, este utilizat manipulatorul ws. În exemplul următor:
double d;
cin >> ws >> d;
sunt ignorate spaţiile albe în procesul citirii valorii în variabila d.
În continuare, vor fi caracterizaţi manipulatorii fluxului de ieşire. Pentru a trece din rând nou la ieşirea (afişarea) informaţiei, este utilizat manipulatorul endl. Din exemplul ce urmează se poate vedea că acest manipulator poate fi modelat afişând secvenţa 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 afişării:
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 numeraţie octal;
dec permite fixarea sistemului de numeraţie zecimal;
hex permite fixarea sistemului de numeraţie hexazecimal.
Oricare dintre aceşti manipulatori îşi fixează acţiunea până la anularea ei de un alt manipulator din acest grup.
int i=10;
cout<<i<<” ”<<oct<<i<<” ”<<hex<<i<<” ”<<dec<<i<<endl;
Rezultatul afişării:
10 12 a 10
Manipulatorii descrişi anterior sunt manipulatori ne-parametrizaţi. Există o serie de manipulatori parametrizaţi, a căror descriere urmează în continuare. Ei sunt realizaţi sub formă de funcţii, deci, modul lor de utilizare este asemănător cu apelarea unor funcţii.
Un manipulator a cărui acţiune este echivalentă cu acţiunea manipulatorilor oct, dec, hex este manipulatorul setbase. Modul lui de utilizare este
setbase(baza)
unde parametrul baza poate lua valorile 8, 10 sau 16, permiţând respectiv fixarea sistemului de numeraţie octal, zecimal sau hexazecimal. Va fi rescris exemplul anterior utilizând setbase:
int i=10;
cout<<i<<” ”<<setbase(8)<<i<<” ”<<setbase(16)<<i<<” ”<<setbase(10)<<i<<endl;
Rezultatul afişării:
10 12 a 10
Dacă este necesară fixarea lăţimii câmpului de afişare, este utilizat manipulatorul setw. Forma generală este:
setw(latime)
unde parametrul latime reprezintă o expresie întreagă care dictează lăţimea câmpului de afişare. Lăţimea câmpului este fixată doar pentru o singură afişare, după care se revine la valoarea implicită egală cu unu.
Un exemplu de utilizare a acestui manipulator:
int i=10, lat=5;
char c=’A’;
cout<<setw(3)<<i<<setw(lat)<<c<<endl;
Rezultatul afişării (fiecare celulă reprezintă o poziţie pe ecran):
|
1 |
0 |
|
|
|
|
A |
Din exemplul precedent se vede că dacă valoarea afişată are mai puţine simboluri decât numărul de poziţii ale câmpului de afişare, atunci poziţiile rămase sunt completate cu spaţii ’ ’. Acesta este caracterul implicit de completare. Însă este posibilă fixarea altui caracter de completare, utilizând manipulatorul setfill. Forma generală este:
setfill(caracter_completare)
unde parametrul caracter_completare reprezintă o expresie, care oferă caracterul de completare. Caracterul de completare este fixat până la o nouă fixare. În continuare este re-scris exemplul precedent, utilizând şi manipulatorul setfill:
int i=10, lat=5;
char c=’A’;
cout<<setfill(’.’);
cout<<setw(3)<<i<<setw(lat)<<c<<endl;
Rezultatul afişării:
. |
1 |
0 |
. |
. |
. |
. |
A |
Pentru a fixa exactitatea de afişare a valorilor cu virgulă mobilă este utilizat manipulatorul setprecision. Forma generală de utilizare este următoarea:
setprecision(exactitate)
unde parametrul exactitate este o expresie întreagă, care dictează numărul de cifre semnificative după virgulă. Exactitatea este fixată pentru toate afişările, până 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;
cout << f << ” ” << d << endl;
for(i=0;i<6;i++)
cout<<setprecision(i)<<f<<” ”<<d<<endl;
Rezultatul afişării:
1.2 -15.456328
1.2 -15.456328
1.2 -15.5
1.2 -15.46
1.2 -15.456
1.2 -15.4563
1.2 -15.45633
Un manipulator cu o gamă largă de acţiuni de formatare este manipulatorul setiosflags. Forma generală de aplicare a acestui manipulator este:
setiosflags(fanioane)
unde parametrul fanioane permite fixarea unor proprietăţi, numite fanioane. Fiecare fanion este responsabil de o anumită acţiune. 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 rezoluţiei.
Tabelul următor descrie acţiunile, ce ţin de fiecare fanion:
Nr. |
Nume fanion |
Acţiune caracteristică |
1 |
ios::skipws |
ignorarea spaţiilor albe |
2
|
ios::left
|
alinierea informaţiei în partea stângă a câmpului |
3
|
ios::right
|
alinierea informaţiei în partea dreaptă a câmpului |
4
|
ios::internal
|
alinierea informaţiei în ambele părţi dacă este posibilă |
5 |
ios::showpos |
forţarea semnului |
6
|
ios::showbase
|
baza sistemului de numeraţie |
7
|
ios::showpoint
|
forţarea punctului pentru numerele cu virgulă mobilă |
8
|
ios::fixed
|
afişarea numerelor reale în format cu punct fix |
9
|
ios::scientific
|
afişarea numerelor reale în format ştiinţific |
10
|
ios::hex
|
afişarea numerelor întregi în format hexazecimal |
11
|
ios::oct
|
afişarea numerelor întregi în format octal |
12
|
ios::dec
|
afişarea numerelor întregi în format zecimal |
13
|
ios::stdio
|
golirea fluxul stdout şi stderr |
14
|
ios::uppercase
|
scrierea numerelor hexazecimale cu litere majuscule |
15
|
ios::unitbuf
|
golirea tuturor fluxurilor de date dupa citirea datelor |
Iată o serie de exemple ce descriu acţiunile manipulatorului setiosflags.
Alinierea stânga sau dreapta:
int i=10;
cout<<setiosflags(ios::left)<<setw(5)<<i
<<setfill(’-’)<<setiosflags(ios::right)
<<setw(5)<<i<<endl;
Rezultatul afişării:
1 |
0 |
|
|
|
- |
- |
- |
1 |
0 |
Forţarea semnului şi a punctului:
float f=20;
cout<<f<<’ ’<<setiosflags(ios::showpos)<<f<<’ ’
<<setiosflags(ios::showpoint)<<f<<’ ’<<endl;
Rezultatul afişării:
2 |
0 |
|
+ |
2 |
0 |
|
+ |
2 |
0 |
. |
0 |
0 |
0 |
0 |
0 |
0 |
|
Manipulatorul setiosflags permite combinarea mai multor fanioane concomitent. Pentru aceasta este utilizat operatorul | (SAU). Iată un exemplu:
long l=0x49;
cout << setiosflags(ios::showbase | ios::hex);
cout << setw(6) << l << setiosflags(ios::internal)
<< setw(6) << l << endl;
Rezultatul afişării:
|
|
0 |
x |
4 |
9 |
0 |
x |
|
|
4 |
9 |
Pentru a restabili fanioanele la valorile implicite, este utilizat manipulatorul resetiosflags, care acţionează similar cu manipulatorul setiosflags. În exemplul următor este restabilit fanionul ios::showpoint:
double d=-320;
cout <<setiosflags(ios::showpoint) << d << ’ ’
<<resetiosflags(ios::showpoint) << d <<endl;
Rezultatul afişării:
- |
3 |
2 |
0 |
. |
0 |
0 |
0 |
0 |
0 |
0 |
|
- |
3 |
2 |
0 |
Clasele orientate la intrarea/ieşirea informaţiei au pe lângă operatori şi o serie de funcţii cu acţiuni specifice, utile în procesul de intrare/ieşire a informaţiei. Astfel, funcţiile corespunzătoare pot fi utilizate cu obiectele cout şi cin.
Pentru a citi un caracter din fluxul de intrare, este utilizată funcţia get(). Forma de utilizare este următoarea
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 doreşte a citi numele şi prenumele unei persoane într-o variabilă, atunci o astfel de soluţie
char persoana[100];
cin >> persoana;
nu este corectă, fiindcă în variabila persoana va fi citit doar numele persoanei, prenumele fiind ignorat, fiindcă spaţiul joacă rolul de trecere la prelucrarea următorului câmp de intrare. Pentru a putea citi propoziţii, va fi utilizată funcţia getline(), care are următoarele forme:
getline(char *sir, int lung_max);
getline(char *sir, int lung_max, char caracter_oprire);
unde parametrul sir este şirul în care se citeşte, parametrul lung_max este lungimea maximală a informaţiei citite, iar parametrul caracter_oprire reprezintă caracterul la întâlnirea căruia se va întrerupe procesul de citire. Pentru prima variantă a funcţiei, caracterul de oprire este ’\n’, generat de tasta Enter. Astfel sarcina propusă anterior poate fi soluţionată aşa:
char persoana[100];
cin.getline(persoana, sizeof(persoana));
Dacă este necesar de a afla câte caractere au fost citite în fluxul neformatat este aplicată funcţia gcount().
char nuA[50];
int i;
cin.getline(nuA, sizeof(nuA), ’a’);
i=gcount();
cout << i << endl;
Fragmentul anterior afişează câte simboluri au fost citite în variabila nuA până la întâlnirea primului simbol ’a’.
Desigur că şi obiectul cout are o serie de funcţii utile. Funcţia width() are două variante. Una permite citirea lăţimii curente, iar alta permite fixarea lăţimii curente. Iată forma lor generală:
int width();
şi
int width(latime);
unde latime este expresia a cărei valoare va fi luată drept bază pentru fixarea lăţimii câmpului de afişare. De exemplu:
float f=10.25;
cout.width(8);
cout<<f<<endl;
Rezultatul afişării:
|
|
|
1 |
0 |
. |
2 |
5 |
Două forme similare are şi funcţia precision(), care se referă la exactitatea de afişare a valorilor cu virgulă mobilă: varianta fără parametri returnează exactitatea curentă, iar varianta cu parametru fixează exactitatea necesară. De exemplu
double d=-114.361782;
cout.precision(4);
cout<<d<<endl;
Rezultatul afişării:
- |
1 |
1 |
4 |
. |
3 |
6 |
1 |
8 |
Pentru a determina caracterul de umplere a poziţiilor ne-acoperite de simbolurile valorii afişate poate fi utilizată funcţia fill() fără parametri. De exemplu, fragmentul ce urmează:
char cu;
cu=cout.fill();
cout << cu << endl;
va afişa caracterul de umplere curent cu, determinat utilizând funcţia fill(). Pentru a fixa caracterul de umplere, este utilizată, de asemenea, funcţia fill(caracter_umplere) cu parametri, având 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 afişării:
* |
* |
* |
- |
2 |
4 |
9 |
7 |
9 |
3 |
Pentru a putea lucra cu fanioanele caracterizate anterior, poate fi utilizată funcţia flags(), care are o variantă fără parametri şi o variantă cu parametri.
int fnv = cout.flags();
int fn = 0x1234;
cout.flags(fn);
cout << 239 << endl;
cout.flags(fnv);
În acest exemplu sunt utilizate ambele variante, care permit fixarea valorii vechi a fanioanelor şi revenirea la această valoare după afişarea valorii întregi 239.
Exemplu. De alcătuit un program în care este modelat un cuprins de carte.
#include<iostream.h>
#include<iomanip.h>
#include<string.h>
#define latimePagina 65
void main()
{
char *paragrafe[]={”Cuprins”,”Introducere”,
”Clase si obiecte”,
”Constructor.Destructor”};
int pagini[]={3,10,12};
int i, lung;
lung=strlen(paragrafe[0]);
cout << setw((latimePagina+lung)/2); //Centrare
cout << paragrafe[0] << endl; //Cuprins
cout << setfill(’.’);
for(i=0; i<3; i++)
{
cout << paragrafe[i+1];
lung=strlen(paragrafe[i+1]);
cout << setw(latimePagina-lung);
cout << pagini[i] << endl;
}
}
Rezultatul afişării:
Cuprins
Introducere.....................................................3
Clase si obiecte...............................................10
Constructor.Destructor.........................................12
