Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml


Bazele sistemului I/O in C++

c



+ Font mai mare | - Font mai mic



Bazele sistemului I/O in C++

Pe langa faptul ca permite sistemul de I/O din C, C++ defineste propriul sau sistem I/O orientat pe obiecte. Ca si sistemul de I/O din C, cel din C++ este complet integrat. Aceasta inseamna ca diferitele tipuri de operatii I/O sunt doar perspective diferite ale aceluiasi mecanism. Aceasta perspectiva integratoare asupra operatiilor I/O are la baza, atat in C cat si in C++, conceptul de flux (stream).



1 Fluxuri in C si C++

Sistemul de fisiere din C si C++ este proiectat sa lucreze cu o mare varietate de echipamente, care include terminale, drivere de disc, drivere de unitate de banda, etc.

AChiar daca echipamentele difera, sistemul de fisiere din C si C++ le transforma intr-un instrument logic numit flux (stream)

Toate fluxurile se comporta la fel. Deoarece fluxurile sunt independente de echipamente, o functie care poate sa scrie intr-un fisier de pe hard poate fi folosita cu aceeasi sintaxa pentru a scrie la alt dispozitiv. Sistemul de fisiere C/C++ recunoaste doua tipuri de fluxuri:text si binar.

Fluxuri de tip text

Un flux de tip text este o secventa de caractere. Standardul ANSI C permite (dar nu impune) ca un flux de tip text sa fie organizat in linii terminate cu un caracter de linie noua. Totusi, caracterul de linie noua din ultima linie este optional, utilizarea sa fiind determinata de modul de implementare a compilatorului (Majoritatea compilatoarelor de C/C++ nu incheie fluxul de tip text cu un caracter de linie noua. Sa mai semnalam faptul ca, intr-un flux de tip text pot sa apara anumite transformari cerute de mediul de operare gazda (De exemplu, un caracter de linie noua poate fi inlocuit cu perechea inceput de rand-linie noua. Acesta este motivul pentru care nu exista o relatie biunivoca intre caracterele care sunt scrise sau citite si cele de la echipamentul extern.

Fluxuri binare

Un flux binar este o secventa de octeti intr-o corespondenta biunivoca cu cei de la echipamentul extern.

Fisiere

In C/C++ un fisier poate sa fie: un fisier de pe disc, tastatura, ecranul monitorului, imprimanta,etc. Un flux se asociaza cu un anumit fisier efectuand o operatie de deschidere. Odata deschis fisierul, este posibil schimbul de date intre el si programul utilizator care l-a deschis.

De observat faptul, trivial pentru cunoscatori, ca nu toate fisierele au aceleasi posibilitati. De exemplu, un fisier de pe disc poate sa admita un acces aleator la datele stocate in el, in timp ce imprimanta nu o poate face. Astfel ca, putem concluziona, pentru claritate:

A Pentru sistemul I/O din C/C++ toate fluxurile sunt la fel dar nu si fisierele.

Daca fisierul admite cereri de pozitionare, deschiderea fisierului initializeaza pointerul de fisier la o valoare care indica inceputul fisierului. Pe masura ce se fac operatii de citire/scriere, pointerul de fisier este incrementat corespunzator naturii operatiei.

Un fisier se disociaza de un flux in urma operatiei de inchidere. Daca este inchis un fisier deschis in operatii de scriere, continutul fluxului asociat este scris la dispozitivul extern (acest proces se numeste flushing=golire a fluxului ).

Toate fisierele se inchid automat cand programul se termina normal. In caz de blocaj sau daca programul se termina ca urmare a apelului functiei abort() fisierele nu se inchid.

Cu mentiunea ca in fisierul antet stdio.h se gasesc structurile de control de tip FILE, indispensabile pentru lucrul cu fisiere in C, prezentam, in continuare contextul C++ referitor la sistemul I/O.

2 Clasele de baza pentru fluxuri in C++

C++ asigura suportul pentru sistemul sau de I/O in fisierul antet IOSTREAM.H. In acest fisier antet sunt definite doua ierarhii de clase care admit operatii de I/O. Clasa cu nivelul de abstractizare cel mai inalt se numeste streambuf si asigura operatiile de baza de intrare/iesire. Ca programatori, nu folositi streambuf direct decat daca veti deriva propriile clase pentru efectuarea operatiilor I/O. A doua ierarhie porneste cu clasa ios, care accepta operatii I/O formatate. Din ea sunt derivate clasele istream, ostream si iostream. Aceste clase sunt folosite pentru a crea fluxuri capabile sa citeasca, sa scrie, respectiv sa citeasca/ sa scrie date din/ la echipamentele externe. Clasa ios contine o serie de alte ramuri relativ la lucrul cu fisiere pe care nu ne propunem sa le studiem in cadrul acestui curs.

Fluxuri predefinite in C++

Cand isi incepe executia un program C++, se deschid automat patru fluxuri predefinite, pe care le prezentam in tabelul de mai jos.

Flux

Semnificatie

Echipament implicit

cin

Intrare standard

Tastatura

cout

Iesire standard

Ecran

cerr

Iesire standard pentru eroare

Ecran

clog

Versiune cu memorie tampon pentru cerr

Ecran

Tabelul 15. Fluxurile predefinite C++

Fluxurile cin, cout, cerr corespund fluxurilor stdin, stdout, stderr din C. Implicit, fluxurile standard sunt folosite pentru a comunica cu consola. Insa, in mediile care admit redirectionarea I/O, fluxurile standard pot fi redirectionate spre alte echipamente sau fisiere.

I/O formatate in C++

Sistemul de I/O din C++ va permite sa formatati operatiile I/O, asa cum se intampla si in cazul utilizarii functiilor C pentru operatii I/O, precum: printf, cprintf, scanf,etc. De exemplu, se poate specifica marimea unui camp, baza unui numar, numarul de cifre dupa punctul zecimal,etc. Operatorii din C++ utilizati pentru introducerea informatiilor de formatare sunt >> si <<.

Exista doua cai inrudite, dar conceptual diferite, prin care se pot formata datele. In primul rand, putem avea acces direct la diferiti membri ai clasei ios. In al doilea rand, putem folosi functii speciale numite manipulatori, care pot fi incluse in expresii de I/O.

Prezentam, in continuare modul de utilizare a manipulatorilor de formate, datorita accesibilitatii mai mari a acestora.

Manipulatorii standard sunt prezentati in tabelul de mai jos.

Manipulator

Exemplu de folosire

Efect

dec

cout<<dec<<intvar;

Converteste intregi in cifre zecimale; corespunde formatului %d din C

endl

cout<<endl

Trimite o noua linie in ostream si descarca bufferul

ends

cout<<ends

Insereaza un caracter nul intr-un flux

flush

cout<<flush

Descarca bufferul fluxului ostream

hex

cout<<hex<<intvar;

cin>>hex>>intvar

Conversie hexazecimala corespunzatoare formatului %x din ANSI C

oct

cout<<oct<<intvar;

cin>>oct>>intvar;

Conversie octala (formatul %o din C)

resetiosflags(long f)

cout<<resetioflags(ios::dec);

Reinitializeaza bitii de formatare specificati de argumentul intreg de tip long

setbase(int baza)

cout<<setbase(10);

cin>>setbase(8);

Stabileste baza de conversie la argumentul intreg (trebuie sa fie 0,8,10 sau 16). Valoarea 0 este baza implicita.

setfill(int ch)

cout<<setfill('.');

cin>>setfill(' ');

Stabileste caracterul folosit pentru completarea campurilor de marime specificata

setiosflags(long f)

cout<<setiosflags(ios::dec);

cin>> setiosflags(ios::hex);

Stabileste bitii de formatare specificati de argumentul intreg de tip long

setprecision(int p)

cout<<setprecision(6);

cin>>setprecision(10);

Stabileste precizia conversiei in virgula mobila la numarul specificat de zecimale

setw(int w)

cout<<setw(6)<<var;

cin>>setw(24)>>buf

Stabileste marimea unui camp la numarul specificat de caractere

ws

cin ws;

Elimina spatiile libere din fluxul de intrare

Tabelul 16. Manipulatori de formatare a operatiilor I/O in C++

Toti acesti manipulatori au prototipul in fisierul antet iomanip.h.

Pentru a utiliza manipulatorii setiosflags si resetiosflags trebuie cunoscuti indicatorii de formatare din tabelul de mai jos.

Nume indicator

Ce efect are utilizarea indicatorului

ios :: skipws

Elimina spatiile goale din intrare

ios :: left

Aliniaza iesirea la stanga in interiorul latimii campului

ios :: right

Aliniaza iesirea la dreapta

ios :: scientific

Foloseste notatia stiintifica pentru numerele in virgula mobila.

ios :: fixed

Foloseste notatia zecimala pentru numere in virgula mobila

ios :: dec

Foloseste notatia zecimala pentru intregi

ios :: hex

Foloseste notatia hexazecimala pentru intregi

ios :: oct

Foloseste notatia octala pentru intregi

ios :: uppercase

Foloseste litere mari pentru iesire

ios :: showbase

Indica baza sistemului de numeratie in cadrul iesirii (prefixul 0x pentru hexazecimal si prefixul 0 pentru octal

ios :: showpoint

Include un punct zecimal pentru iesiri in virgula mobila

ios :: showpos

Include si semnul + la afisarea valorilor pozitive

ios :: unitbuf

Goleste toate fluxurile dupa inserarea caracterlor intr-un flux

Tabelul 16. Indicatori de formatare a operatiilor I/O in C++

Notatia ios :: <Nume_indicator> este folosita pentru a identifica indicatorul ca pe un membru al clasei ios.

Exemplificam cele spuse mai sus prin codul C++ de mai jos.

#include<iostream.h>

#include<iomanip.h>

#include<conio.h>

#include<stdio.h>

void main

Fisiere utilizator in C++

Chiar daca abordarea operatiilor I/O din C++ formeaza un sistem integrat, operatiile cu fisiere (altele decat cele predefinite), sunt suficient de specializate pentru a fi necesar sa la discutam separat.

Pentru a efectua operatii I/O cu fisiere conform paradigmei C++, trebuie sa includeti in programul Dvs. fisierul antet FSTREAM.H. Acesta defineste mai multe clase, printre care ifstream, ofstream si fstream. Aceste clase sunt derivate din istream si, respectiv, din ostream la care ne-am referit si mai sus.

Deschiderea si inchiderea unui fisier

Un fisier se deschide in C++ legandu-l de un flux. Inainte de a putea sa deschideti un fisier, trebuie, pentru inceput, sa aveti un flux. Exista trei tipuri de fluxuri: de intrare, de iesire si de intrare/iesire. Pentru a crea un flux de intrare, trebuie sa-l declarati ca fiind din clasa ifstream. Pentru a crea un flux de iesire, trebuie sa-l declarati ca fiind din clasa ofstream. Fluxurile care efectuiaza atat operatii de intrare cat si operatii de iesire, trebuie declarate ca fiind din clasa fstream. Odata declarat fluxul, o modalitate de a-i asocia un fisier extern o reprezinta utilizarea functiei open() avand prototipul:

void open(const char *nume_fisier , int mod, int acces=filebuf::openprot);

nume_fisier este un nume extern de fisier care poate include si specificarea caii de acces.

Valoarea parametrului mod determina modul de deschidere a fisierului. Parametrul mod poate avea una sau mai multe din valorile prezentate in tabelul de mai jos.

Nume mod

Operatie

ios::app

Adauga date in fisier

ios::ate

Cand se deschide pentru prima data, opereaza pozitionarea in fisier la sfarsitul fisierului (ate inseamna la sfarsit)

ios::binary

Deschide fisierul in mod binar, inhiband interpretarea caracterelor <CR> <LF>

ios::in

Deschide fisierul pentru citire

ios::nocreate

Nu efectueaza deschiderea fisierului daca acesta nu exista deja

ios::noreplace

Daca fisierul exista, incercarea de a-l deschide pentru iesire esueaza, cu exceptia cazului in care ios::app sau ios::ate sunt operate

ios::out

Deschide fisierul pentru scriere

ios:trunc

Trunchiaza fisierul daca el exista deja

Tabelul 17. Valorile parametrului care stabileste modul de deschidere a unui fisier

Puteti specifica mai mult de un mod de lucru pentru un fisier, folosind operatorul pe biti SAU cu modurile respective. De exemplu, pentru deschiderea unui fisier pentru iesire si pozitionarea pointerului la sfarsitul lui se folosesc modurile ios::out si ios::ate astfel:

ofstream oflux("o_fisier",ios::out | ios::ate);

ceea ce ne arata al doilea procedeu de deschidere a unui fisier, utilizand constructorul clasei ofstream sau, de ce nu, ifstream, daca este cazul.

Pentru a inchide un fisier, folositi functia membru close(). Aceasta functie nu preia nici un parametru si nu returneaza nici o valoare. De analizat utilizarea functiei close() in exemplele care vor urma.

Scrierea si citirea fisierelor text

Sunt doua operatii foarte usoare, realizate apeland la operatorii >> si << intr-un mod asemanator operatiilor referitoare la consola sistemului, cu deosebirea ca in loc sa folositi cin si cout apelati la un flux legat de un fisier . Codul de mai jos arata cum poate fi afisat pe ecranul monitorului continutul unui fisier text.

#include <fstream.h>

#include <stdlib.h>

#include<conio.h>

//Functia principala a programului citeste linia de comanda a programului

void main(int argc,char *argv[])

// Deschidere fisier text de nume specificat in argv[1]

ifstream in(argv[1],ios::in);

if (!in)

char c;

clrscr();

while (in.get(c))

cout<<c;

in.close();

I/O in fisiere de tip binar

Exista doua modalitati de a scrie si citi date binare intr-un fisier. Prima modalitate se refera la utilizarea functiilor get() si put(). Aceste functii sunt orientate pe octeti, ceea ce inseamna ca get() va citi un octet de date iar put() va scrie un octet de date.

Functia get() are mai multe forme; o prezentam, in continuare, impreuna cu omoloaga ei put(), pe cea mai des folosita:

istream &get(char &ch);

ostream &put(char ch);

Functia get() citeste un singur caracter din streamul asociat si memoreaza valoarea sa in ch. De asemenea, se mai observa ca functia returneaza o referinta catre flux. Functia put() scrie ch in flux si returneaza fluxului o referinta.

Ca un exemplu de utilizare a functiilor get() si put() prezentam codul de mai jos, care realizeaza copierea unui fisier in altul. Daca numele executabilului asociat acestui cod este copyf, atunci sintaxa de lansare in executie a programului este:

copyf <fisier_sursa> <fisier_destinatie>

#include<stdlib.h>

#include <fstream.h>

void main(int argc, char *argv)

//Deschide fisierul de intrare si il conecteaza la fluxul ins

ifstream ins(argv[1]);

if(!ins)

//Deschide fisierul de iesire si il conecteaza la fluxul outs

ofstream outs(argv[2]);

if(!outs)

//Citeste din sursa si scrie in destinatie

char c;

while(ins.get(c) && outs) outs.put(c);

A doua modalitate de a citi si scrie blocuri de date in binar este folosirea functiilor din C++ read() si write(). Prototipurile lor sunt:

istream &read(unsigned char *buf, int numar);

ostream &write(const unsigned char *buf, int numar);

Functia read() citeste numar octeti din fluxul asociat si il pune in buffer-ul indicat de buf . Functia write() scrie in fluxul asociat numar octeti cititi din buffer-ul spre care indica buf.

Detectarea EOF

Puteti sa detectati sfarsitul fisierului folosind functia membru eof() ,care are acest prototip:

int eof();

Ea returneaza o valoare nenula cand a fost atins sfarsitul fisierului; altfel, returneaza zero. Utilizarea functiei eof() si alte elemente legate de lucrul cu fisiere, prezentam in codul de mai jos, care realizeaza afisarea continutului unui fisier atat in hexazecimal cat si in cod ASCII, atunci cand codul asociat este printabil.

#include <fstream.h>

#include <ctype.h>

#include <iomanip.h>

#include <stdlib.h>

#include<conio.h>

void main(int argc,char *argv[])

ifstream in(argv[1],ios::in|ios::binary);

if (!in)

register int i,j;

int count=0;

char c[16];

cout.setf(ios::uppercase);

clrscr();

while(!in.eof())

if (i<16) i--;

for(j=0;j<i;j++)

cout<<setw(3)<<hex<<(int) c[j];

for(;j<16;j++)

cout<<'t';

for(j=0;j<i;j++)

if(isprint(c[j])) cout<<c[j];

else cout<<'.';

cout<<endl;

count++;

if(count==16)

in.close();

Accesul aleator in fisiere

In sistemul de I/O din C++, accesul aleator se efectueaza folosind functiile

seekg() si seekp(). Formele lor cele mai uzuale sunt:

istream &seekg(streamoff offset, seek_dir origine);

ostream &seekp(streamoff offset, seek_dir origine);

streamoff este un tip definit in IOSTREAM.H, capabil sa contina cea mai mare valoare valida pe care o poate avea offset, iar seek_dir este o enumerare care are aceste valori:

ios::beg

ios::cur

ios::end

Sistemul de I/O din C++ opereaza cu doi pointeri asociati unui fisier. Unul este pointerul de get , care specifica unde va aparea urmatoarea operatie de intrare in fisier. Celalalt este pointerul de put si specifica unde va avea loc urmatoarea operatie de iesire. Dupa fiecare operatie de intrare sau iesire, pointerul corespunzator este avansat automat, secvential. Dar, folosirea functiilor seekg() si seekp() permite un acces nesecvential la datele din fisier.

seekg() si seekp() deplaseaza pointerul de inregistrare cu offset octeti fata de origine.

Cu mentiunea ca lucrul cu fisiere in C++ are nenumarate alte fatete pentru a caror prezentare nu dispunem de timpul si spatiul necesar, incheiem aceasta scurta excursie in problematica fisierelor. In fine, pentru curiosi facem si precizarea ca, insusi batranul C are propria filozofie, extrem de puternica, in ceea ce priveste lucrul cu fisiere.

3 Programare generica in C++

In programarea profesionala apar nenumarate situatii in care reutilizarea codului presupune o solutie de un anumit tip pentru o problema data. Situatia la care ne referim in aceasta sectiune este, potential vorbind, urmatoarea: Ce putem face pentru a comprima codul sursa in situatia in care structuri de date, diferite ca tip, suporta prelucrari similare.

Solutia acestei probleme de stil de programare o reprezinta programarea generica. Exprimandu-ne in termenii limbajului C, o functie generica defineste un set general de operatii care vor fi aplicate unor tipuri de date diferite.

Ca un exemplu, o solutie generica pentru modelarea unei stive este un pretext ideal pentru precizarea ideilor principale ale programarii generice.

Altfel spus, daca dorim o stiva, in care, de la caz la caz, sa putem pastra numere intregi, numere reale sau siruri de caractere (deci tipuri de date diferite), operatiile fiind aceleasi ( push() si pop() ), este clar ca ne aflam in situatia in care avem nevoie de suport pentru scrierea de cod cu proprietati generice.

Daca in Pascal programarea generica se baza pe tipuri procedurale si programarea la nivel de octet, in C++ exista suport evoluat pentru programare generica, sub forma sabloanelor. Cu un sablon, in C++ se poate crea o functie generica sau o clasa generica.

Functii TEMPLATE

O functie template este o functie sablon, avand unul sau mai multi parametri formali de un tip generic. In functie de nevoile de utilizare a acestei functii, compilatorul genereaza functii propriu-zise, inlocuind tipul generic cu un tip concret. Tipul concret poate fi orice tip fundamental, derivat sau clasa predefinita.

Consideram un exemplu. Fie functia max(x,y) care returneaza valoarea maxima a argumentelor sale. Tipul variabilelor x si y trebuie, obligatoriu, specificat in momentul compilarii. Solutia clasica consta in redefinirea (over-loading) functiei max pentru fiecare tip al argumentelor x si y (de observat si cele spuse la paragraful 2.2 relativ la functii supraincarcate in C++).

Trebuie, asadar, sa definim mai multe versiuni ale functiei max.

int max(int x, int y)

float max(float x, float y)

Mecanismul template permite definirea o singura data a sablonului de functii, dupa care se genereaza automat functiile propriu-zise in concordanta cu necesitatile de utilizare, dar ,evident, in faza de compilare.

Sintaxa la specificare este:

template <class Nume_tip_generic_1 [,.class Nume_tip_generic_n]>

Nume_sablon

definitie_sablon

De precizat urmatoarele:

YCaracterele < si > fac parte din sintaxa obligatorie.

YLista de parametri formali ai unei functii sablon trebuie sa utilizeze toate tipurile de date generice.

YIn cazul functiilor template nu se fac conversii

YFunctia care are acelasi nume si acelasi numar de parametri cu o functie sablon se numeste caz exceptat (Supraincarcarea explicita este prioritara).

Sintaxa la utilizare este:

Nume sablon (Expresie_1[, .,Expresie_n]);

Prezentam, in continuare, definitia functiei sablon max , urmata de o secventa client de utilizare.

template <class T>

T max(T x, T y)

#include <conio.h>

#include<iostream.h>

//Definire sablon functie

template<class T>

T max(T x,T y)

void main

Prezentam, totodata, un exemplu de functie generica pentru compararea unor date dupa valoarea unei chei incapsulate in aceste date.

#include <conio.h>

#include <stdio.h>

#include <string.h>

#include <iostream.h>

#include <ctype.h>

//Definirea unei <functii generice> pentru compararea

//unor date dupa valoarea unei chei incapsulate

//in aceste date

template <class T>

int comp(T i1,T i2)

//Structura aleasa pentru exemplificare

//cheia generica incapsulata este campul <key>

struct tpers

//Instantiere <struct tpers>

struct tpers tam,pers[50];

void main

//Listare persoane pe ecranul monitorului

//in ordinea citirii de la tastatura

while(toupper(getch())!='N');

clrscr();

cout<<'Listare persoane in ordinea citirii de la tastatura'<<endl;

cout<<'__________ ______ ____ ________________'<<endl;

for(int j=0;j<i;j++)

cout.width(30);cout.setf(ios::left);

cout<<pers[j].np<<' '<<pers[j].key<<endl;

getch();

//Sortare persoane

int sortat;

do

};

};

while(!sortat);

//Listare persoane dupa sortare in ordinea

//crescatoare a matricolelor

clrscr();

cout<<'Listare persoane dupa sortare.'<<endl;

cout<<'__________ ______ ____ _____________'<<endl;

for(int k=0;k<i;k++)

cout.width(30);cout.setf(ios::left);

cout<<pers[k].np<<' '<<pers[k].key<<endl;

getch();

Clase TEMPLATE

O clasa template defineste un sablon pe baza caruia se pot genera clase propriu-zise. Din acest motiv, o clasa template se mai numeste si clasa generica , clasa generator sau metaclasa.

AAstfel ca, o clasa template devine o clasa de clase, reprezentand cel mai inalt nivel de abstractizare admis de programarea obiect orientata

In cadrul clasei sablon se pot declara atribute informationale de un tip ambiguu, care sunt particularizate in cadrul clasei generata pe baza sablonului. Evident, si in acest caz, generarea se face in faza de compilare in concordanta cu cerintele clientului.

Sintaxa la specificarea clasei:

template <class T>

class nume_clasa

;

Sintaxa la implementarea functiilor membre ale unei clase template:

template <class T> Tip returnat nume_clasa <T>::nume_functie(.)

;

Pentru mai multa claritate prezentam si exemplul de mai jos.

#include <iostream.h>

#include <stdlib.h>

#include <conio.h>

const int SIZE = 10;

//Definire clasa matrice generica

template <class ATip>

class genmat

//Implementare constructor clasa generica

template <class ATip> genmat<ATip>::genmat()

//Implementare supraincarcare operator [ ]

template <class ATip> ATip &genmat<ATip>::operator[ ](int i)

return a[i];

//Functia principala

void main

Tot pentru exemplificare sa consideram si o situatie deosebit de simpla. Ni se cere sa construim o clasa template corespunzatoare conceptului de stiva, din care, ulterior, sa se poata concretiza clase care simuleaza stiva pentru tipuri de date diferite.

//Clasa sablon CSTack

template <Class T>

class Cstack

~Cstack()

void push(T a)

T pop()

Dupa ce o astfel de clasa template a fost declarata si definita, se poate trece deja la instantierea de obiecte. Singura deosebire fata de folosirea unei clase obisnuite consta in faptul ca trebuie specificat tipul concret care inlocuieste tipul generic T. Ca un exemplu, sa instantiem un obiect stiva (CStack) , in care incap maximum 100 elemente de tip char.

CStack<char> sc

In acest caz, compilatorul genereaza clasa ce rezulta prin inlocuirea lui T cu char, dupa care instantiaza obiectul sc. Constructorul acestuia primeste ca argument valoarea 100. Pentru mai multe detalii urmariti exemplul de mai jos.

#include <iostream.h>

#include <conio.h>

// Definirea clasei stack. Se vede ca instantele ei nu sunt protejate fata de //exceptii.

Despre exceptii urmeaza sa discutam.

template <class T>

class CStack

~CStack() //destructor

void push(T a) //Functia de inserare in stiva

T pop()

//Functia principala

void main

//Vizualizare continut stiva

clrscr();

for (i=0;i<=9;i++)

getch();

//Al doilea exemplu de instantiere a stivei generice; caractere, incepand cu "A"

CStack<char> st2(20);

//Incarcare stiva

for (i=65;i<75;i++)

//Vizualizare continut stiva

clrscr();

for (i=0;i<10;i++)

getch();

Tratarea exceptiilor

Programatorii adevarati trebuie sa ia in calcul si posibilitatea de a crea programe robuste, care fac fata atat cerintelor specificate dar nerafinate suficient, cat si cerintelor nespecificate dar formulate de utilizator, din diverse motive. Programele care au aceste calitati se numesc robuste.

In programarea clasica solutia acestei probleme se putea numi, destul de exact spus, programare defensiva. Seamana putin cu conducerea preventiva din soferie daca ne gandim ca programand defensiv, in fond punem raul inainte, deci nu ne bazam pe cumsecadenia si buna pregatire a utilizatorului.

Pentru a face fata cerintelor legate de problema tratarii exceptiilor (asa se numesc in jargon profesional erorile care apar in timpul executiei programelor) anumite limbaje de programare ofera suport adecvat. As include aici limbaje precum Delphi, C++, Java, Visual C.

Nu toate compilatoarele de C++ ofera suport, dar standardul ANSI C++ cere acest lucru in mod explicit. Compilatoarele din familia Borland incepand cu versiunea 4.0 ofera acest suport.

Esentialul din punctul de vedere al programatorului C++ este ca el sa-si formeze abilitatea de a scrie in jurul aplicatiilor cod C++ care indeplineste functia de handler de exceptii.

Suportul sintactic C++ pentru tratarea exceptiilor se rezuma la trei cuvinte cheie, a caror semantica preliminara o prezentam in Tabelul xxxxx.

Cuvantul cheie

Semnificatie

try

Delimiteaza o portiune de cod in care se instituie controlul sistemului asupra exceptiilor in timpul rularii.

throw

Lanseaza o exceptie de un anumit tip

catch

Capteaza o exceptie lansata

Tabelul 18 Cuvintele cheie ale limbajului C++ referitoare la tratarea exceptiilor

Forma de baza a tratarii exceptiilor

Asadar, atunci cand programele dumneavoastra efectueaza prelucrarea exceptiilor, trebuie sa includeti in cadrul unui bloc try instructiunile pe care doriti sa le monitorizati in vederea unei exceptii. Daca executia unei instructiuni se termina cu o eroare, trebuie sa lansati o eroare corespunzatoare actiunii functiei in care se afla instructiunea. Programul plaseaza instructiunea throw in cadrul blocului try-catch. Forma generalizata a blocului care capteaza si trateaza erorile este:

try

catch (Tip_exceptie Nume_variabila )

In cadrul acestei forme generalizate, valoarea valoare_exceptie lansata trebuie sa corespunda tipului Tip_exceptie .

Scrierea unui handler de exceptii simplu

Pentru a intelege mai bine semantica unui handler de exceptii, studiati programul de mai jos.

#include <iostream.h>

void main()

catch (int i)

cout<<'Sfarsit.';

Programul de mai sus implementeaza un bloc try-catch simplu. In loc sa se astepte ca programul sa esueze datorita unei erori, se utilizeaza instructiunea throw pentru lansarea erorii prezumtive. Dupa ce blocul try lanseaza eroarea, blocul catch o capteaza si prelucreaza valoarea transmisa de instructiunea throw. Este evident si din acest exemplu ca mecanismul try-throw-catch ofera suport pentru rezolvarea problemei tratarii exceptiilor dar nu rezolva de la sine aceasta problema. Altfel spus, tratarea corecta a exceptiilor unui program este o problema de atitudine ca proiectant si ca programator.

Lansarea exceptiilor cu o functie din cadrul blocului try

Atunci cand programele apeleaza functii din cadrul blocurilor try , C++ va transmite exceptia aparuta intr-o astfel de functie in afara functiei daca nu exista un bloc try in interiorul functiei.

Exemplul de mai jos arata cum se petrec lucrurile intr-o astfel de situatie.

#include <iostream>

void XHandler(int test)

void main()

catch(int i)

cout<<'Sfarsit';

Plasarea unui bloc try intr-o functie

Am vazut cum apare un bloc try in functia principala a unui program. C++ permite blocuri try si in alte functii ale unui program diferite de functia principala.

Atunci cand se plaseaza un bloc try intr-o functie C++ reinitializeaza blocul de fiecare data cand intrati se intra in acea functie. Programul urmator ilustreaza cele spuse.

#include <iostream.h>

void XHandler(int test)

catch(int i)

void main()

Un comentariu pe marginea celor prezentate pana acum ar fi urmatorul: o instructiune catch se executa numai daca programul lanseaza o exceptie in cadrul blocului try situat imediat inainte. In caz ca o astfel de exceptie nu se lanseaza blocul catch va fi ignorat.

Utilizarea mai multor instructiuni catch cu un singur bloc try

Pe masura ce tratarile exceptiilor devin tot mai complexe, uneori este necesar si posibil ca un singur bloc try sa lanseze exceptii de mai multe tipuri. In cadrul programelor dumneavoastra puteti construi un handler de exceptii astfel incat sa accepte captarea mai multor exceptii. Intr-o astfel de situatie sintaxa generala este:

try

catch (tip1)

catch(tip2)

catch(tipn)

Cu acest amendament sintactic deducem ca instructiunile catch pot capta orice tip returnat, nu numai tipurile de baza acceptate de C++. Acest 'fenomen' este ilustrat in codul de mai jos.

#include <iostream.h>

void XHandler(int test)

catch(int i)

catch(char *sir)

catch(double d)

void main()

Blocuri catch generice (utilizarea operatorului puncte de suspensie)

Programele scrise de dumneavoastra pot capta exceptii din cadrul mai multor blocuri try (de exemplu un bloc try care incapsuleaza mai multe functii care lanseaza exceptii diferite din blocuri try diferite sau sa utilizeze mai multe instructiuni catch intr-un singur bloc try. C++ permite, de asemenea, utilizarea operatorului puncte de suspensie (.) pentru a capta orice tip de eroare care apare intr-un singur bloc try. Sintaxa care permite captarea tuturor erorilor care apar intr-un bloc try este prezentata mai jos.

try

catch(.)

Pentru exemplificare propun codul de mai jos.

#include <iostream.h>

void XHandler(int test)

catch(.)

void main()

Evident, prelucrarile din cadrul blocului catch generic trebuie sa fie independente de tipul erorii.

Mecanismul captarii exceptiilor explicite poate fi combinat cu mecanismul exceptiilor generice ca in exemplul de mai jos.

#include <iostream.h>

void XHandler(int test)

catch(int i)

catch(.)

void main()

Restrictionarea exceptiilor

Pe masura ce programele dumneavoastra devin mai complexe, ele vor apela frecvent functii din cadrul unui bloc try. Atunci cand programele dumneavoastra apeleaza functii dintr-un bloc try, puteti restrictiona tipurile de exceptii pe care functia apelata le poate lansa. De asemenea puteti preveni lansarea oricarei exceptii dintr-o anumita functie.

Sintaxa pentru restrictionare este:

tip_returnat nume_functie(lista_arg) throw(lista_tipuri )

Sintaxa care inhiba lansare oricarei exceptii este:

tip_returnat nume_functie(lista_arg) throw()

Este bine sa subliniem ca atunci cand declarati o functie cu clauza throw ea poate sa lanseze doar acele tipuri precizate in lista. Daca functia lanseaza orice al tip programul este abortat.

Un exemplu in continuare.

#include <iostream.h>

void XHandler(int test) throw(int, char, double)

void main()

catch(int i)

catch(char c)

catch(double d)

cout<<'Sfarsit';

Relansarea unei exceptii

In anumite situatii poate fi necesar sa se relanseze o exceptie din interiorul unui handler de exceptii. Daca relansati o exceptie, C++ o va transmite unui bloc try exterior daca acesta exista. Cea mai probabila situatie in care puteti opta pentru aceasta varianta este atunci cand doriti sa tratati o exceptie in cadrul a doua programe handler distincte. Pentru mai multa claritate urmariti exemplul de mai jos.

#include <iostream.h>

void XHandler(void)

catch(char *)

void main()

catch(char *)

cout<<'Sfarsit.';

Mod de utilizare a exceptiilor

Toate elementele prezentate au incercat sa demonstreze ca C++ are o atitudine activa fata de problema tratarii exceptiilor. Suportul oferit de C++ il ajuta pe programator sa defineasca un comportament al programului cand se produc evenimente anormale sau neasteptate. O idee mai pragmatica de utilizare a suportului C++ in situatii efective o puteti desprinde din exemplul de mai jos.

#include <iostream.h>

void div (double a, double b)

catch(double b)

void main()

while (i!=0);



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 1518
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved