----------- Introducere ----------- Un prim exemplu de program C++:
#include
void main { int a;
cout << "Dati un numarn"; cin >> a; cout << "Ati tastat numarul " << a << endl; } ----------- Observatii: << este operatorul de insertie ----------- >> este operatorul de extractie "n" este echivalent cu endl cout este "console out" cin este "console in"
Afisarea numerelor double (cu format) se face astfel:
cout << b; } ----------- Observatie. cout.precision(2) face ca numarul sa fie afisat cu 2 zecimale ----------- Daca apoi, se doreste afisarea a trei zecimale, se face doar setarea cout.precision(3); In general, cout afiseaza un format asemanator cu cel de intrare.
int main() { cout << "Media dintre 6.0, 7.5 si 8.75 este " << media(6.0,7.5,8.75) << endl; cout << "Media dintre 8.5 si 10 este " << media(8.5,10.0); return 0; }
Deci avem 2 functii ave cu numar diferit de parametri. In acest caz compilatorul o va alege pe cea ce are numarul corect de parametri.
------------------------------------ Supraincarcarea numelui unei functii ------------------------------------ Inseamna definirea a doua sau mai multe functii cu acelasi nume. Cand supraincarcarea numelui unei functii trebuie sa aiba numar diferit de parametri formali sau anumiti parametri formali de tipuri diferite. Cand avem un apel de functie compilatorul foloseste definitia functiei a carei numar si tipuri de parametri formali se potriveste cu argumentele din apelul functiei.
Folosirea aceluiasi nume pentru a desemna lucruri diferite se numeste polimorfism (termen derivat din cuvinte grecesti ce inseamna "multe forme"). Supraincarcarea este un exemplu de polimorfism.
--------------------------------- Exemplu (special de ambiguitate): --------------------------------- void f(int a) { cout << a; } void f(float a) { cout << a; }
Avem 2 cazuri atunci cand apelam una din functii folosind constante: I. daca apelam functia f cu o constanta de tip intreg atunci compilatorul va folosi f(int a) ceea ce este corect; II. daca apelam functia f cu o constanta de tip float atunci compilatorul va semanala eroarea "Ambiguity between f(int) and f(float)"
In aceste conditii daca in functia main (sa zicem) apelam: f(1.2) - "Ambiguity between f(int) and f(float)" f(1) - se executa f(int a) f(a) - unde int a=2; -> se executa f(int a) f(x) - unde float x=2.0; -> se executa f(float a)
------------------------------ Apeluri ale functiilor din C++ ------------------------------ Se stie ca in C exista 2 tipuri de apeluri ale functiilor: - prin valoare (se transmite valoarea) - prin referinta (se transmite adresa) ---------------------------------------- Exemplu (interschimbarea a doi intregi): ---------------------------------------- #include
void schimba(int& a, int& b) { // Preconditie: parametri formali se numesc "parametri adresa". // Postconditie: valorile celor doi intregi de la aceste adrese sunt // intershimbate. // Mecanismul acesta de apel prin referinta (adresa) este nou si specific // limbajului C++. "Legaturile de adresa" sunt facute de // catre compilator si sunt transparente programatorului. Ideea este ca se // aloca spatiu de memorie pentru parametri adresa // "a" si "b" cu specificarea ca inainte de parasirea functiei, valorile // de la adresele parametrilor actuali (de exemplu "i" si "j") sunt // actualizate cu valorile de la adresele lui "a", respectiv "b". Pe durata // executiei functiei, parametrii actuali sunt inaccesibili // in cazul nostru, variabilele "i" si "j". Se mai spune ca variabilele // actuale sunt "trimise cu totul" (in sensul ca le stiu adresa lor).
int aux; aux = a; a = b; b = aux; }
void schimba(int *adresa1, int *adresa2) { // Preconditie: primesc ca parametri adresele celor doua variabile // Postconditie: valorile celor doi intregi de la aceste adrese sunt // intershimbate. // Mecanismul acesta de apel prin referinta (adresa) este vechi si // specific limbajului C. "Legaturile de adresa" trebuie facute de // catre programator. int aux; aux = *adresa1; *adresa1 = *adresa2; *adresa2 = aux; }
void main() { int i = 2, j = 3; cout << "i = " << i << "; j = " << j << endl; schimba(i, j); cout << "i = " << i << "; j = " << j << endl; schimba(&i, &j); cout << "i = " << i << "; j = " << j << endl; }
------------------------------------------------- Fluxuri de I/O (intrare/iesire) - Fisiere de I/O ------------------------------------------------- Un stream (curent,curs) este un flux de caractere (sau alte tipuri de date). Daca fluxul este in program, atunci se cheama flux de intrare altfel este flux de iesire. In C++, un flux este un tip special de variabile cunoscut ca obiect. Tipul pentru variabile de flux-fisiere de intrare este numit ifstream (input file stream), iar pentru iesire ofstream (output file stream).
Aceste doua clase sunt definite in fisierul header , fisier ce trebuie inclus cu #include . Variabilele de flux trebuie declarate, adica conectate la un fisier. Asta inseamna deschiderea fisierului.
in_stream.open("fis.dat");
Putem extrage acum din fisierul "infile.dat" (citim doi intregi):
int a,b; in_stream >> a >> b;
La fel, putem face deschiderea in acces de scriere:
Fiecare fisier de intrare sau iesire folosit de programul dumneavoastra doua nume. Numele fisierului extern este numele real al fisierului, dar acesta este folosit doar in apelul de deschidere a fisierului, care conecteaza fisierul catre un flux. Inchiderea unui fisier se face astfel:
in_stream.close(); out_stream.close();
unde in_stream si out_stream sunt obiecte din clasele ifstream si ofstream, "." este operatorul punct, iar close() este o functie membru din aceste clase. -------- Exemplu: -------- Clasa ifstream are o functie membru numita open si clasa ofstream are de asemeni o functie membru numita open. Clasa ofstream are de asemeni o functie membru numita precision. (dar clasa ifstream nu are nici o functie membru numita precision).
In general, un obiect este o variabila care are functii asociate. Aceste functii se numesc functii membru. O clasa este un tip a carui variabile sunt obiecte. Tipul obiectului determina ce functii membru are obiectul. Apelarea unei functii membru pentru un obiect se face folosind operatorul "." Sintaxa generala este:
Un alt caz de polimorfism este folosirea aceluiasi nume pentru functii membru din clase diferite, cum este cazul lui open. Exista o functie membru fail care testeaza daca o operatie de flux a esuat sau nu. Aceasta functie membru este valabila pentru ifstream si ofstream. -------- Exemplu: -------- in_stream.open("fisier.dat"); if (in_stream.fail()) { cout << "Fisier de intrare deschis gresit" << endl; exit(1); } ----------- Observatie: Functia exit este caracteristica limbajului C si este definita in ----------- fisierul antet .
int main() { // Acest program citeste doua nume de fisiere unul de intrare, unul de iesire. // Apoi citim din fisierul de intrare trei numere intregi, // apoi in fisierul de iesire scriem suma lor. char fis_in[16], fis_out[16]; ifstream in_stream; ofstream out_stream; cout << "Dati numele fisierului de intrare.n"; cin >> fis_in; in_stream.open(fis_in); if (in_stream.fail()) { cout << "Eroare la deschidere fisier de intrare.n"; exit(1); } cout << "Dati numele fisierului de iesire.n"; cin >> fis_out; out_stream.open(fis_out); if (out_stream.fail()) { cout << "Eroare la deschidere fisier de iesire.n"; exit(1); } int a, b, c; in_stream >> a >> b >> c; out_stream << "Suma celor 3 numere este " << a + b + c << endl; in_stream.close(); out_stream.close(); return 0; }
Am vazut ca formatarea afisarii unui double se face cu:
Functia membru setf (set flag) seteaza un "stegulet". Exista 6 tipuri de flag-uri ce pot fi setate (sau nesetate):
------------------------------------------------------------------------------- | Flag | Inteles (semantica intuitiva) | Implicit | |---------------------------------------------------------------|------------| | ios::fixed | numerele in punct flotant | nesetat | | | nu se afiseaza in format exponential | | | ios::scientific | numerele un punct flotant | nesetat | | | sunt afisate in notatia exponentiala | | | ios::showpoint | punctul zecimal si zecimalele zero sunt | nesetat | | | mereu afisate pentru numere in punct flotant| | | ios::showpos | semnul + este afisat in fata | nesetat | | | valorilor pozitive | | | ios::right | daca este setat si valoarea latimii campului| setat | | | este data cu un apel al functiei membru | | | | width, atunci urmatorul articol va fi la | | | | capatul din dreapta al spatiului specificat | | | | cu witdh | | | ios::left | daca este setat si valoarea latimii campului| nesetat | | | este data cu un apel al functiei membru | | | | width, atunci urmatorul articol va fi la | | | | capatul din stanga al spatiului specificat | | | | cu witdh | | |----------------------------------------------------------------------------|
Resetarea acestor valori se face cu unsetf (flags) -------- Exemplu: cout.unsetf(ios::showpos); -------- Un manipulator este o functie membru de flux apelata in mod netraditional. Acestia sunt plantati dupa operatorul de insertie <<. Deocamdata prezentam doar 2 manipulatori: setwidth si setprecision (manipulatorul setwidth este echivalent cu functia membru witdh).
Manipulatorul precision este echivalent cu functia membru precision. -------- Exemplu: -------- cout.setf(ios::fixed); cout.setf(ios:showpoint); cout << "$" << setprecision(2) << 10.3 << endl << setprecision(3) << "$" << 20.5 << endl;
Iesirea va fi: $10.30 $20.500 ----------- Observatie: ----------- Pentru lucrul cu acesti operatori(setw, setprecision) trebuie sa includeti directiva: #include
----------------------------------- Fluxuri ca argumente pentru functii ----------------------------------- Un flux (stream) poate fi un argument pentru o functie. Singura restrictie este aceea ca parametrul formal al functiei trebuie sa fie referinta. -------- Exemplu: -------- // Programul urmator ilustreaza instructiuni de formatare a iesirii citeste // toate numerele din fisierul "fis_in.dat" si le scrie la // ecran si in fisierul "fis_out.dat" intr-o forma frumoasa.
void afisare_frumoasa(ifstream& fisier_urat, ofstream& fisier_frumos, int precizie, int latime_camp); // Preconditie: Fluxurile fisier_urat si fisier_frumos sunt conectate la // fisiere folosind functia open. // Postconditie: Numerele din fisierul fisier_urat vor fi afisate la ecran si // in fisier_frumos. Numerele sunt cate unul pe linie, in // notatie punct fix (si nu exponentiala) cu precizie cifre dupa punctul // zecimal; fiecare numar este precedat de + sau -, in // total avand la dispozitie latime_camp caractere. int main() { ifstream fin; ofstream fout; fin.open("fis_in.dat"); if (fin.fail()) { cout << "Deschiderea fisierului de intrare a esuat.n"; exit(1); } fout.open("fis_out.dat"); if (fout.fail()) { cout << "Deschiderea fisierului de iesire a esuat.n"; exit(1); } afisare_frumoasa(fin, fout, 5, 12); fin.close(); fout.close(); cout << "Sfarsitul programului.n"; return 0; }
// folosim acum bibliotecile iostream.h, fstream.h si iomanip.h void afisare_frumoasa(ifstream& fisier_urat, ofstream& fisier_frumos, int precizie, int latime_camp) { fisier_frumos.setf(ios::fixed); // nu notatie exponentiala fisier_frumos.setf(ios::showpoint); // afiseaza punctul zecimal fisier_frumos.setf(ios::showpos); // afiseaza semnul (+,-) fisier_frumos.precision(precizie); // acum facem setarile si pentru afisarea pe ecran cout.setf(ios::fixed); cout.setf(ios::showpoint); cout.setf(ios::showpos); cout.precision(precizie); double urmator; while (fisier_urat >> urmator) { cout << setw(latime_camp) << urmator << endl; fisier_frumos << setw(latime_camp) << urmator << endl; } }
--------------------------- Functiile membru get si put --------------------------- Functia get permite citirea unui caracter de la intrare si memorarea lui intr-o variabila de tip char. get este o functie membru pentru fluxul cin si se poate extinde la orice fisier de intrare. Am vazut ca cin cu operatorul de extractie >> citeste un caracter (sau orice alta intrare). Unele lucruri sunt facute automat, cum ar fi trecerea peste spatii. Folosind functia membru get, nimic nu se face automat (trecerea peste spatii trebuie facuta de catre programator). -------- Exemplu: -------- char a; cin.get(a); ----------- Observatie: argumentul a este de tip output, adica isi modifica valoarea ----------- -------- Exemplu: -------- char c1,c2,c3; cin.get(c1); cin.get(c2); cin.get(c3);
Presupunem ca la intrare avem: AB CD atunci se va unifica astfel: C1='A',C2='B',C3='n' -------- Exemplu: detectam folosind get sfarsitul liniei. -------- cout << "Dati o linie pentru a o dublan"; char c; do { cin.getÅ ; cout << c << c; } while (c != 'n');
----------------------------------------- Deosebiri si asemanari intre 'n' si "n" ----------------------------------------- Asemanari: intr-o instructiune cout, ele produc acelasi efect. Deosebiri: 'n' - valoare de tip char (se poate fi memorat intr-o variabila de tip char) "n" - este un sir format dintr-un singur caracter dar nu poate fi memorat intr-o variabila de tip char -------- Exemplu: __________ -------- 'a' - un caracter, "a"=|'a'|'