CATEGORII DOCUMENTE |
Operatiile de intrare se realizeaza cu ajutorul operatorului >>, care in acest caz se va numi operator de extragere.
Operandul din stanga al operatorului de extragere trebuie sa fie un obiect al clasei istream, sau a unei clase derivate din clasa istream. Operandul din dreapta va fi o expresie, care poate apartine atat unui tip standard, cat si unui tip abstract. In cazul tipurilor standard se va apela o functie membru a clasei istream de forma:
istream& operator>>( tip_standard& );
In cazul tipurilor abstracte programatorul poate supraincarca operatorul de extragere.
Pentru a prezenta legatura cu functia scanf, revenim la exemplul din sectiunea 15.1, in urmatoarea forma mai detaliata. Fisierul stream9.cpp
#include <iostream.h>
#include <conio.h>
int main()
Prin executarea programului obtinem urmatorul rezultat (s-au evidentiat caracterele citite de la intrare).
x (int) = 123
y (double) = 45.67
z (sir de caractere) = exemplu
Datele citite sunt:
x = 123
y = 45.67
z = exemplu
Acelasi rezultat se obtine si prin executarea programului urmator. Fisierul stream10.cpp
#include <stdio.h>
#include <conio.h>
int main()
Rezulta, ca in cazurile de mai sus, nu exista nici o diferenta intre citirea datelor de la intrarea standard folosind operatorul de extragere, respectiv cu functia scanf. Totusi in anumite cazuri pot sa apara diferente. De exemplu secventa
char c;
cin >> c;
nu este identica cu
char c;
scanf('%c', &c);
Diferenta apare in cazul in care la intrare caracterul curent este un caracter alb, deci spatiu, tab sau caracter newline (trecere la rand nou). Prezentam in continuare un exemplu, din care rezulta, ca intr-adevar cele doua secvente de program nu sunt identice.
Vom defini o clasa c_alb pentru gestionarea caracterelor, si vom supraincarca operatorul de inserare pentru aceasta clasa. Supraincarcarea se va face astfel incat caracterele albe sa fie afisate in mod vizibil (' ' pentru spatii, 't' pentru taburi si 'n' pentru caractere newline). Caracterele diferite de cele albe vor fi afisate nemodificate. Fisierul stream11.cpp
#include <iostream.h>
#include <stdio.h>
#include <conio.h>
class c_alb
ostream& afisare( ostream& s);
};
ostream& c_alb::afisare( ostream& s)
return s;
}
ostream& operator<<(ostream& s, c_alb car)
int main()
Dupa executarea programului, obtinem un rezultat de forma:
Citire caracter (cu operatorul >>). Intrare = q
Caracterul citit (cu operatorul >>) este: q
Citire caracter (cu functia scanf). Intrare = q
Caracterul citit (cu functia scanf) este: 't'
La intrare s-a tastat mai intai un caracter tab, dupa aceea caracterul 'q', urmat de trecerea la un rand nou. Acelasi lucru s-a repetat si in cazul citirii cu scanf. La scriere, caracterul v s-a convertit mai intai intr-un obiect anonim de tip c_alb, pentru a obtine o afisare corespunzatoare a caracterelor albe.
Din cele de mai sus rezulta ca prin citirea cu operatorul de extragere s-a obtinut primul caracter diferit de caracterele albe. Daca s-a apelat functia scanf, s-a citit caracterului curent, indiferent daca el a fost caracter alb sau nu.
Mentionam ca diferenta dintre cele doua modalitati de citire se poate inlatura, daca se anuleaza bitul skipws al datei membru x_flags a clasei ios (valoarea acestui bit in mod implicit este unu).
In cazul functiei scanf, campul din care se face citirea incepe cu primul caracter diferit de caracterele albe si se termina, daca urmatorul caracter este alb, sau caracterul respectiv nu mai corespunde formatului.
In cazul citirii unui sir de caractere, lungimea maxima a campului, din care se face citirea, se determina cu functia membru width a clasei ios. Este valabil tot ce s-a spus referitor la functia membru width in sectiunea 18.2.3, dar in acest caz valoarea datei membru x_width se interpreteaza ca si lungimea maxima a campului, din care se face citirea, in loc de lungimea minima a campului in care se face afisarea. Un avantaj important al functiei membru width, comparativ cu utilizarea functiei scanf, este ca parametrul actual al functiei width poate fi orice expresie, in timp ce aceasta valoare in cazul functiei scanf poate fi numai o constanta. Acest lucru se poate folosi pentru inlaturarea erorilor, care ar putea sa apara din cauza citirii unui numar mai mare de caractere, decat zona de memorie alocata. Consideram urmatorul exemplu pentru ilustrare. Fisierul stream12.cpp
#include <iostream.h>
#include <conio.h>
int main()
Dupa executarea programului obtinem urmatorul rezultat (s‑au evidentiat datele de intrare).
max = 5
Citire cel mult 4 caractere: abcdefghij
Caracterele citite sunt: abcd
Se observa, ca desi la intrare s-au tastat mai multe caractere, nu s-a citit decat numarul de caractere, care se poate memora in spatiul alocat sirului.
In cazul operatiilor de extragere se pot folosi si manipulatorii definiti in paragraful 18.2.4, cu exceptia manipulatorilor endl, ends, flush si setbase. Manipulatorul ws poate fi folosit numai in operatii de extragere. Se pot folosi si celelalte functii membru ale clasei ios, de exemplu functia membru setf.
Daca citirea nu s-a terminat cu succes, atunci streamul ajunge intr-o stare de eroare, despre care putem obtine informatii cu ajutorul datei membru state a clasei ios.
Starea de eroare este caracterizata de bitii datei membru state. Data membru state este de tipul int, iar referirea la bitii ei se poate face cu ajutorul tipului enumerare io_state, definit in clasa ios in modul urmator.
class ios ;
};
Bitii eofbit, failbit, badbit si hardfail se vor numi biti de eroare. Valorile bitilor datei membru state pot fi determinate folosind urmatoarele functii membru ale clasei ios.
int good(); // goodbit este setat, deci nici unul din bitii de eroare
// nu este setat
int eof(); // bitul eofbit este setat
int fail(); // cel putin unul din bitii failbit, badbit sau hardfail este setat
int bad(); // cel putin unul din bitii badbit sau hardfail este setat
Aceste functii membru returneaza valori diferite de zero, daca conditiile scrise sub forma de comentarii sunt indeplinite. Functia membru
int rdstate();
returneaza valoarea datei membru state. Modificarea valorilor bitilor datei membru state se poate efectua cu ajutorul functiei membru clear a clasei ios, functie declarata in urmatorul mod:
void clear(int = 0);
Daca se apeleaza functia membru clear fara parametru, atunci toti bitii de eroare se vor anula, in afara de bitul hardfail, care nu se poate anula. Daca parametrul activ este prezent, atunci data membru state va lua valoarea parametrului. Pentru a seta un anumit bit se va folosi numele bitului precedat de numele clasei ios si operatorul de rezolutie. Daca se foloseste o constructie de forma
cin.clear( ios::badbit | cin.rdstate() );
ceilalti biti vor ramane nemodificati. In acest caz s-a setat numai bitul badbit, iar ceilalti au ramas nemodificati. In urmatorul exemplu este prezentat modul de utilizare al functiei clear. Fisierul stare1.cpp
#include <iostream.h>
#include <conio.h>
void binar( int x )
void nume_bit_1( char* s[], int x )
if ( good_setat )
cout << endl;
}
void afisare( char* s[], int x )
int main() ;
char t[255];
clrscr();
afisare( nume_state, cin.rdstate() );
int x;
cout << 'x = ';
cin >> x;
cout << 'fail() = ' << cin.fail() << endl;
afisare( nume_state, cin.rdstate() );
cout << ' Setarea bitului badbitn';
cin.clear(ios::badbit | cin.rdstate() );
afisare( nume_state, cin.rdstate() );
cout << ' Anularea bitilor de eroaren'
<< ' si vidarea zonei tampon la intraren';
cin.clear(); // anularea bitilor de eroare
cin.getline(t, 255); // vidarea zonei tampon la intrare
afisare( nume_state, cin.rdstate() );
int y;
cout << 'y = ';
cin >> y;
cout << 'fail() = ' << cin.fail() << endl;
afisare( nume_state, cin.rdstate() );
return 0;
}
Daca se executa programul, se obtine urmatorul rezultat (sunt evidentiate caracterele citite).
Data membru state: 00000000 00000000
goodbit
x = a
fail() = 2
Data membru state: 00000000 00000010
failbit
Setarea bitului badbit
Data membru state: 00000000 00000110
failbit badbit
Anularea bitilor de eroare
si vidarea zonei tampon la intrare
Data membru state: 00000000 00000000
goodbit
y = 3140
fail() = 0
Data membru state: 00000000 00000000
goodbit
Functia binar afiseaza valoarea parametrului actual de tip int, in modul in care este memorat, iar functia nume_bit_1 afiseaza numele bitilor cu valoarea unu. Functia afisare apeleaza mai intai functia binar, iar dupa aceea functia nume_bit_1. Deci un apel de forma
afisare( nume_state, cin.rdstate() );
afiseaza mai intai data membru state in forma in care este memorata in calculator, iar dupa aceea numele tuturor bitilor setati ai datei membru state.
Observam ca in cazul citirii variabilei de tip intreg x, la intrare nu s-a aflat un numar intreg (s-a tastat caracterul a). De aceea streamul cin a intrat in stare de eroare, si s-a setat bitul failbit. Functia fail a returnat o valoare diferita de zero (valoarea 2 corespunzatoare bitului failbit). Dupa aceea s-a setat bitul badbit de catre programator folosind functia membru clear. In continuare, anularea bitilor de eroare s-a facut tot cu functia membru clear.
Inainte de o noua citire trebuie vidat zona tampon corespunzatoare intrarii standard. Acest lucru se poate efectua prin citirea unui sir de caractere. Daca citirea s-ar fi facut cu ajutorul operatorului de extragere, atunci sirul de caractere citit s-ar fi terminat la primul caracter alb. De aceea s-a folosit functia membru getline a clasei istream, functie care este declarata in urmatoarele forme:
istream& getline(signed char* z, int n, char c = 'n');
si
istream& getline(unsigned char* z, int n, char c='n');
Functia membru getline citeste din streamul clasei istream un numar de cel mult n-1 caractere. Citirea se termina la intalnirea caracterului c, sau daca s‑au citit toate cele n-1 caractere. Mentionam ca nu se iau in considerare formatarile referitoare la streamul din care se citeste.
Deoarece functia membru getline citeste si caracterele albe, ea poate fi folosita pentru vidarea zonei tampon si in cazul in care la intrare s-au tastat mai multe caractere albe. Observam ca intr-adevar dupa anularea bitilor de eroare si vidarea zonei tampon, se poate citi si valoarea altor date, in acest caz valoarea variabilei y.
Faptul ca un stream se afla in stare de eroare sau nu, poate fi verificat si in urmatoarele doua moduri:
folosind supraincarcarea operatorului '!';
folosind conversia streamului intr-un pointer de tip void*.
Operatorul '!' este supraincarcat cu functia membru
int operator !();
a clasei ios. Valoarea returnata va fi diferita de zero, daca cel putin unul din bitii failbit, badbit sau hardfail este setat, deci daca functia membru fail() returneaza o valoare diferita de zero.
Operatorul '!' se va folosi in urmatorul exemplu. Sa se defineasca o clasa pentru prelucrarea datelor de tip intreg. Pentru clasa respectiva se va defini o functie membru citeste, care va realiza citirea unei date de tip intreg. In cazul unei eventuale erori, citirea se va repeta pana cand se va citi o data corecta, sau se ajunge la sfarsit de fisier. Pentru scrierea datei de tip intreg se va supraincarca operatorul de inserare. Fisierul stare2.cpp
#include <iostream.h>
#include <string.h>
#include <conio.h>
class Intreg ;
Intreg::Intreg( int x_1, char* scrie, char* mesaj)
istream& Intreg::citeste(istream& s)
else return s;
} while ( 1 );
}
ostream& Intreg::afiseaza(ostream& s)
ostream& operator <<(ostream& s, Intreg n)
int main()
return 0;
}
Prin executarea programului obtinem urmatorul rezultat (s-au evidentiat caracterele citite).
i = abcd
Eroare la citire.
i = efg123
Eroare la citire.
i = 45
Valoarea citita este: 45
Clasa Intreg are urmatoarele trei date membru: intregul propriu zis; textul, care se va afisa inainte de citire si mesajul, care va apare in cazul unei erori inainte de a relua citirea.
Observam ca in corpul functiei membru citeste s-a folosit operatorul '!' pentru a testa daca a aparut o eroare la citire sau nu. In cazul in care nu s-a tastat un intreg, streamul intra in stare de eroare, deci bitii de eroare trebuie anulati si trebuie vidata zona tampon. Anularea bitilor de eroare s-a facut cu functia membru clear.
Vidarea zonei tampon s-a realizat cu ajutorul functiei membru get a clasei istream. Ea are mai multe forme, din care amintim urmatoarele:
int get();
istream& get(signed char*, int, char = 'n');
istream& get(unsigned char*, int, char = 'n');
Prima forma a functiei membru get extrage urmatorul caracter din streamul curent. In caz de sfarsit de fisier returneaza EOF, deci valoarea -1. A doua, respectiv a treia forma a functiei membru get este asemanatoare cu cele doua forme a functiei membru getline. Diferenta este ca in cazul functiei get caracterul terminal, determinat prin parametrul al treilea, nu se extrage din streamul curent, iar in cazul functiei getline se extrage si caracterul terminal.
Vidarea zonei tampon s-ar fi putut efectua ca si in cazul exemplului din fisierul stare1.cpp, folosind functia membru getline in modul urmator:
s.getline(t, 255);
Daca inlocuim secventa
s.get(t, 255); // vidarea zonei tampon la intrare
if ( s.get() == EOF ) //citeste 'n' sau EOF
return s;
din fisierul stare1.cpp, cu apelarea functiei getline, obtinem un rezultat asemanator, dar pot sa apara si diferente, de exemplu in cazul urmator.
Rezultatul obtinut prin executarea programului stare2.cpp, varianta cu functia membru getline este
i = abc^Z
Eroare la citire.
i =
si varianta cu functia membru get este
i = abc^Z
Desi ambele rezultate pot fi considerate corecte, credem ca in acest caz nu mai este necesara afisarea mesajului de eroare si a textului pentru reluarea citirii. De aceea varianta cu functia membru get este cea preferata.
O alta modalitate de a verifica faptul ca streamul este in stare de eroare sau nu, este conversia spre tipul void*. Rezultatul conversiei este pointerul NUL, deci valoarea zero, daca cel putin unul din bitii failbit, badbit sau hardfail este setat, adica functia membru fail() returneaza o valoare diferita de zero. In caz contrar se obtine un pointer diferit de zero. De obicei aceasta conversie se va efectua in cazul constructiilor de forma urmatoare:
if ( stream >> data )
Rezultatul expresiei stream >> data este o referinta la obiectul stream, care apartine clasei istream. In cazul de mai sus rezultatul acestei expresii se va converti in mod automat intr-un pointer de tip void*. Astfel se poate testa, daca un bit de eroare, diferit de bitul eofbit, este setat sau nu.
De exemplu functia membru citeste a clasei Intreg se poate scrie in urmatoarea forma:
istream& Intreg::citeste(istream& s)
while ( 1 );
si rezultatul obtinut este identic cu cel al programului din fisierul stare2.cpp
Ca si in cazul operatorului de inserare, operatorul de extragere se poate supraincarca pentru tipuri abstracte definite de programator. Pentru o clasa oareacare cu numele Clasa, operatorul de extragere se poate supraincarca cu urmatoarea functie prieten:
class Clasa ;
In vederea maririi gradului de protectie a datelor se va evita utilizarea functiilor prieten. De exemplu, se poate folosi o functie membru citire, care se va apela de catre functia, care supraincarca operatorul de extragere, in modul urmator:
class Clasa ;
istream& Clasa::citire(istream& s)
istream& operator>>(istream& s, Clasa& c1)
De exemplu, in cazul fisierului stare2.cpp operatorul de extragere se va supraincarca in modul urmator:
istream& operator >>(istream& s, Intreg& n)
Daca se modifica fisierului stare2.cpp, astfel incat sa fie supraincarcat operatorul de extragere, atunci expresia
i.citeste( cin );
din functia main poate fi scrisa in forma:
cin >> i;
Observam ca functia, care supraincarca operatorul de extragere, nu are acces la datele protejate ale clasei Intreg. Deci s-a realizat o protectie mai buna a datelor, decat prin folosirea unei functii prieten.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 838
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved