CATEGORII DOCUMENTE |
Operatiile de scriere pe dispozitivul standard de iesire, intr-un fisier, sau intr-o zona de memorie se pot efectua cu ajutorul operatorului <<, care in acest caz se va numi operator de inserare.
Operandul din partea stanga al operatorului << trebuie sa fie un obiect al clasei ostream (bineinteles si obiectele claselor derivate din clasa ostream se considera obiecte ale clasei ostream). Pentru operatiile de scriere pe dispozitivul standard de iesire se va folosi obiectul cout.
Operandul din partea dreapta al operatorului << poate fi o expresie. Pentru tipul corespunzator expresiei, trebuie sa fie supraincarcat operatorul <<. In cazul tipurilor standard operatorul de inserare este supraincarcat cu o functie membru de forma:
ostream& operator << (nume_tip_standard);
Mentionam ca pentru tipurile abstracte programatorul poate supraincarca operatorul de inserare. Sa consideram acum din nou exemplul prezentat in sectiunea 15.1, intr-o forma mai detaliata, pentru a ilustra asemanarea cu utilizarea functiei printf. Fisierul stream1.cpp
#include <stdio.h>
#include <iostream.h>
#include <conio.h>
int main()
Dupa executarea programului se obtine:
230
230
543.67
543.67
a
a
exemplu
exemplu
0xfff4
FFF4
Observam ca rezultatul afisarii sirurilor de caractere si datelor de tip int, double si char este acelasi. In cazul afisarii unui pointer, valoarea afisata este identica, dar formatul difera (daca se scrie cu printf se folosesc litere mari si nu se afiseaza baza, iar in cazul scrierii cu cout se folosesc litere mici si se afiseaza baza).
Deoarece operatorul << returneaza o referinta catre clasa curenta, operatorul se poate aplica in mod inlantuit. Acest lucru este prezentat in exemplul urmator. Fisierul stream2.cpp
#include <stdio.h>
#include <iostream.h>
#include <conio.h>
int main()
Prin executie obtinem:
x (dupa incrementare) = 11
x (inainte de incrementare) = 10
x (dupa incrementare) = 11
x (inainte de incrementare) = 10
In afara de faptul ca operatorul de inserare se poate aplica in mod inlantuit, se observa ca evaluarea expresiilor afisate s-a facut in ordine inversa comparativ cu afisarea. Numai in acest fel se poate explica faptul ca valoarea afisata mai repede este deja incrementata, iar cea afisata mai tarziu are valoarea initiala. De fapt si pentru functia printf este valabila acelasi lucru.
Ordinea de evaluare a expresiilor, care se afiseaza folosind operatorul de inserare in mod inlantuit, este ilustrata mai explicit de urmatorul exemplu. Fisierul stream3.cpp
#include <iostream.h>
#include <conio.h>
char* f1()
char* f2()
int main()
Dupa executarea programului obtinem:
Evaluare functie f2
Evaluare functie f1
afisare 1
afisare 2
Deci evaluarea functiei f2 s-a facut inainte de evaluarea functiei f1.
In limbajul C functia printf ne permite afisarea datelor conform unui format specificat de programator. Acest lucru se poate realiza si cu ajutorul ierarhiilor de clase definite in limbajul C++. In clasa ios s-a declarat o data membru x_flags, care se refera la formatul cu care se vor efectua operatiile de intrare/iesire. Tot in clasa ios s-a definit un tip enumerare cu care se pot face referiri la bitii datei membru x_flags. Tipul enumerare este urmatorul:
class ios ;
};
Data membru x_flags are o valoare implicita pentru fiecare tip standard. Astfel rezultatul afisarii datelor care apartin tipurilor standard, va fi in mod implicit identic cu rezultatul afisarii cu functia printf, folosind specificatori de format care corespund tipurilor respective. Prezentam aceasta legatura intre tipuri si specificatori de format, in tabelul 4.
Daca se modifica bitii corespunzatori datei membru x_flags, atunci si rezultatul afisarii se va schimba, conform formatului specificat. Acest lucru se poate realiza cu ajutorul unor functii membru.
Tip |
Specificator de format corespunzator |
int |
%d |
long |
%ld |
unsigned |
%u |
long unsigned |
%lu |
float |
%g |
double |
%lg |
long double |
%Lg |
char |
%c |
sir de caractere |
%s |
Tabelul 4. Legatura dintre tipuri si specificatori de format
Pentru a putea lucra mai usor cu bitii corespunzatori formatului, s-au determinat trei grupe ale bitilor datei membru x_flags. Fiecare grupa are un nume, care este de fapt numele unei constante statice de tip long declarate in clasa ios. Aceste grupe sunt:
adjustfield (right, left si internal), pentru modul de cadrare;
basefield (dec, oct si hex), pentru determinarea bazei;
floatfield (scientific si fixed), pentru scrierea numerelor reale.
Fiecare grupa are proprietatea ca numai un singur bit poate fi setat in cadrul grupei. Bitii datei membru x_flags pot fi setati cu ajutorul functiei membru setf al clasei ios. Functia membru setf are doua forme:
long setf(long format);
si
long setf(long setbit, long grupa);
Prima varianta a functiei membru setf seteaza bitii corespunzator parametrului de tip long: format. Daca un bit din format este egal cu unu, atunci bitul corespunzator din x_flags va fi unu, iar daca bitul din format este egal cu zero, atunci bitul corepunzator din x_flags ramane neschimbat.
A doua varianta a functiei membru setf seteaza un bit din una dintre cele trei grupe adjustfield, basefield sau floatfield. Cu ajutorul parametrului setbit se determina bitul care se va seta. In locul unde se afla bitul trebuie sa fie unu, iar in rest zero. In parametrul al doilea trebuie specificat numele grupei. In acest caz se anuleaza bitii corespunzatori grupei dupa care se seteaza bitii din setbit. Ambele variante ale functiei setf returneaza valoarea datei membru x_flags inainte de modificare.
Referirea la bitii datei membru x_flags se face cu numele clasei ios, urmata de operatorul de rezolutie si numele bitului din tipul enumerare. Referirea la numele unei grupe se face in mod analog, inlocuid numele din tipul enumerare cu numele grupei. Utilizarea functiei membru setf este ilustrata de urmatorul exemplu. Fisierul stream4.cpp
#include <stdio.h>
#include <iostream.h>
#include <conio.h>
void binar_c( long x )
printf('n');
}
void nume_bit_1( char* s[], long x )
void afisare( char* s[], long x )
int main() ;
clrscr();
afisare( nume_enum, cout.flags() );
cout.setf(ios::oct, ios::basefield);
afisare( nume_enum, cout.flags() );
cout << 36 << 'n';
afisare( nume_enum, cout.flags() );
cout.setf(ios::showbase);
cout.setf(ios::hex, ios::basefield);
afisare( nume_enum, cout.flags() );
cout << 36 << 'n';
afisare( nume_enum, cout.flags() );
return 0;
}
Prin executarea programului obtinem:
x_flags: 00000000 00000000 00100000 00000001
skipws unitbuf
x_flags: 00000000 00000000 00100000 00100001
skipws oct unitbuf
44
x_flags: 00000000 00000000 00100000 00100001
skipws oct unitbuf
x_flags: 00000000 00000000 00100000 11000001
skipws hex showbase unitbuf
0x24
x_flags: 00000000 00000000 00100000 11000001
skipws hex showbase unitbuf
Functia afisare, din exemplul de mai sus, afiseaza mai intai bitii datei membru x_flags, dupa care se scriu numele bitilor cu valoarea egala cu unu. Mai intai s-a setat bitul oct si s-a afisat valoarea constantei de tip intreg 36, folosind o conversie in octal. Astfel s-a obtinut valoarea 44. Dupa aceea s-au setat bitii hex si showbase, si s-a afisat valoarea constantei inca o data, astfel obtinand valoarea 0x24. Observam ca folosind a doua varianta a functiei membru setf pentru setarea bitului hex, s-a obtinut si anularea bitului oct, asa cum am dorit.
Mentionam ca numele si valoarea tuturor bitilor tipului enumerare s-ar fi putut afisa de exemplu cu urmatoarea functie:
void nume_bit( char* s[], long x )
O alta observatie este urmatoarea. Pentru afisarea bitilor datei membru x_flags s-a folosit functia printf si nu ierarhia de clase declarata in fisierul iostream.h. Desi in general o functie de forma
void binar_stream( long x )
ar afisa corect bitii datei membru x_flags, vor apare erori in cazul in care este setat bitul showbase si unul dintre bitii oct sau hex. In aceste cazuri se va afisa si baza setata, deci numerele intregi vor fi precedate de zero in cazul conversiei in octal, si de 0x sau 0X in cazul conversiei in hexazecimal.
Ca si in cazul functiei printf, se poate determina lungimea minima a campului in care se va afisa data respectiva. Aceasta valoare este memorata in data membru x_width a clasei ios. Valoarea implicita a datei membru x_width este zero, ceea ce inseamna ca afisarea se va face pe atatea caractere cate sunt necesare.
Valoarea datei membru x_width poate fi determinata sau modificata cu functia membru width a clasei ios. Ea are urmatoarele doua forme:
int width();
si
int width( int lungime );
Prima forma a functiei membru width returneaza valoarea datei membru x_width. A doua varianta modifica valoarea datei membru x_width la valoarea determinata de lungime, si returneaza vechea valoare a lui x_width.
Este foarte important de mentionat ca dupa orice operatie de intare/iesire valoarea datei membru x_width se va reseta la valoarea zero. Deci daca nu se determina o lungime a campului inainte de o operatie de inserare, atunci se va folosi valoarea implicita.
Daca lungimea campului, in care se face afisarea, este mai mare decat numarul de caractere, care vor fi afisate, atunci cadrarea se va face in mod implicit la dreapta, si spatiul ramas se va completa cu caractere de umplere. In mod implicit caracterele de umplere sunt spatii, dar ele pot fi modificate cu ajutorul functiei membru fill a clasei ios. Clasa ios are o data membru x_fill in care se memoreaza caracterul de umplere. Functia membru fill are urmatoarele doua forme:
char fill();
si
char fill( char car );
Prima varianta returneaza caracterul de umplere curent. A doua forma a functiei membru fill modifica data membru x_fill la caracterul car, si returneaza vechea valoare a caracterului de umplere.
In cazul in care se afiseaza valoarea unor numere reale, se poate determina precizia, adica numarul de zecimale, care se va folosi la scrierea datelor. Clasa ios are o data membru x_precision, care are valoarea implicita egala cu zero. In mod implicit datele de tip real se vor afisa cu sase zecimale. Functia membru precision are urmatoarele doua forme:
int precision();
si
int precision( int p );
Prima varianta a functiei membru precision returneaza valoarea curenta a datei membru x_precision. A doua varianta atribuie valoarea parametrului p datei membru x_precision si returneaza valoarea anterioara. Functia membru precision s-a folosit in fisierul sup_fun1.cpp pentru a determina numarul de cifre care vor fi afisate in cazul calcularii numarului π. Prezentam in continuare un alt exemplu, in care se vor folosi functiile membru din acest paragraf. Fisierul stream5.cpp
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
const double pi = 3.141592653;
void scrie_width_precision_c()
void afisare_pi_c()
int main()
Dupa executarea programului obtinem:
x_width: 0
x_precision: 0
*3.141593*
x_width: 7
x_precision: 2
* 3.14*
x_width: 0
x_precision: 2
*3.14*
x_width: 7
x_precision: 2
*@@@3.14*
Caracterele '*' s-au afisat pentru a evidentia campul in care se face scrierea datelor. Observam ca intr-adevar valoarea implicita a datelor membru x_width si x_precision este zero, deci afisarea se va face cu sase zecimale.
Dupa modificarea acestor date membru la valorile x_width=7 respectiv x_precision=2, afisarea se face in campul de sapte caractere, cu doua zecimale, numarul fiind cadrat la dreapta. Dupa operatia de scriere valoarea datei membru x_width devine zero, dar valoarea datei membru x_precision nu se modifica.
Folosind functia membru width, se atribuie datei membru x_width din nou valoarea sapte. In continuare se foloseste functia membru fill pentru a determina un caracter de umplere diferit de caracterul blanc.
Mentionam ca daca in loc de apelarea functiei printf, s-ar fi folosit ierarhia de clase declarata in fisierul iostream.h, atunci nu s-ar fi obtinut ceea ce s-a dorit. De exemplu, daca in loc de functiile scrie_width_precision_c si afisare_pi_c s‑ar fi folosit functiile:
void scrie_width_precision_stream()
void afisare_pi_stream()
atunci s-ar fi obtinut:
x_width: 0
x_precision: 0
*3.141593*
x_width: 7
x_precision: 2
*3.14*
x_width: 0
x_precision: 2
*3.14*
x_width: 7
x_precision: 2
*3.14*
In acest caz, de fiecare data, valoarea lui π se afiseaza pe un camp de lungime egala cu numarul de caractere afisate. Explicatia este ca datei membru x_width dupa prima operatie de inserare s-a atribuit valoarea zero, prima operatie fiind afisarea sirului de caractere 'x_width: ', si nu scrierea valorii lui π.
Exista o legatura stransa intre functiile membru prezentate in acest paragraf si formatarile din cadrul functiei printf. Acest lucru este ilustrat de urmatorul exemplu. Fisierul stream6.cpp
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
int main()
Prin executie obtinem:
*000987.6543*
*000987.6543*
In exemplul de mai sus s-a afisat valoarea constantei de tip real x in doua moduri: cu ajutorul streamurilor, si folosind functia printf. Putem sa constatam ca s-a obtinut acelasi rezultat. Desi in acest caz scrierea cu functia printf este mai compacta, afisarea cu streamuri este mai generala, deoarece caracterul de umplere poate fi orice alt caracter, nu numai
Bitii datei membru x_flags, care corespund conversiei, pot fi setati si intr‑un alt mod, folosind functii membru speciale, numite manipulatori. Avantajul manipulatorilor este ca ei returneaza o referinta la un stream, deci apelurile acestor functii membru pot fi inlantuite.
O parte a manipulatorilor este declarata in fisierul iostream.h, iar celelalte in fisierul iomanip.h. Manipulatorii declarati in fisierul iostream.h sunt:
endl |
trecere la linie noua si vidarea zonei tampon corespunzatoare streamului |
ends |
inserarea caracterului |
flush |
vidarea zonei tampon a unui obiect al clasei ostream |
dec |
conversie in zecimal |
hex |
conversie in hexazecimal |
oct |
conversie in octal |
ws |
setarea bitului skipws |
Tabelul 5. Manipulatorii declarati in fisierul iostream.h
Manipulatorii declarati in fisierul iomanip.h sunt:
setbase(int b) |
setarea bazei sistemului de numeratie corespunzatoare conversiei, la valoarea bє |
resetiosflags(long x) |
stergerea bitilor specificati in parametrul x, din data membru x_flags |
setiosflags(long x) |
setarea bitilor din data membru x_flags, specificati in parametrul x |
setfill(int f) |
datei membru x_fill i se atribuie valoarea parametrului f |
setprecision(int p) |
datei membru x_precision i se atribuie valoarea parametrului p |
setw(int w) |
datei membru x_width i se atribuie valoarea parametrului w |
Tabelul 6. Manipulatorii declarati in fisierul iomanip.h
Folosind manipulatori exemplul din fisierul stream6.cpp se poate transcrie in urmatorul mod. Fisierul stream7.cpp
#include <iostream.h>
#include <iomanip.h>
#include <stdio.h>
#include <conio.h>
int main()
Rezultatul obtinut este identic cu cel al programului anterior. Manipulatorii s‑au apelat in mod inlantuit, deci programul devine mai simplu. In urmatorul exemplu se va afisa valoarea datei membru x_flags in binar, hexazecimal, octal si zecimal. Fisierul stream8.cpp
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
void binar_c( long x )
printf('n');
}
int main()
Rezultatul obtinut este urmatorul:
x_flags: 00000000 00000000 00100000 00000001
Hexazecimal: 2001
Octal: 20001
Zecimal: 8193
In acest caz nu a fost necesara includerea fisierului iomanip.h, deoarece manipulatorii hex, oct si dec sunt declarate in fisierul iostream.h
Daca se schimba modul de conversie cu un manipulator, ea ramane valabila in continuare, pana la o noua modificare a conversiei.
In paragrafele de mai sus ne-am ocupat de modul de folosire a operatorului de inserare pentru afisarea datelor care apartin tipurilor standard. Ar fi de dorit ca operatorul << sa fie utilizabil si in cazul tipurilor abstracte de date. De exemplu in cazul clasei numerelor rationale
class Fractie
afisarea unei fractii ar trebui sa se efectueze in forma
cout << f;
unde f este un obiect al clasei fractie.
Acest lucru se poate realiza, daca se supraincarca operatorul de inserare pentru afisarea obiectelor de tip Fractie.
Deoarece operandul din stanga al operatorului << este un stream, supraincarcarea operatorului se poate efectua cu o functie prieten a clasei Fractie. Pentru o clasa oareacare cu numele Clasa, operatorul de inserare se poate supraincarca cu urmatoarea functie prieten:
class Clasa ;
Pentru clasa Fractie supraincarcarea operatorului de inserare printr-o functie prieten se poate face in felul urmator. Fisierul fractie2.cpp
#include <iostream.h>
#include <conio.h>
class Fractie
friend ostream& operator<<( ostream& s, Fractie f);
};
ostream& operator<<( ostream& s, Fractie f)
int main()
Daca se executa programul se obtine:
3 / 5
Deci se afiseaza numarul rational, asa cum am dorit. Apelarea operatorului de inserare se poate aplica in mod inlantuit, deoarece functia prieten a clasei Fractie returneaza o referinta la streamul curent.
Desi metoda de mai sus este simpla si da rezultat corect, ea are dezavantajul ca utilizarea unei functii prieten micsoreaza gradul de protectie a datelor. Deci datele membru protejate pot fi modificate in interiorul functiei prieten, care supraincarca operatorul de inserare. In continuare prezentam o modalitate de supraincarcare a operatorului <<, fara a introduce o functie prieten. Pentru o clasa oarecare cu numele Clasa, acest lucru poate fi realizat cu ajutorul unei functii membru afisare, care se va apela de catre functia care supraincarca operatorul de inserare. Deci
class Clasa ;
ostream& Clasa::afisare(ostream& s)
ostream& operator<<(ostream& s, Clasa c1)
In cazul clasei Fractie supraincarcarea operatorului de inserare fara utilizarea unei functii prieten se poate efectua in modul urmator. Fisierul fractie3.cpp
#include <iostream.h>
#include <conio.h>
class Fractie
ostream& afisare( ostream& s );
};
ostream& Fractie::afisare( ostream& s )
ostream& operator<<( ostream& s, Fractie f)
int main()
Rezultatul obtinut prin executarea acestui program este identic cu cel al fisierului fractie2.cpp, dar in acest caz nu s-a folosit functia prieten.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 748
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved