Sunteți pe pagina 1din 10

Curs 1

Bibliografie:
Walter Sowitch - Problem Solving with C++
Addison Wedey Publishing Company, 1996
Referinte:
http://www.cs.tuiasi.ro/library

Introducere

Un prim exemplu de program C++:

#include <iostream.h>

void main
{
int a;

cout << "Dati un numar\n";


cin >> a;
cout << "Ati tastat numarul " << a << endl;
}

Obs.: << 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:

#include <iostream.h>

void main()
{
double b;

cout << "Dati un numar real:" << endl;


cin >> b;
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
cout << b;
}

Obs: 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.

Supraincarcarea functiilor ( caz particular de polimorfism )

Ex:
#include <iostream.h>

double ave(double n1, double n2)


{return ((n1+n2)/2); }

double ave(double n1, double n2, double n3)


{return ((n1+n2+n3)/3.0);}
int main()
{
cout << "Media dintre 6.0, 7.5 si 8.75 este "
<< ave(6.0,7.5,8.75) << endl;
cout << "Media dintre 8.5 si 10 este "
<< ave(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.

Ex. 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(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 )

Ex: (interschimbarea a doi intregi)

#include <iostream.h>

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).

Ex:
ifstream in_stream;
ofstream out_stream;

Aceste doua clase sunt definite in fisierul header <fstream.h>, fisier ce trebuie inclus cu #include <fstream.h>.
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, deschiderea in acces de scriere:

ofstream out_stream;
out_stream.open("outfile.dat");

Putem scrie acum in fisierul "outfile.dat":

out_stream << "a=" << a << "b=" << b;


Fiecare fisier de intrare sau iesire folosit de programul dvs. 2 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.

Ex:
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:
obiect_apelat.nume_functie_membru(lista_argumente)

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.

Ex:
in_stream.open("fisier.dat");
if ( in_stream.fail() )
{
cout << "Fisier de intrare deschis gresit" << endl;
exit(1);
}

Obs:
Functia exit este caracteristica limbajului C si este definita in
fiserul antet <stdlib.h>

#include <fstream.h>
#include <iostream.h>
#include <stdlib.h>

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:

out_stream.setf(ios::fixed);
out_stream.setf(ios::showpoint);
out_stream.precision(2);

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 nu sunt afisate in notatie exponentiala nesetat
ios::scientific Numerele in punct flotant sunt afisate in notatie exponentiala nesetat
ios::showpoint Punctul zecimal si zecimalele zero sunt mereu afisate pentru numere in nesetat
punct flotant
ios::showpos Semnul + este afisat in fata valorilor pozitive nesetat
ios::right daca este setat si valoarea latimii campului este data cu un apel al functiei setat
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 este data cu un apel al functiei nesetat
membru width, atunci urmatorul articol va fi la capatul din stanga al
spatiului specificat cu witdh

Resetarea acestor valori se face cu unsetf (flags)

Ex: 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 ).

Ex:
cout << "Afisam intregii " << setw(4) << 10
<< setw(5) << 20 << setw(6) << 30;

Iesirea va fi: Afisam intregii 10 20 30

Manipulatorul precision este echivalent cu functia membru precision.

Ex:
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

Obs:
Pentru lucrul cu acesti operatori(setw, setprecision) trebuie sa includeti
directiva:
#include <iomanip.h>

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.

Ex:

// 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.
#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <iomanip.h>

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. Folsind functia membru get, nimic nu se face automat (trecerea peste spatii trebuie
facuta de catre programator).

Ex_1:
char a;
cin.get(a);

Obs_1: argumentul a este de tip output, adica isi modifica valoarea

Ex_2:
char c1,c2,c3;

cin.get(c1);
cin.get(c2);
cin.get(c3);

La intrare avem: AB
CD, atunci se va unifica astfel: C1='A',C2='B',C3='\n'

Ex_3: detectam folosind get sfarsitul liniei.

cout << "Dati o linie pentru a o dubla\n";


char c;
do
{
cin.get(c);
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

Alt exemplu:
'a' - un caracter, "a" = 'a' '\0' - un sir de 2 caractere

Sintaxa generala a functiei membru get este:

Flux_de_intrare.get(variabila_de_tip_char)

Obs:
Flux_de_intrare este obiect al clasei ifstream.
in cazul lui cin, trebuie sa adaugam fisierul antet <iostream.h>
daca este alt fisier (diferit de cel standard) atunci adaugam <fstream.h>

Functia membru put este valabila pentru fluxuri de iesire. Ea are un argument care este o expresie de tip char. Cand
functia este apelata,
valoarea argumentului este trimisa catre fluxul de iesire.

Sintaxa: Flux_de_iesire.put(Expresie_caracter);

Ex: cout.put(simbol_urmator);
cout.put('a');

Functia membru putback

Uneori dorim sa stim urmatorul caracter dintr-un flux de intrare. Insa folosind get, pointerul catre fisier indica deja
urmatorul caracter. Dorim acum sa punem inapoi caracterul in fluxul de intrare.
Functia putback este un membru pentru orice flux de intrare. Acesta are un argument de tip char si plaseaza valoarea
argumentului inapoi in fluxul de intrare.

Ex: scriem un cod C care citeste caracter cu caracter pana la primul spatiu care este introdus inapoi in fluxul de intrare:

fin.get(a);
while (a != ' ')
{
fout.put(a);
fin.get(a);
}
fin.putback(a);

Obs: fin este un obiect al clasei ifstream iar fout este un obiect al clasei ofstream.

Functia membru eof

Fiecare flux al unui fisier de intrare are o functie membru eof (end-of-file), folosita pentru determinarea situatiei cand
intreg fisierul a fost citit si nu a mai rama nici o intrare pentru program.
Functia eof nu are argumente, deci daca fluxul de intrare se cheama fin, atunci un apel al functiei eof se scrie:
fin.eof();

Expresia fin.eof() returneaza true daca programul a fost citit (adica s-a trecut de sfarsitul fisierului de intrare), altfel
expresia de mai sus intoarce false.

Ex_1:
if ( ! fin.eof() )
cout << "Nu este sfarsitul fisierului.\n";
else
cout << "Sfarsitul fisierului.\n";

Ex_2:
//presupunem ca fluxul de intrare in_stream este conectat la un fisier
//de intrare. Iata cum putem lista la ecran continutul fisierului.

in_stream.get(next);
while ( ! in_stream.eof() )
{
cout << next;
in_stream.get(next);
}

Functii ( macrouri ) din <ctype.h>: toupper, tolower, isupper, islower, isalpha, isdigit, isspace ( de repetat din C ).

Mostenire
Una din trasaturile cele mai importante ale C++ o reprezinta folosirea claselor derivate. O clasa este derivata din alta
clasa daca clasa derivata se obtine din prima adaugand cateva trasaturi (proprietati).

Ex_1:
Clasa fluxurilor fisierelor de intrare (ifstream) este derivata din clasa tuturor fluxurilor de intrare (istream) prin
adaugarea de functii membru suplimentare cum ar fi open si close. Fluxul cin apartine clasei tuturor fluxurilor de intrare,
dar nu apartine clasei fluxurilor fisierelor de intrare pentru ca cin nu are functiile membru open si close.

Mostenire in clasele stream

Un obiect este o variabila care are functii membru, iar o clasa, este un tip a carui variabile sunt obiecte. Fluxurile ( cum ar
fi cin, cout, fluxuri fisiere de intrare, fluxuri fisiere de iesire) sunt obiecte, deci tipurile fluxurilor (ifstream,ofstream) sunt
clase.
Cin si fluxurile fisiere de intrare sunt fluxuri de intrare. Putem folosi operatorul de extractie >> pe ambele tipuri de
fluxuri. Un flux fisier de intrare poate fi conectat la un fisier folosind functia open, dar nu si cin. Un flux fisier de intrare
este de tip ifstream, iar cin este de tip istream.
Clasele ifstream si istream sunt diferite. Legatura dintre ele este ca ifstream este o clasa derivata a lui istream.

Ex:
Consideram o functie care citeste doi intregi din fluxul de intrare fisier_sursa si afiseaza suma lor la ecran:

void suma(ifstream& fisier_sursa)


{
int n1,n2;
fisier_sursa >> n1 >> n2;
cout << n1 << "+" << n2 << "=" << (n1+n2) << endl;
}

I. Presupunem ca avem urmatoarea declaratie:

ifstream fin; // conectat la un fisier cu open

putem face acum apelul:


suma(fin);

II. Presupunem ca dorim citirea de la tastatura. Un apel de genul


suma(cin);

nu va functiona deoarece cin nu este de tip ifstream. Deoarece cin este de tip istream, mai scriem inca
o functie numita suma1, in care argumentul este istream& fisier_sursa. Acum apelul suma1(cin); va functiona.
De asemeni, va functiona si apelul suma1(fin). Justificarea este ca ifstream este o clasa derivata a clasei
istream.
Orice flux de tip ifstream este de asemeni de tip istream, deci un parametru formal de tip istream argumentul
folosit in apelul functiei poate fi un flux de intrare conectat la un fisier sau la fluxul cin.

Clasele derivate sunt des folosite pentru mostenire si relatiile dintre functii. Daca clasa B este clasa derivata a clasei A,
atunci clasa B se numeste fiul (copilul) clasei A si clasa A se numeste parintele clasei B. Clasa derivata mosteneste
functiile membru ale clasei parinte.

Ex: am vazut ca orice flux de intrare mosteneste operatorul de extractie << din clasa tuturor functiilor.

Exercitii propuse spre implementare

1. Scrieti un program C++ ce foloseste stream-uri astfel incat sa inlocuiti fiecare caracter dintr-un fisier de intrare cu un sir
de caractere in fisierul fisierul de iesire.
2. Scrieti un program C++ care citeste dintr-un fisier inregistrari de forma (cate o inregistrare pe linie):

string double

nume prenume nota1 nota2 nota3

si creeaza un alt fisier care contine informatiile din primul fisier + media celor 3 note pe respectiva linie ( la sfarsit ).
3. Scrieti un program C++ care citeste numere sortate crescator din doua fisiere si scrie in al treilea fisier reuniunea lor
sortata crescator.
ATENTIE ! Se utilizeaza spatiu suplimentar de memorie constant.
4. Scrieti un program care citeste un fisier C++ inlocuind versiunile incorecte cin << si cout >> cu versiunile corecte cin
>> si cout << .
ATENTIE! Intre cin si << pot fi oricate spatii ( inclusiv 0 ).

S-ar putea să vă placă și