You are on page 1of 58

Crash Course in C++

Un tutorial practic despre C++ destinat programatorilor de Java


Adrian Scoic a
Observatii si comentarii: adrian.scoica@gmail.com , ,

15 februarie 2011

CUPRINS

Cuprins
1 Introducere 1.1 Ce este C++? . . . . . . . . . . . . . . . . . . . . . . . . 1.2 C++ sau Java? (*) . . . . . . . . . . . . . . . . . . . . . 1.3 C++ vs Java. Avantaje si dezavantaje comparative. (*) , 1.3.1 Compilat vs. interpretat (*) . . . . . . . . . . . . 1.3.2 Timpul de executie (*) . . . . . . . . . . . . . . . , 1.3.3 Memoria si accesul la memorie (*) . . . . . . . . , 2 Clase 2.1 Declaratie . . . . . . . . . . . . . . . . . . , 2.2 Modicatorii de acces . . . . . . . . . . . 2.3 Constructori . . . . . . . . . . . . . . . . . 2.4 Denire . . . . . . . . . . . . . . . . . . . 2.5 Ciclul de viat al obiectelor. Destructori. ,a 2.6 Membrii statici (**) . . . . . . . . . . . . 2.7 Derivarea claselor (**) . . . . . . . . . . . 2.8 Polimorsm. Functii virtuale (**) . . . . . , 2.9 Supra arcarea operatorilor . . . . . . . . nc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 2 3 3 3 4 6 6 7 8 9 13 16 18 20 23

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

3 Input/Output 27 3.1 Fisiere si stream-uri . . . . . . . . . . . . . . . . . . . . . . . . . 27 , , 4 Siruri de caractere 30 , 4.1 Clasele std::string si std::stringstream . . . . . . . . . . . . . . . 30 , 5 Gestiunea memoriei 32 5.1 Alocarea memoriei: new vs. malloc . . . . . . . . . . . . . . . . . 32 5.2 Dezalocarea memoriei: free vs. delete . . . . . . . . . . . . . . . . 34 6 Standard Template Library 6.1 Introducere: de ce STL? (*) . . . . . . . 6.2 Template-uri (**) . . . . . . . . . . . . . 6.3 Componentele STL . . . . . . . . . . . . 6.4 Containere: vector, list, slist, deque, set, 6.5 Adaptori: queue, stack, priority queue . 6.6 Iteratori . . . . . . . . . . . . . . . . . . 6.7 Algoritmi . . . . . . . . . . . . . . . . . 6.8 Greseli frecvente . . . . . . . . . . . . . , 7 Namespace-uri (***) 8 Tratarea Exceptiilor (***) , 36 36 37 39 40 46 46 49 51 53 55

. . . . . . . . . map . . . . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

CUPRINS

9 Resurse Legend: a (*) informatii cu caracter general , (**) anumite aspecte de detaliu (***) facilitti avansate si mai rar folosite ale limbajului a, ,

56

INTRODUCERE

1
1.1

Introducere
Ce este C++?

Mult lume deneste C++ ca ind un superset de C. Aceast denitie este a a , , partial corect, dar esueaz a conferi limbajului credit pentru inovatiile si a a n , , , , gradul de expersivitate superioare pe care le aduce fat de limbajul C. ,a C++ a fost creat de ctre Bjarne Stroustrup pe baza limbajului C, pentru a a suporta noile concepte vehiculate programare la vremea respectiv, cunosn a cute colectiv sub numele de Programare Orientat pe Obiecte. Limbajul a fost a proiectat s permit exprimarea conceptelor specice programrii orientate spre a a a obiecte, ind construit s suporte clase, virtualizare, supra arcare, mostenire a nc , multipl, template-uri si tratarea exceptiilor, printre altele, trsturi care l-ar a aa , , distantat mod normal de C a de la n nc nceput. Cu toate acestea, familiari, tatea programatorilor cu C-ul, nevoia de compatibilitate invers cu codul deja a existent, precum si statutul incontestatbil al C-ului au condus la dezvoltarea , unui limbaj care s cuprind gramatica sa aceleasi constructii sintactice. a a n , , C++ este unul dintre cele mai populare limbaje de programare scrise vreodat, ind cel putin la fel de ecient si portabil ca C-ul, dar complexitatea a , , crescut ct si libertatea de exprimare (amintit cel mai adesea sub forma liba a , a ertatea de a gresi) foarte mare au ncurajat dezvoltarea de limbaje simplicate , sau specializate care s implementeze doar partial functionalittile acestuia si a a, , , , s se concentreze pe alte aspecte. a Cele mai cunoscute limbaje care au fost puternic inuentate de C++ sunt , Java, respectiv C#.

1.2

C++ sau Java?

Uneori se pune ntrebarea: Este C++ mai bun sau mai ru dect Java? Adevrul a a a este c un rspuns la aceast a a a ntrebare, din pcate, nu exist. principiu, C++ a a In pune la dispozitia programatorului mult mai mult libertate de exprimare, dar a , acest lucru nu este ntotdeauna ceva bun. Mai degrab, a ntrebarea ar face parte din aceeasi clas (no pun intended) cu a ntrebarea Este Engleza mai bun sau a , mai rea dect Romna?. a a Este destul de evident c att Romna ct si Engleza au calitate de limbi a a a a , n vorbite, aceeasi putere de exprimare. Unele idei sunt mai usor de exprimat n , , Romn, altele sunt mai adcinate Englez, dar denitiv nu exist nici a a nr a n a n a un concept ce se poate exprima ntr-una din limbi fr s se regseasc printre aa a a a constructiile celeilalte un echivalent. , general, Java s-au sacricat o parte considerabil dintre functionalittile In n a a, , C++ din dorinta de a grbi ritmul de dezvoltare al aplicatiilor de ctre proa a , ,

INTRODUCERE

gramatori, precum si din dorinta de a standardiza mai usor protocoalele folosite. , , , Motivul principal st faptul c Java a fost initial conceput pentru a realiza a n a , aplicatii end-user bazate pe accesul la Internet, timp ce C++ se foloseste n , , mai mult pentru a realiza aplicatii de tip back-end (servere) sau aplicatii care , , efectueaz intens calcule si deci trebuie optimizate ct mai bine. a a ,

1.3

C++ vs Java. Avantaje si dezavantaje comparative. ,

In ncheierea acestei sectiuni, voi trata comparativ cteva dintre diferentele maa , , jore dintre C++ si Java, pregtind acelasi timp mindset-ul necesar parcurgerii a n , , urmtoarelor capitole. Din dorinta de a m exprima ct mai ecient, voi cona a a , sidera stiute aspectele legate de Java si m voi concentra mai mult asupra celor a , , specice C++. Cu toate acestea, tutorialul de fat nu este si nu se doreste a ,a , , un substitut pentru un manual de C++. Mai degrab, considerati-l un cheat a , sheet care s v introduc limbaj, astfel at s v ajute s scrieti cod a a a n nc a a a , corect si mult mai ecient. Pentru cteva resurse dedicate de C++, vericati a , , sectiunea de bibliograe. , Recomand parcurgerea urmtoarelor pagini, mai ales dac Java este primul a a limbaj de Programare Orientat Obiect pe care l-ati stpnit. Este posibil s a a a a , nu realizati la o prim lectur importanta unora dintre diferente, dar v veti a a a , , , , lovi relativ repede de ele dac veti a , ncerca s traduceti efectiv sintaxa de Java a , si veti constata c de fapt codul nu functioneaz asa cum v-ati asteptat. a a , , , , , ,

1.3.1

Compilat vs. interpretat

C++ este, ca si C, un limbaj compilat. Compilatorul parseaz codul surs a a , si produce la iesire cod binar formatul masinii zice pe care se compileaz n a , , , (ex: executabile Linux pe 32bit). Avantaje: executabilele sunt imagini de procese pentru masina gazd. Aplicatiile scrise C++ au toate functionalittile a n a, , , , celorlaltor aplicatii native ale sistemului: un PID, siere de I/O (stdin, std, out, stderr), si accesul direct la toate celelalte resurse ale sistemului de operare , (de exemplu: sistemele de siere, socketi, memoria virtual, apeluri de sistem), a , , linkarea cu biblioteci partajate pentru arcarea dinamic de cod la rulare, etc. nc a Compilatorul de Java, pe de alta parte, produce cod intermediar care trebuie interpretat. Cu alte cuvinte, dup compilarea Java, bytecode-ul obtinut a , este rulat interiorul unei masini virtuale. Avantaje: deoarece aplicatiile scrise n , , Java nu ruleaz nativ masina zic, ci executia lor este simulat de n a n a a , , Java Runtime Environment, acest lucru eradicheaz practic problemele de coma patibilitate ntre platforme, permite arcarea dinamic a claselor la runtime, nc a existenta blocurilor statice de initializare si mecanismul de garbage collection. , , , 1.3.2 Timpul de executie ,

INTRODUCERE

Compilatoarele de C++ sunt, general, capabile s produc cod binar extrem n a a de optimizat. Mai mult, faptul c acest cod se ruleaz nativ pe masina gazd a a a , reduce la minimum overhead-ul. De asemenea, C++ nu v adaug singur a a n cod functionalitti suplimentare pe care nu le-ati cerut si care v trag aplicatia a, a , , , , napoi. Nu ultimul rnd, C++ permite, optional, specicarea de directive n a , care s instruiasc compilatorul despre cum s v genereze codul de asamblare. a a a a

Dac nici asta nu este sucient, puteti oricnd s introduceti proiectul a a a n , , vostru de C++ implementri de functii critice scrise direct limbaj de asama n , blare (care daca sunt scrise bine, pot accelera semnicativ aplicatia). Desi ar , , putea prea un gest extrem, circumstantele speciale impun msuri speciale. a a , Nu este recomandat totusi s introduceti C++ cod de asamblare, pentru c a a , , n pierdeti portabilitatea (si categoric nu nainte de nalizarea aplicatiei). , , , Viteza de executie a fost mereu primul si cel mai important dezavantaj pen, , tru Java. Deoarece bytecode-ul trebuie interpretat de masina virtual, acest a , lucru nseamn c pentru ecare operatie elementar de bytecode, masina zic a a a a , , ar trebui teoretic s execute dou instructiuni: una pentru interpretare si una a a , , pentru executie. cazul noilor masini virtuale, acest lucru nu este valabil, si In , , , performantele aplicatiilor Java s-au mbunttit constant de la aparitia limbaa a, , , , jului: dac la a nceput, o aplicatie Java rula de pn la de 20 de ori mai lent a a , dect echivalentul C++, acum s-a ajuns la un un factor de timp de aproximativ a 1,5 - 2. Lupta pentru performant a dus la aparitia masinii virtuale Java HotSpot, ,a , , scris C++ (:D). Ideea din spatele HotSpot este cu adevrat state-of-the-art a n a ceea ce priveste stiinta compilatoarelor: crearea de optimizri adaptive ale n a , , , codului surs functie de statisticile de runtime ale codului, si poate coduce a n , , cazuri particulare la cod la fel de rapid ca si echivalentul nativ C++ fr n aa , optimizri specice. Alte inovatii cu adevrat valoroase aduse de masinile vira a , , tuale Java sunt Just-In-Time compilation, Garbage Collection sau Class Data Sharing.

1.3.3

Memoria si accesul la memorie ,

Gestiunea memoriei este poate ultima diferenta cu adevrat important dina a , tre C++ si Java. C++, executabilul nal este imaginea unui proces. Cu In , alte cuvinte, veti avea acces la un spatiu de memorie virtual propriu, din care a , , puteti aloca/citi orice, conform cu nevoile aplicatiei (ca C). La fel ca C, n n , , puteti accesa memoria e prin intermediul unu obiect, e prin numele variabilei , care ocup acea zon de memorie, e prin intermediul unui pointer. Alocarea, a a eliberarea, precum si accesul corect la memorie sunt n ntregime responsabil, itatea programatorului. Mai multe despre asta veti , elege din ciclul de nt , viat al obiectelor. Mai mult, C++ pune la dispozitie si facilitti mai avansate a, ,a , ,

INTRODUCERE

de acces la memorie: referintele: sunt doar dubluri de nume pentru obiecte existente spatiul n , , de memorie, si functioneaz putin diferit dect Java: ele trebuie initializate a , a n , , , la instantiere, si se comport precum obiectul referentiat. Nu le puteti disa , , , , truge mod explicit, si nici atribui, dar v scap de o parte din greselile n a a , , lucrurlui cu pointeri. iteratorii: iteratorii C++ sunt pur si simplu clase care abstractizeaz n a , un pointer (permit operatii precum (*it) sau it++ prin supra arcarea nc , operatorilor specici). Spre deosebire de Java, iteratorii sunt stronglytyped si mai intuitiv de folosit, dar la fel ca si pointerii, v pot crea a , , probleme dac nu sunteti atenti. a , , smart-pointerii: exist doar C++, deoarece Java nu aveti acces a n n , la memorie. Functioneaz ca niste pointeri obisnuiti, cu mentiunea c a a , , , , , atribuirea se face prin transferul complet al obiectului referentiat, pen, tru a evita memory leak-urile. Cel mai probabil nu veti avea prea curnd a , de a face cu ei. Deoarece gestiunea memoriei C++ este foarte complicat, si este probabil n a , sursa numrul 1 de erori pentru programele scrise acest limbaj, Java a venit a n cu o solutie radical: nu vi se permite accesul la memorie! Toate obiectele sunt a , manipulate prin referinte (mai apropiate ca functionalitate de pointerii din C++ , , dect de referintele de acolo). Se tolereaz si tipuri de dat scalare (int, double), a a, a , dar acestea nu pot integrate colectiile de date ale limbajului. Acest lucru n , poate uneori extrem de inconvenabil, pentru c duce la scrierea de cod foarte a inecient! De asemenea, Java v descurajeaz s contati pe ciclul de viat al a a a , ,a obiectelor, si nu v las s controlati consumul de memorie (Garbage Collecterul a a a , , este nedeterminist). Aparent, acest lucru este convenabil pentru programator, dar realitate v oblig s apelati in logica aplicatiei cod explicit pentru elibn a a a , , erarea resurselor (siere, coenxiuni), ceea ce poate complica lucrurile. , Totusi, decizia Java de a nu v permite accesul la memorie are si alte jusa , , ticri. Deoarece codul se execut masina virtual, memoria nu este mapat a a n a a , ad literam RAM, iar dac se dorea accesul la memorie, atunci ar trebuit simn a ulat acest lucru. De asemenea, dac vi s-ar permis accesul la memorie, atunci a ati putut compromite transparenta serializrii si se pierdea din usurinta lima , , , , , bajului de a scrie aplicatii network-oriented. cele din urm, nalitti diferite In a a, , justic decizii de implementare diferite! a

CLASE

2
2.1

Clase
Declaratie ,

Cel mai important concept din OOP este conceptul de clas. Desigur, toat lumea a a care a programat Java are deja o intuitie despre ce anume e o clas, dar nu n a , este corect s confundm un limbaj cu paradigma pe care trebuie s o exprime. a a a Eu propun s uitm o clip de programare ca s putem gndi mai elegant: Ce a a a a a este realitate o clas? n a principiu, cred c vom cu totii de acord c o clas descrie o multime In a a a , , de lucruri care au ceva comun. Asemnrile dintre dou obiecte din aceeasi n a a a , clas pot s e: a a structurale (oamenii au neuroni) comportamentale (oamenii gndesc) a Cu alte cuvinte, dac stii despre un obiect c se a , a ncadreaz a ntr-o clas, a atunci stii s descrii la nivel general. a l , Haideti s retinem acest idee si s revenim la C++. C++, o clas a , a a In a , , seamnn mult cu un tip de date. Ca s folosim o clas, trebuie mai ai s a a a a nt a o descriem. Putem face asta dou moduri: folosind keyword-ul class sau n a folosind keyword-ul struct.
1 class Student{ 2 3 char * name; 4 double grade[3]; 5 6 void setMark(int subject, double newMark); 7 }; // ATENTIE! Nu uitati de ;. Aceasta este o greseala foarte frecventa! 8 9 // sau asa: 10 11 struct Student{ 12 13 char * name; 14 double grade[3]; 15 16 void setMark(int subject, double newMark); 17 }; // ATENTIE! Nu uitati de ;. Aceasta este o greseala foarte frecventa!

CLASE

2.2

Modicatorii de acces

Ca si Java, membrii din interiorul unei clase, e ei date sau functii, pot avea , n , modicatori de acces: public - Datele sau functiile se pot folosi att din interioul ct si din afara a a , , clasei. private - Datele sau functiile se pot folosi doar interiorul clasei. Clasele n , care o deriv nu au acces la ele. a protected - Datele sau functiile pot folosite interiorul clasei curente n , si al tuturor claselor care o deriv. a , C++, exist o singur diferent In a a , , a ntre class si struct: nivelul de acces implicit, care este private - pentru clasele declarate cu class public - pentru clasele declarate cu struct Java, accesul trebuie specicat pentru ecare membru parte. C++, In n In este sucient s scriem cuvntul cheie o singur dat urmat de doua puncte si a a a a , el rmne valabil pn la alnirea unui alt modicator de acces, sau pn la a a a a nt a a nalul clasei. De exemplu, pentru clasele de mai sus, putem modica accesul la membri adugnd doar putin cod: a a , GRESEALA FRECVENTA! Dac veti a , ncerca s scrieti ca Java, a n nainte , , de numele membrului fr : veti primi eroare de compilare! aa ,
1 class Student{ 2 3 char * name; //private (implicit pt class) 4 protected: 5 double grade[3]; //protected 6 public: 7 void setMark(int subject, double newMark); //public 8 9 }; 10 11 // sau de exemplu cu struct 12 13 struct Student{ 14 15 char * name; //public (implicit pt struct) 16 private: 17 18 19 20 }; double grade[3]; //private void setMark(int subject, double newMark); //also private

CLASE

2.3

Constructori

Atunci cnd vrem s descriem o clas, un aspect important este s denim [cel a a a a putin] un constructor pentru acea clas. Ca si Java, constructorii nu rea n , , turneaz valori si au numele identic cu al clasei. a , De retinut este c dac nu declarm un constructor pentru o clas, atunci a a a a , compilatorul va considera din ociu un constructor care nu face nimic (nu are nici o instructiune). , exemplul anterior, ar bine s ne putem asigura cumva program c la In a n a orice moment de timp, numele unui student este un pointer valid dac este privit a din afara clasei (pentru ca daca reusim asta, niciodat nu o s avem SegFault a a , folosindu-l). Solutia din C era s crem o functie care initializeaz o structur, a a a a , , , dar C++ singurul mod corect de a face asta este s declarm constructori. n a a A folosi o functie separat de initializare ar a nsemna s avem a ncredere c a , , oricine ar declara vreodata un obiect de tip Student va apela constiincios , functia nainte de a folosi obiectul. Asta e cea mai sigura cale catre ore n , sir de debugging. ,
1 class Student{ 2 //private: este implicit, pentru ca am folosit class 3 char * name; 4 double grade[3]; 5 public: 6 void setMark(int subject, double newMark); 7 8 Student(); // Constructor fara parametri 9 10 }; Student(char * name); // Stim numele dinainte

CLASE

10

2.4

Denire

Chiar dac nu am amintit dect pe scurt despre cteva din conceptele relevante a a a pentru declararea claselor C++, relativ la Java acestea ar trebui s e sun a ciente pentru a avea o , elegere elementar a sintaxei. Vom trece continuare nt a n la denirea de clase. Ce este, totusi denirea unei clase si cum difer ea de declarare? Putem s a a , , ne gndim astfel: a Declararea unei clase reprezint o descriere general a acesteia. Coma a pilatorul a c exist tipul de dat corespunztor, stie ce metode sunt a a a a a , specice si ce variabile ar trebui s a ncapsuleze clasa, cum este ea nrudit a , cu alte clase, dar nu stie nimic concret despre clas. a , Denirea unei clase, pe de alt parte, este procesul prin care se detaliaz a a exact codul din metode care trebuie executat, dar si construirea mem, brilor statici memorie. Un alt termen echivalent pentru denire este n implementare. Java, clasele erau organizate pachete, iar metodele erau denite obliIn n gatoriu acelasi sier cu declaratia clasei din care fceau parte. Acest mod de n a , , , a scrie codul poate prea mai simplu din anumite puncte de vedere, dar este a destul de inexibil, motiv pentru care C++ este folosit doar situatii spen n , ciale pe care le vom aminti mai trziu. a Abordarea corect C++ este de a separa declaratia si denitia unei clase a n , , , siere distincte: n , Headere (*.h, *.hpp), care contin numai declaratii , , Implementri (*.cpp), care contin numai denitii a , , Avantajul evident este c putem a nlocui rapid o implementare scurt dar a inecient a unei clase cu una mai lung dar mai optimizat fr a trebui s a a a aa a mai editm codul, specicnd doar alt sier la compilare. Pe de alt parte, a a a , separndu-le, putem evita erorile de denire multipl atunci cnd avem lanturi a a a , mai complexe de includeri. Pentru a deni o metod dintr-o clas, trebuie s prexm metoda cu nua a a a mele clasei, urmat de operatorul de rezolutie de scop (::). Facem acest lucru , pentru a informa compilatorul c de fapt denim o metod dintr-o clas, si nu a a a , o functie global cu acel nume. Un exemplu valoreaza ct o mie de cuvinte. a a ,

CLASE

11

Pentru clasa urmtoare: a


1 // Student.h 2 3 #pragma once 4 5 #ifndef STUDENT H 6 #define STUDENT H 7 8 class Student{ 9 //private: este implicit, pentru ca am folosit class 10 char * name; 11 double grade[3]; 12 public: 13 void setMark(int subject, double newMark); 14 15 Student(); // Constructor fara parametri 16 Student(char * name); // Stim numele dinainte 17 }; 18 19 #endif

Un exemplu de implementare ar putea urmtoarul: a


1 // Student.cpp 2 3 #include <cstring> // Pentru strdup() 4 5 #include "Student.h" 6 7 // din clasa Student, denim functia Student() 8 Student::Student(){ 9 name = strdup("Name Unknown"); 10 } 11 12 // din clasa Student, denim Student(char*) 13 Student::Student(char * name){ 14 // ATENTIE! In C++, this este un pointer, nu o referinta! 15 this->name = strdup(name); 16 } 17 18 // din clasa Student, denim setMark(int, double) 19 void Student::setMark(int subject, double newMark){ 20 grade[subject] = newMark; 21 } 22

CLASE

12

declaratia de mai sus, ati remarcat dou elemente noi, care nu sunt obliIn a , , gatorii orice situatie, dar care au devenit standard practice C++: n n , #pragma once - aceast directiv este specic C++ si informeaz coma a a a , pilatorul s nu includ acelasi sier de mai multe ori a a ntr-o ierarhie de , , clase. #ifndef ... #endif - aceast directiv este specic C si a a a ndeplineste , , acelasi rol. De obicei se pun amndou din motive de compatibilitate cu a a , compilatoarele care nu , eleg una din directive, dar o interpreteaz corect nt a pe cealalt. a O alt observatie important se refer la constructori. Practic exemplul a a a n , de mai sus, constructorii initializeaz membrii claselor prin atribuiri. Acest mod a , de a scrie codul seamn cu sintaxa din Java, dar nu functinoeaz mereu. a a a In , mod corect, C++, constructorii sunt niste functii speciale care se apeleaz n a , , automat nainte ca un obiect s a nceap efectiv s existe, deci trebuie invocati a a , corespunztor. Acest lucru se specic folosind operatorul : dupa antetul a a constructorului, urmat de o serie de constructii separate de virgul. Rescriind a , sierul de mai sus, obtinem: , ,
1 // Student.cpp 2 3 #include <cstring> // Pentru strdup() 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include "Student.h" // invocarea corect a constructorului pentru membrul name a Student::Student() : name(strdup("Name Unknown")) { } // // // // invocarea corect a constructorului pentru membrul name. a Compilatorul stie s fac diferenta a a ntre: , , * membrii clasei, pe care se apeleaz constructorii a * parametri, care pot s apar doar paranteze ca valori a a n

Student::Student(char * name) : name(strdup(name)) { } // din clasa Student, denim setMark(int, double) void Student::setMark(int subject, double newMark){ grade[subject] = newMark;

22 23 } 24

CLASE

13

Nu ultimul rnd, C++ v las s deniti o clas momentul declarrii n a a a a a n a , (ca Java). Mai mult, acest stil de denire devine obligatoriu pentru anun mite optimizri (functii inline, respectiv headere precompilate). Acest mod de a , denire poart tot numele de inline. Clasa de mai sus denit inline ar arta a a a astfel:
1 2 3 4 5 6 7 // Student.h #pragma once #ifndef #define STUDENT H STUDENT H

8 #include <cstring> 9 10 class Student{ 11 char * name; 12 double grade[3]; 13 public: 14 void setMark(int subject, double newMark) { 15 grade[subject] = newMark; 16 } 17 18 Student() : name(strdup("Name Unknown")) { } 19 20 }; 21 22 #endif Student(char * name) : name(strdup(name)) { }

ATENTIE! C++ nu puteti reapela alt constructor din interiorul unui In , constructor pentru un obiect (cum se ampla Java, cnd scriati this(0,0), nt n a , de exemplu). De asemenea, nu exist cuvntul cheie super, mecanismul de a a instantiere pentru clasele derivate ind explicat mai jos, la capitolul aferent. ,

CLASE

14

2.5

Ciclul de viat al obiectelor. Destructori. ,a

Ciclul de viat al obiectelor este probabil aspectul cel mai divergent al pro,a gramrii orientate pe obiecte, asa cum este ea exprimat C++, respectiv a a n , Java. Java exist mai multe tipuri de obiecte, unele cu scope explicit (de In a exemplu, scalarii: int, double sau referintele sine) si altele fr scope explicit, n aa , , care se creaz cu operatorul new si a cror spatiu ocupat este reciclat de a a , , ctre garbage collecter momentul care nu mai sunt referite din nici un a n n thread. ciuda aparentelor, C++ gestionarea memoriei este mult mai simpl In n a , conceptual (dezavantajul ind faptul c o mare parte din ea cade sarcina a n programatorului). C++, orice variabil este declarat In a a ntr-un scope. Exemple de scope-uri sunt: Spatiul unei functii/metode , , Spatiul global de nume , Interiorul blocurilor de program (cuprinse ntre { }) momentul care se iese din interiorul unui scope, trebuie eliberat stiva. In n a Pentru ecare obiect care este scos din stiv, C++ apeleaz automat o functie a a , special care se numeste destructor. Destructorul ofer obiectului sansa de a a a , , elibera resursele pe care le tinea ocupate. Ca si cazul constructorilor, dac nu a , , n denim nici un destructor, C++ genereaz din ociu un destructor vid pentru a clasele pe care le scriem. Cteva situatii care ar putea nevoie de destructori sunt: a n , Atunci cnd clasa contine resurse pe care le-a alocat dinamic si care trebuie a , , dezalocate. Atunci cnd clasa a deschis siere pe care trebuie s le a a nchid. a , Atunci cnd clasa a deschis socketi pe care trebuie s a a i nchid. a , Atunci cnd clasa detine lock-uri pe care trebuie s le elibereze (nu veti a a , , alni la materiile din anul 2). nt Destructorii se denesc asemntor cu constructorii, doar c numele lor trea a a buie precedat de caracterul tild si nu pot primi parametri! De asemenea, desi a , , limbajul permite, teorie nu este corect s apelati niciodat explicit destrucn a a , tori! Ei se apeleaza automat la iesirea din scop a obiectelor, sau la dezalocarea , memoriei alocate dinamic. Vom reveni din nou la exemplu pentru a explica mai bine conceptul:

CLASE

15

1 #include <iostream> 2 #include <cstring> 3 4 class Student{ 5 char * name; 6 double grade[3]; 7 public: 8 void setMark(int subject, double newMark) { 9 grade[subject] = newMark; 10 } 11 12 13 14 15 16 17 char * getName() { return name; } Student() : name(strdup("Name Unknown")) { } Student(char * name) : name(strdup(name)) { }

18 }; 19 20 void functie1(){ 21 // In declaratia de mai jos se apeleaza constructorul fara parametrii pentru a 22 // crea un obiect care este valabil incepand de aici pana la sfarsitul functiei. 23 Student student1; 24 25 std::cout << student1.getName() << "\n"; // Se aseaza Name Unknown 26 } 27 28 void functie2(Student student2){ 29 30 31 32 } 33 34 int 35 { 36 37 38 39 } // student2 este un obiect care se creaza in momentul apeului si este valabil // pana la sfarsitul functiei. std::cout << student2.getName() << "\n"; // Se aseaza Eric Cartman

main() functie1(); functie2(Student("Eric Carman")); return 0;

Pe parcursul executiei acestui program, se vor construi, pe rnd, dou obiecte a a , de tip Student. Fiecare instantiere se face cu apelarea constructorului pentru , clasa Student, iar constructorii aloca memorie care copie un sir de caractere n , care s reprezinte numele studentului. Dar ce se ampl cu aceast memorie? a nt a a Nu apare nicieri nici apel ctre free. realitate, aceast memorie nu se a a In a elibereaz odat cu obiectele (se distruge doar pointerul, nu si memoria pe care a a ,

CLASE

16

o pointeaz), ci produce leak. Pentru a rezolva aceast problem, vom declara a a a clas un destructor care s elibereze memoria atunci cnd obiectele ies din n a a a scop. Programul corect este:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdlib> 4 5 class Student{ 6 char * name; 7 double grade[3]; 8 public: 9 10 11 12 13 14 15 16 17 18 19 20 void setMark(int subject, double newMark) { grade[subject] = newMark; } char * getName() { return name; } Student() : name(strdup("Name Unknown")) { } Student(char * name) : name(strdup(name)) { } // Cand obiectul trebuie distrus, se executa codul din destructor!

21 Student() { free(name); } 22 }; 23 24 void functie1(){ 25 // In declaratia de mai jos se apeleaza constructorul fara parametrii. 26 Student student1; 27 std::cout << student1.getName() << "\n"; // Se aseaza Name Unknown 28 // AICI se apeleaza destructorul pentru student1. Variabila e locala functiei. 29 } 30 31 void functie2(Student student2){ 32 33 34 35 } 36 37 int 38 { 39 40 41 42 } // student2 este un obiect care se creaza in momentul apeului. std::cout << student2.getName() << "\n"; // Se aseaza Eric Cartman // AICI se apeleaza destructorul pentru student2. Parametrul e local functiei.

main() functie1(); functie2(Student("Eric Carman")); return 0;

CLASE

17

2.6

Membrii statici

C++, membrii statici ai claselor sunt resurse pe care le au comun toate In n obiectele clasei. La fel ca Java, ei se declara folosind cuvntul cheie static n a n fata membrului ce trebuie declarat (NU denit), si functioneaz asemntor, a a a , , , dar exist cteva deosebiri. a a Variabilele membre statice terbuie denite sierele de implementare. n , Acest lucru este necesar deoarece prin simpla declarare, lor nu li se aloca efectiv memorie segmentul de date! n Spre deosebire de Java, nu este posibil crearea unor blocuri statice de a initializare. Cel mai bine este s considerati c la a a nceputul executiei pro, , , gramului, variabilele statice au valori nedenite (sau, anumite compilan toare, initializate cu 0). Variabilele statice de tip clas sunt construite a automat folosind un constructor fr parametri, dac nu se specic altfel aa a a cod. n Functiile statice, dac au permisiunile corecte, pot folosite ca orice alte a , functii globale, si se pot deduce pointeri din ele astfel at s poat nc a a , , date ca parametri locul functiilor globale. Pentru functiile membre n , , nonstatice, acest lucru NU este valabil din cauza ncapsulrii. Aceast a a problem nu aprea Java oricum, deoarece acolo nu putem avea pointeri a a n la functii. , Pentru a apela o functie static, trebuie s specicm si spatiul de nume a a i a , , , (prexnd apelul cu numele clasei, urmat de operatorul ::). a Urmtorul exemplu ilustreaz folosirea de membrii statici. a a
1 #include <iostream> 2 #include <cstring> 3 #include <cstdlib> 4 5 class Student{ 6 char * name; 7 8 // Declaram un obiect static 9 static int instanceCount; 10 public: 11 // Declaram o functie statica 12 static void printInstanceCount() { 13 std::cout << instanceCount << "\n"; 14 15 16 17 18 19 } Student() : name(strdup("Name Unknown")) { instanceCount++; }

CLASE

18

20 21 22 23 24

Student(char * name) : name(strdup(name)) { instanceCount++; } Student() {

25 free(name); 26 instanceCount--; 27 } 28 }; 29 30 // DEFINIM zic obiectul instanceCount (i se va aloca memorie) 31 32 33 34 35 36 // si il initializam explicit cu 0 la lansarea in executie int Student::instanceCount = 0; void functie() { Student s2;

37 Student::printInstanceCount(); // Se aseaza 2: s1 si s2 38 // AICI se apeleaza destructorul lui s2, care decrementeaza instanceCount 39 } 40 41 int main() 42 { 43 44 45 46 47 48 49 } 50 Student s1; Student::printInstanceCount(); // Se aseaza 1: s1 functie(); Student::printInstanceCount(); // Se aseaza 1: s1 return 0; // AICI se apeleaza destructorul lui s1, care decrementeaza instanceCount

CLASE

19

2.7

Derivarea claselor

Printre cele mai importante avantaje ale programrii orientate pe obiecte a l reprezint mecanismul de mostenire. Din nou, acesta este un capitol la care a , exist diferente notabile a ntre C++ si Java. , Paradigma impus de Java este aceea c Totul este un obiect. Prin ura a mare, absolut orice clas pe care am scrie-o deriv (sau folosind terminologia a a Java, extinde) din clasa Object. Mai mult, Java o clas nu poate extinde n a dect o singur alt clas. Cu alte cuvinte, dac avem o clas care extinde a a a a a a java.util.Vector, atunci ea nu poate extinde nici o alt clas. Din cauza c asta a a a ar limitat limbajul foarte tare, cei care au proiectat Java au permis doar un singur tip de mostenire multipl: mostenirea de interfete. Diferenta este c o a a , , , , interfat cuprinde doar functii si de aceea nu pot aprea conicte de instantiere a ,a , , , multipl a membrilor de date. a Pe de alt parte, C++ nu exist nici o restrictie din acest punct de vedere, a n a , iar lucrurile stau mult mai complicat (desi este extrem de improbabil s aveti a , , de-a face cu altceva dect derivare de tip direct si public). a , Pentru a deriva o clas, este sucient s , iruiti clasele pe care aceasta a a ns , le extinde dup numele clasei, cu virgul a a ntre ele. Numele claselor de baz a (printe) trebuie precedat cu tipul de derivare. acest sens, derivarea public a In a este aproape universal. a O exceptie notabil este c C++, nu exist o clas de baz din care s a a n a a a a , derive toate celelalte clase (echivalent cu Object din Java)! Unul dintre moa tivele pentru care Java exist clasa Object este tratarea uniform a tipurilor, n a a dar C++ exist pentru acest scop pointerii generici (void*). n a Un exemplu de sintaxa prin care se specica derivarea de clase regsim a n continuare:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdlib> 4 5 class Om { 6 protected: 7 char * nume; 8 public: 9 Om(const char * nume) : nume(strdup(nume)) { } 10 virtual Om() { free(nume); } 11 }; 12 13 class Student : public Om { 14 double nota;

CLASE

20

15 public: 16 Student(const char * nume, double nota) : Om(nume), nota(nota) { } 17 void display() { 18 std::cout << "(" << nume << "," << nota << ")\n"; 19 } 20 }; 21 22 int main() 23 { 24 Student s("Eric Cartman", 10.0f); 25 s.display(); 26 27 } return 0;

exemplul de mai sus, exist trei elemente de sintax care trebuie explicate: In a a Apelarea constructorului clasei de baza in constructorul clasei Student, pe linia 16. Un Student este, primul rnd, un caz particular de Om, n a deci nainte ca obiectul Student s poat explicit construit, trebuie s a a a apelm constructorul clasei de baz folosind sintaxa specic constructoa a a rilor. Acest lucru este echivalent cu apelurile ctre apelurile la constructori a de tipul super() din Java. Se remarc de asemenea c a trebuit s declarm numele ca ind protected, a a a a pentru ca functia display() din clasa Student s aib acces la el. a a , Nu ultimul rnd, destructorul din clasa de baz este declarat ca ind n a a virtual. acest caz, acel cuvnt cheie nu produce diferente, dar cazul In a n , general este extrem de recomandat ca destructorii claselor s e declarati a , ca virtuali. Vom discuta acest lucru la virtualizare. Atunci cnd o clas deriveaz alt clas, aceasta mosteneste din clasa de a a a a a , , baz toti membrii, mai putin membrii statici. Trebuie de tinut minte c mema , a , , brii statici nu particip la mecanismul de mostenire, deoarece acest lucru nu a , are sens (ei sunt partajati doar ntre obiectele unei anumite clase, dar nu se , pot replica pentru clasele derivate). Setarea drepturilor de acces astfel at nc clasele derivate s aib acces la ei creaz uneori o fals impresie de mostenire, a a a a , dar aceasta este o greseal de exprimare des alnit la programatori. a nt a , Un alt lucru care trebuie retinut este c C++, supradenirea unei metode a n , dintr-o clas de baz duce cu sine la pierderea tuturor metodelor cu acelasi a a , nume mostenite din clasa de baz, indiferent de semntura parametrilor. Metodele a a , din clasele de baz se pot apela prin specicarea namespace-ului corect (numele a clasei, urmat de operatorul ::). Ca si curiozitate, ar trebui mentionat c C++ derivarea poate s e si de a n a , , , tip protected/private (sunt rare situatiile cnd asa ceva este cu adevrat necesar a a , , cod), dar si virtual (pentru solutionarea problemei mostenirii romb). n a n , , ,

CLASE

21

2.8

Polimorsm. Functii virtuale ,

Polimorsmul C++ este la n nceput o surs continu de confuzie pentru proa a gramatorii Java. Java, un singur comportament este posibil: toate functiile In , sunt implicit polimorce. Cu alte cuvinte, dac o clas deriveaz o clas de baz a a a a a si suradeneste o metod, atunci toate situatiile care folosim o referint la a n n , , , ,a obiecte derivate, se va apela noua metod. a realitate, acest comportament impune s tinem evidenta tipului real al tuIn a, , turor obiectelor instantiate, si , , ncetineste foarte mult toate apelurile de functie , , din nevoie ntretinerii unei tabele de virtualizare. , Prin urmare, C++ virtualizarea nu se face automat. Ea trebuie specicat n a mod explicit cu ajutorul cuvntului cheie virtual declaratie (NU denitie). n a n , , Odat denit ca ind virtual o metod dintr-o clas rmne virtual si a a a a a a a a , n clasele derivate, chiar dac nu mai specicm acest lucru noile declaratii. a a n , Cel mai clar este s folosim un exemplu care s justice efectele lipsei virtua a alizrii: a
1 #include <iostream> 2 #include <cstring> 3 #include <cstdlib> 4 5 class Animal { 6 protected: 7 char * name; 8 public: 9 void makeSound() { 10 std::cout << "No sound\n"; 11 } 12 Animal(const char * name) : name(strdup(name)) { } 13 virtual Animal() { free(name); } 14 }; 15 16 class Bird : public Animal { 17 public: 18 void makeSound() { 19 std::cout << "Cra-Cra\n"; 20 } 21 Bird(const char * name) : Animal(name) { } 22 }; 23 24 class Dog : public Animal { 25 public: 26 void makeSound() { 27 std::cout << "Whoof-Whoof\n";

CLASE

22

28 } 29 Dog(const char * name) : Animal(name) { } 30 }; 31 32 void forceToSpeak(Animal * animal){ 33 34 35 36 37 } 38 // Se va asa mereu No sound, deoarece compilatorul nu poate decide // tipul real al obiectului la care se refera pointerul animal daca nu // specicam noi sa tina evidenta printr-o tabela de virtualizare animal->makeSound();

39 int main() 40 { 41 Bird bird("Tweetie"); 42 Dog dog("Pluto"); 43 std::cout << "// Apeluri directe: \n"; 44 bird.makeSound(); 45 46 47 48 49 50 } 51 dog.makeSound(); std::cout << "// Apeluri prin pointeri: \n"; forceToSpeak(&bird); forceToSpeak(&dog); return 0;

exemplul de mai sus, putem forta compilatorul s tin evidenta tipului In a , a , , real folosind cuvntul cheie virtual fata metodei makeSound(). De asemea n , nea, din moment ce nu vom avea niciodat o instat de Animal program, a n ,a dup ce facem metoda makeSound virtual, putem s scoatem implementarea a a a i a aa default si s o facem pur virtual. O metod pur virtual este o metod fr a a a a , implementare, ceea ce face clasa respectiv s e abstract. Exact ca si a a a n , Java, o clas abstract nu se poate instatia. Putem face o metod s e pur a a a a , virtual specicnd cadrul declaratiei c nu i se va atasa nici un pointer de a a n a , , functie (este o functie cu pointerul NULL). Astfel, este sucient s modicm a a , , urmtoarele clasa Animal: a n
1 class Animal { 2 protected: 3 char * name; 4 public: 5 // Am declarat metoda makeSound ca ind virtuala si am specicat 6 // ca aceasta clasa nu o poate implementa folosind = 0; 7 8 9 10 11 }; // Acest lucru face din Animal o clasa abstracta. virtual void makeSound() = 0; Animal(const char * name) : name(strdup(name)) { } virtual Animal() { free(name); }

CLASE

23

Este recomandat ca destructorii claselor s e mereu declarati ca virtua , ali, deoarece altfel este posibil ca atunci cnd vrem s distrugem un obiect a a referentiat de un pointer s nu se apeleze destructorul corect, ci destructorul a , unui printe al clasei, producnd astfel comportamente neasteptate program. a a n ,

CLASE

24

2.9

Supra arcarea operatorilor nc

Un motiv pentru care C++ este mai expresiv si produce cod mai clar dect a , Java reprezint posibilitatea de a supra arca operatori. Supra arcarea l a nc nc operatorilor se refer la interpretarea semnicatiei lor functie de tipurile a n , , operanzilor. Aceast facilitate este foarte puternic C++, permitnd ima a n ,a plementarea de ctre utilizatori a grupurilor algebrice direct limbaj! Desi a n , supra arcarea operatorilor nu necesit mecanisme de parsare foarte complinc a cate, ea nu a fost implementat mai departe Java din motive de claritate a n a codului nal. Supra arcarea operatorilor exista totusi intr-o form rudinc a , mentar si C, unde operatorul plus, de exemplu, are semnicatie diferit a , n a n , functie de tipul operanzilor: , (int)(1)+(int)(2) == (int)(3), pe cnd a (int*)1+(int)2 == (int*)(1+sizeof(int)*2) C++, se poate supra arca oricare dintre operatorii existenti ai limbaIn nc , jului: unari ( , !, ++, etc.) sau binari (+, -, +=, -=, ==, <, etc.). Prin supra arcare, vom deni o functie care s e invocat automat atunci cnd nc a a a , alnim la stnga si [eventual] la dreapta tipurile de date corespunztoare. Din nt a a , motive de consistent a limbajului, nu se pot redeni operatori care exist deja a ,a bine deniti, de exemplu operatorul + ntre dou numere a ntregi va nsemna , mereu numai adunare. Ceea ce putem redeni a sunt operatorii pe tipuri de date denite de ns utilizator. Cel mai des supra arcati operatori sunt operatorii mai mic si nc , , atribuire. Trebuie retinut c supra arcarea operatorilor nu afecteaz pria nc a , orittile lor. Atunci cnd dorim o ordine anume de aplicare a operatiilor, este ina, a , dicat s folosim paranteze pentru sigurant. O pereche de paranteze se tasteaz a a ,a sub o secund, dar lipsa lor poate provoca ore n a ntregi de debugging. Operatorii C++ seamn din punct de vedere sintactic (si semantic) cu n a a , niste functii. Ei pot deniti la nivel global, sau pot ncapsulati clase. n , , , , Cu toate acestea, a doua practic nu este a ncurajat deoarece ascunde niste a , probleme de implementare (deoarece operandul din stnga este implicit obieca tul referit de this). Pentru a exemplica sintaxa prin care se pot supra arca operatorii C++, nc n ne vom folosi de un exemplu matematic foarte simplu: vom deni o clas Coma plex care s retin numere complexe si vom implementa operatiile cu numere a , a , , complexe folosind operatorii nativi din limbaj, loc de functii. n ,
1 #include <iostream> 2 3 class Complex{ 4 public: 5 // Membrii de date

CLASE

25

6 7 8 9 10 };

double real, imag; // Constructori Complex(double real, double imag) : real(real), imag(imag) { } Complex() : real(0), imag(0) { }

11 12 // Spunem ce executa + intre doua obiecte Complex 13 Complex operator+ (Complex& left, Complex& right){ 14 return Complex(left.real+right.real, left.imag+right.imag); 15 } 16 17 18 19 20 21 22 // Spunem ce executa + intre un obiect Complex si un obiect int Complex operator+ (Complex& left, int right){ return Complex(left.real+right, left.imag); } // Spunem ce executa += intre doua obiecte Complex

23 Complex& operator+= (Complex& left, Complex& right){ 24 left.real += right.real; 25 left.imag += right.imag; 26 return left; 27 } 28 29 // Spunem ce executa << intre un obiect ostream si un obiect Complex 30 std::ostream& operator<< (std::ostream& out, Complex right){ 31 out << "(" << right.real << "," << right.imag << ")"; 32 return out; 33 } 34 35 int main() 36 { 37 Complex a(1,1), b(-1,2); 38 std::cout << "A: " << a << "\nB: " << b; 39 std::cout << "\nA+B: " << (a+b) << "\n"; 40 } 41

Atunci cnd scriem operatori este bine s a a ncercm s privim mai degrab a a i a ca pe niste aplicatii matematice pentru a nu gresi. Se observ din exemplul de a , , , mai sus c operatorii += si << a ntorc referinte ctre obiectul din stnga. V a a a , , veti , ntreba de ce acest lucru nu este redundant. Rspunsul se a modul care compilatorul interpreteaz codul. Noi a a n n a scriem std::cout << a << b; si ne-am astepta ca programul s execute pe rnd, a a , , codul de asare pentru obiectul a si apoi pentru obiectul b pe ecran. realitate In , , a, ceea ce se execut arat mai degrab felul urmtor (tinnd cont c orns a a a n a a a ,

CLASE

26

dinea de evaluare este de la stnga la dreapta): operator<<( operator<<(std::cout, a a), b). Dac operatorul << nu ar returnat mai departe referinta ctre a a , obiectul de tip ostream care se face asarea, atunci nu am putut n mbrica , apelurile ctre operatori, sau cu alte cuvinte nu am putut antui operaa nl , torii. antuirea este mai ales relevant atunci cnd scriem operatori pentru Inl , a a expresii matematice, deoarece este un caz particular destul de rar ca o expresie matematic s contin un singur operator. De altfel, atunci cnd s-au studiat a a a a , operatori la algebr, acesti operatori erau de fapt functii care luau valori a n , , multimea peste care era denit algebra. a , Dintre toti operatorii, doi sunt mai des supra arcati si pun probleme mai nc , , , serioase programatorilor: Operatorul =, numit si assigment operator , Operatorul < Operatorul de atribuire se sintetizeaz din ociu de ctre compilator dac a a a nu supra arcati voi explicit. Operatorul din ociu pur si simplu realizeaz l nc , a , atriburiea ntre toti membrii din clas (proces numit shallow copy). Acest a , lucru devine problematic atunci cnd aveti pointeri clase, deoarece riscati a n , , memory leak-uri la ecare atribuire. acest caz, va trebui s implementati In a , voi explicit atribuirea. Un amnunt destul de important este de asemenea c a a operatorul de atribuire nu trebuie confundat cu copy constructor-ul unei clase, chiar dac se pot nota amndoi cu simbolul =. Vom discuta despre copy cona a structor la momentul potrivit. De asemenea, sunt situatii care nu vi se poate n , sintetiza operatorul de atribuire pentru o clas, deocare clasa contine referinte a , , ca membrii (iar referintelor C++ nu li se pot atribui valori dup instantiere). n a , , Operatorul < este important mai ales pentru c el este cerut implicit de a anumite containere din biblioteca standard de template-uri. El are rol similar cu functia din interfata Comparable din Java, doar c C++ vericarea a n , , existentei operatorilor corecti se face abia la linking. Cu alte cuvinte, dac a , , folositi o structur de date sortat (de exemplu, un map care cheile sunt a a n , obiecte denite de voi), va trebui mod obligatoriu s deniti operatorul < n a , pentru chei, altfel nu veti putea compila codul. Pentru a respecta semntura a , impus de containerele din C++, este de asemenea necesar ca operatorul < s a a primeasc argumente de tip const, pentru c altfel nu se garanteaz corectia a a tudinea algoritmului de sortare. Lipsa specicrii parametrilor de tip const a va duce la erori de compilare. Un alt operator interesant este operatorul functional (), care permite obiectelor , s e apelate ca functii. Aceste obiecte poart numele de functori si nu au nici a a , , un concept echivalent Java. Functorii pot face anumite programe greu de n , eles, dar sunt interesanti din punct de vedere al logicii limbajului, deoarece nt ,

CLASE

27

sterg practic granita ntre clase si functii. , , , , nal, ar trebui amintit c o serie dintre coding guide-urile consacrate In a (de exemplu, cel al Google sau cel al Mozilla) sftuiesc vehement a mpotriva supra arcrii operatorilor limbajului, doarece semnicatia acestora poate denc a , veni ambigu foarte repede. Exceptie fac principiu operatorii de atribuire si a n , , cei logici (declarati cu cuvntul cheie const imediat dup lista de argumente. a a , Aceast sintax restrictioneaz implementarea s nu produc efecte laterale (un a a a a a , concept despre veti mai auzi mai ales la programarea functional). V veti a a , , , convinge repede c abuzul de facilittile C++ poate conduce dac nu sunteti a a, a , atenti la cod imposibil de ntretinut. , ,

INPUT/OUTPUT

28

3
3.1

Input/Output
Fisiere si stream-uri , ,

C, metoda cea mai folosit de I/O era folosirea de siere. Pentru a simplica In a , lucrul cu siere, C-ul denea o structur wrapper peste descriptorii de sier a , , pe care s o folosim (structura FILE). C++, a, s-a dorit abstractizarea a In ns lucrului cu resursele de I/O. Astfel, desi se pot continuare folosi functiile si n , , , structurile din C, C++ pune la dispozitie stream-uri. , Stream-urile din C++ seamn conceptual (si de fapt, constituie sursa de a a , inspiratie) pentru cele din Java, dar exist cteva diferente care le separ. a a a , , Stream-urile din biblioteca standard deriv toate e din clasa ostream a (pentru uxuri de iesire), e din clasa istream (pentru uxuri de intrare). , Este posibil lucrul cu siere instantiind obiecte e din clasa ofstream , , (pentru siere de iesire), e din clasa ifstream (pentru siere de intrare). , , , Desi operatiile din C++ pot arunca exceptii, veti observa c spre deosebire a , , , , de Java, C++ nu v oblig s tratati exceptiile. Acest lucru poate si a a a , , , bun si ru. Dac alegeti s nu folositi exceptiile de la streamuri, ele vor a a a , , , , ignorate si codul va continua s se execute (eventual) defectuos. Pentru a , compatibilitate, C++ seteaz si o serie de ag-uri care sunt mostenite a , , pentru toate stream-urile din clasa printe comun ios. Cele mai folosite a a a n ag-uri sunt: eof, fail si bad. Semnicatiile lor variaz functie de tipul , , , de stream folosit si este indicat s vericati documentatia, mai ales la a , , , nceput. Pentru cele trei siere clasice care sunt deschise automat pentru orice pro, gram care se execut, C++ deneste trei stream-uri cu nume consacrate: a , std::cin - stream-ul denit peste stdin std::cout - stream-ul denit peste stdout std::cerr - stream-ul denit peste stderr Implicit, toate operatiile cu stream-uri C++ sunt buer-ate (acest lun , cru se poate dezactiva cu ajutorul unei functii handler). Din acest motiv, este , a foarte gresit s folositi simultan pentru asare sau citire si stream-uri si siere, , , , , , , deoarece vi se vor aleca rezultatele si efectul nal nu va cel dorit. nc , Asarea si citirea n/din stream-uri se face mod conventional cu operatorii n , , , << pentru scriere si >> pentru citire. ,

INPUT/OUTPUT

29

Nu ultimul rnd, trebuie retinut c obiectele de tip stream sunt fcute n a a a , mod intentionat neasignabile C++ (a fost omis intentionat denirea opn n a , , eratorului de atribuire). Motivul pentru care nu se pot atribui stream-uri este riscul duplicrii buerului. Exist metode prin care puteti manipula buerele, a a , dar este improbabil s aveti cu adevrat de lucrat cu asa ceva (v-ar putea folosi a a , , doar la redirectionri fortate, dar puteti rezolva altfel, folosind pointeri). a , , , urmtorul exemplu de cod, deschidem dou siere, unul de intrare si unul In a a , , de iesire, si efectum cteva operatii elementare de I/O. a a , , ,
1 #include <iostream> 2 #include <fstream> 3 4 int main() 5 { 6 // Instantiem si deschidem simultan doua stream-uri: 7 // * in, pentru citire 8 // * out, pentru scriere 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 } std::ifstream in("input.txt"); std::ofstream out("output.txt"); // Vericam daca s-a deschis out cu succes if (out.fail()) { std::cerr << "ERROR: Nu s-a putut deschide fisierul \"output.txt\"\n"; return 0; } int a, b; // Citim doi intregi din in in >> a >> b; // Inchidem in in.close(); // Atasam lui in alt sier de intrare // (pentru siere binare, folosim ifstream::bin) in.open("other input.txt", std::ifstream::in); // Scriem a si b in out, cu niste text suplimentar out << "A este: " << a << " iar B este: " << b << "\n"; // Inchidem toate sierele in.close(); out.close(); return 0;

INPUT/OUTPUT

30

38 39

Un ultim aspect care ar trebui tratat aici si de care s-ar putea s aveti nevoie a , , la proiect este serializarea. C++, mecanismul de serializare/deserializare nu In se face automat ca Java, deci va trebui s folositi stream-uri binare si s n a a , , supra arcati voi operatorii <<, respectiv >> conform cu protocolul pe care nc , vreti s implementati. a l , ,

SIRURI DE CARACTERE ,

31

4
4.1

Siruri de caractere ,
Clasele std::string si std::stringstream ,

C++ pune la dispozitie o clas nativ pentru lucrul cu siruri de caractere, si a a , , , anume clasa string. Aceast clas exist, desigur si Java, unde functioneaz a a a a , n , aproximativ similar. Pentru clasa string sunt supra arcati ctiva operatori nc , a mai intuitivi: Indexare: [] - permite accesul la caracterul de pe o anumit pozitie a , Adunare: += - permite adugarea unui sir/caracter la sfrsitul unui string a a , , Egalitate: == - functioneaz similar cu functia strcmp() a , , Atribuire: = - permite copierea string-urilor Avantajul evident la string-uri este faptul c acestea pot folosite mult a mai intuitiv dect sirurile de caractere, si nu mai este nevoie s tinem cont de a , a , , terminatorii de sir sau de problemele de realocare de memorie, deoarece aces, tea sunt tratate automat interiorul claselor. De asemenea, folosind operatorii n supra arcati, codul arat mai aerisit si mai intuitiv, ceea ce face implementarea nc , a , mai rapid si mai elegant. a , a Spre deosebire de Java a, C++ nu are suport nativ pentru caracterele Unins code sau UTF-8. Toate caracterele sunt retinut mod ASCII. Exist separat n a , tipul de date w char pentru tipuri de caractere wide (pe doi Bytes), iar biblioteca standard pune la dispozitie inclusiv macro-uri pentru cast-ul implicit, , dar greseli se pot face frecvent. Mai mult, o serie de clase template accept a , alocatori ca parametru constructori, ceea ce n nseamn c de fapt ele pot a a functiona cu orice tip de caractere am vrea noi s denim dac scriem codul a a , necesar manipulrii tipului de date. Ca si alte situatii, C++ are un pret de a n , , , pltit pentru libertatea de alegere pe care o ofer programatorului: mai mult a a a btaie de cap! a Cu toate acestea, este extrem de improbabil s aveti nevoie de caractere a , Unicode aplicatiile de algoritmic, deci nu trebuie s v faceti griji cu privire n a a a , , la encoding, ci puteti s contati pe faptul c este mereu ASCII. a a , , Clasa stringstream este folosit C++ pentru a stoca un buer de caraca n tere. De obicei ea este util atunci cnd vrem s generm stringuri prin asri a a a a ,a repetate din diferite obiecte. Java, puteam face conversia automat folosind o In constructie de tipul (+object), dar C++ acest cod nu are efectul asteptat. n , , Pentru serializare trebuie s folosim operatorul << pentru a printa informatiile a , ntr-un stringstream, si apoi putem folosi functia membru str() fr parametrii aa , , pentru a recupera sirul generat din stream. Dac trimitem un parametru a n , membrul str(), acesta are efectul de a nlocui stringul din stream.

SIRURI DE CARACTERE , continuare avem un exemplu orientativ de folosire al acestor clase: In


1 2 3 4 5 #include #include #include #include <iostream> <string> // Clasa string <cstring> // Functiile pe stringuri din C <sstream>

32

6 int main() 7 { 8 // Instantiem un string: 9 std::string s("Ana are mere"); 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 } // Adaugam la sfarsit un sux: s += ". Eric Cartman "; // Daca caracterul de pe pozitia 3 este spatiu, asam lungimea sirului si sirul if (s[3] == ) { std::cout << "Lungimea " << s.length() << ": " << s << "\n"; } // Obtinem continutul din s sub forma de string stil C n // c str() intoarce un pointer la o zona statica de memorie, deci daca vrem sa // pastram string-ul, trebuie sa folosim strdup() ca sa il clonam char * ps = strdup(s.c str()); char * toFree = ps; // Cream un stream dintr-un string std::stringstream ss; // Impartim ps in cuvinte si le scriem in ss, separate prin underscore ps = strtok(ps," "); while (ps){ ss << ps << " "; ps = strtok(NULL, " "); } // Asam continutul din ss pe ecran std::string rep = ss.str(); std::cout << "Lungimea " << rep.length() << ": " << rep << "\n"; // Distrugem ps; delete[] toFree; return 0;

GESTIUNEA MEMORIEI

33

5
5.1

Gestiunea memoriei
Alocarea memoriei: new vs. malloc

C, alocarea memoriei se fcea mod obisnuit prin intermediul functiei malIn a n , , loc (sau alternativ se puteau folosi si calloc sau pentru siruri de caractere, , , strdup). Desi functiile din bibliotecile de C sunt pstrate aproape integral a n , , C++, folosirea lui malloc nu este sucient pentru alocarea de noi obiecte, a n acest scop denindu-se operatorul new (care a fost implementat si Java mai , n apoi). Atunci cnd folosim operatorul new, se execut spate dou lucruri disa a n a tincte: Se aloc memorie sucient pentru a stoca obiectele cerute, conform cu a a spatiul ocupat de clas a , Se invoc automat constructorii pentru obiectele nou create a Valoarea returnat de operator este un pointer ctre zona de memorie care a a contine obiectul sau obiectele nou alocate. , Deja ar trebui s e evident motivul pentru care un simplu apel la malloc a nu este sucient pentru a crea un obiect dintr-o clas: chiar dac se aloc mema a a orie sucient, problema este c nu este invocat constructorul pentru obiectele a a respective. Dac vom lua drept exemplu un obiect de tipul std::vector pe care a ncercm s alocm dinamic cu malloc, realitate noi nu facem dect s reza a l a n a a ervm memorie pentru un obiect de tipul std::vector, dar acel spatiu nu se va a n , construi efectiv un astfel de obiect, ci vom avea valori neinitializate. Incercarea , ulterioar de a insera un element acea colectie va esua aproape sigur, din a n , , diferite motive posibile: pointerii interni clasei nu au la rndul lor memorie alocat, deoarece reza a ervarea de memorie s-ar fcut constructor a n variabilele care tin de starea intern a obiectului nu au valori consisa , tente (de exemplu, capacitatea maxim rezervat, numrul de elemente a a a din colectie, etc.) , a a Singura metod corect de a aloca dinamic un obiect sau un array de tip structural este prin intermediul operatorului new. Se pune atunci, evident, ntrebarea de ce au mai fost pstrate functiile de alocare de memorie din C. a , Rspunsul nu tine doar de compatibilitate. Functiile din C de alocare de mema , , orie se pot folosi continuare pentru alocri de buere binare sau de obiecte n a care nu au constructori expliciti (acolo unde nu exist oricum nevoie de a exea , cuta cod momentul instantierii). Asa cum v-ati obisnuit deja, limbajul v d n a a , , , ,

GESTIUNEA MEMORIEI

34

libertatea s folositi ce vreti si nu v constrnge nici un fel, dar scrieti totul a a a n , , , , pe propria rspundere. a continuare am dat ca exemplu cteva exemple de folosire a operatorului In a new.
1 #include <iostream> 2 3 class Complex{ 4 public: 5 double real, imag; 6 Complex(double real, double imag) : real(real), imag(imag) { } 7 Complex() : real(0), imag(0) { } 8 }; 9 10 std::ostream& operator<< (std::ostream& out, Complex& right){ 11 return out << "(" << right.real << "," << right.imag << ")\n"; 12 } 13 14 int main() 15 { 16 // Alocam un intreg dinamic si citim o valoare de la tastatur a 17 int * n = new int; 18 std::cin >> (*n); 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 } // Cream un vector de n numere complexe // Pentru toate elementele din array vom invoca constructorul // fara parametri! Complex* v = new Complex[(*n)]; // Citim n numere Complexe si le asam apoi int real, imag; for (int i = 0; i < (*n); i++){ std::cin >> real >> imag; v[i] = Complex(real,imag); } for (int i = 0; i < (*n); i++){ std::cout << v[i]; } // Nu dezalocam memoria inca, vom trata acest lucru in capitolul urmator. return 0;

GESTIUNEA MEMORIEI

35

5.2

Dezalocarea memoriei: free vs. delete

Dezalocarea memoriei alocate dinamic C se fcea prin intermediul functiei n a , free. Asa cum deja intuiti, simplul apel ctre free nu este sucient pentru a a , , dezaloca memoria C++, din motive similare cu motivele pe care le-am citat n la alocarea de memorie. Pentru a dezaloca memorie, C++ exist operatorul n a delete, care primeste ca operand un pointer catre o zon de memorie alocat a a , dinamic. Diferenta dintre free si delete este aceea c dezalocarea unui obiect cu free a , , nu face dect s elibereze spatiul ocupat de obiectul sine si att, timp ce a a n a n , , dezalocarea cu delete invoc mod automat si destructorul obiectului. Acest a n , lucru este relevant, de exemplu, dac interiorul clasei exist pointeri la mema n a orie alocat dinamic care trebuie de asemenea distrus si care ar leak-uit dac a a, a foloseam free si nu se apela destructorul. , Din aceleasi ratiuni ca si sectiunea precedent, free nu este nicidecum o a , , , n , functie obsolete, ci are aplicativitate alte aspecte ale limbajului. Ca regul n a , general: a tot ceea ce este alocat cu new trebuie dezalocat cu delete tot ceea ce este alocat cu malloc (si alte functii similare din C) trebuie , , dezalocat cu free De remarcat aici c atunci cnd vrem s dezalocm un array si nu un singur a a a a , obiect, sintaxa corect este delete[]. continuare, adugnd codul pentru a In a a dezalocarea de memorie la exemplul de la punctul precedent, se obtine: ,
1 #include <iostream> 2 3 class Complex{ 4 public: 5 double real, imag; 6 Complex(double real, double imag) : real(real), imag(imag) { } 7 Complex() : real(0), imag(0) { } 8 }; 9 10 std::ostream& operator<< (std::ostream& out, Complex& right){ 11 return out << "(" << right.real << "," << right.imag << ")\n"; 12 } 13 14 int main() 15 { 16 int * n = new int; 17 std::cin >> (*n); 18 Complex* v = new Complex[(*n)]; 19

GESTIUNEA MEMORIEI

36

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 }

int real, imag; for (int i = 0; i < (*n); i++){ std::cin >> real >> imag; v[i] = Complex(real,imag); } for (int i = 0; i < (*n); i++){ std::cout << v[i]; } // Asa se dezaloca un singur obiect (avem nevoie de pointerul sau) delete n; // Asa se dezaloca un array (avem nevoie de pointerul primului element) delete[] v; return 0;

STANDARD TEMPLATE LIBRARY

37

6
6.1

Standard Template Library


Introducere: de ce STL?

Standard Template Library (sau pe scurt, STL) este o bibliotec generic a a de C++, partial inclus C++ Standard Library. Ea reprezint una din a n a , modalittile de a folosi cod gata implementat, si contine implementri de templatea, a , , uri pentru o serie de structuri de date si algoritmi mai uzuali. Buna stpnire a a , a notiunilor legate de STL v va ajuta s dezvoltati rapid implementri la fel a a a , , de scurte si mult mai eciente dect cele din Java. a , Asta face efortul atrii folosirii STL s par mic comparatie cu avannv , a a a n , tajele obtinute. Pentru nceput, veti observa c puteti citi valori, sorta vectori, a , , , si alte operatii de rutin a ntr-un mod mult mai sigur si mai ecient, scriind , , , un singur rnd de cod, iar timp veti descoperi cum operatii mai complexe a n , , precum folosirea de cozi ordonate sau cutarea arbori binari pot fcute la a n a fel de usor si sigur. , , Pentru a sprijini armatiile fcute mai sus, vom porni de la un exemplu a , simplu: un program care citeste valorile unui vector, le sorteaz si le aseaz pe a, a , , ecran. Prima variant este o implmentare clasic de C. a a
1 #include <stdlib.h> 2 3 4 5 6 #include <iostream.h> // a and b point to integers. cmp returns -1 if a is less than b, // 0 if they are equal, and 1 if a is greater than b. inline int cmp (const void *a, const void *b) int aa = *(int *)a; int bb = *(int *)b; return (aa < bb) ? -1 : (aa > bb) ? 1 : 0;

7 { 8 9 10 11 } 12 13 14 15 16 17 18 19 20 21 22 23 24 25

// Read a list of integers from stdin // Sort (c library qsort) // Print the list main (int argc, char *argv[]) { const int size = 1000; // array of 1000 integers int array [size]; int n = 0; // read an integer into the n+1 th element of array while (cin >> array[n++]); n--; // it got incremented once too many times

STANDARD TEMPLATE LIBRARY

38

26 27 28 29 30 31 }

qsort (array, n, sizeof(int), cmp); for (int i = 0; i < n; i++) cout << array[i] << "\n";

A doua variant foloseste STL. Nu v faceti griji dac nu , elegeti a cum a a a nt , , , nc functinoeaz, mai multe detalii se vor regsi sectiunile care urmeaz: a a n a , ,
1 #include <algorithm> 2 #include <vector> 3 4 5 6 7 8 #include <iostream> #include <iterator> using namespace std; int main () vector<int> v; istream iterator<int> start (cin); istream iterator<int> end; back insert iterator< vector<int> > dest(v); copy (start, end, dest); sort (v.begin(), v.end()); copy (v.begin(), v.end(), ostream iterator<int>(cout, "\n")); return 0;

9 { 10 11 12 13 14 15 16 17 18 19 }

6.2

Template-uri

Inainte de a continua, as dori s precizez c tot codul din STL se compune din a a , clase template. Din acest motiv, pare resc s ne punem a ntrebarea Ce este un template?, chiar dac o parte dintre programatorii a nceptori le folosesc a corect fr s constientizeze cu adevrat ce sunt si cum functioneaz. aa a a a , , , Un template este esent solutia pe care o aduce C++ pentru progran ,a , marea generic: este o portiune de cod care anumite tipuri de date sunt lsate a n a , necompletate mod intetionat. Tipurile de date abstractizate se noteaz cu n a , niste nume simbolice (asemntoare cu niste variabile) care vor a a nlocuite la , , compilare conform cu specializrile si instantierile din restul codului. a , , Pentru a da un exemplu scurt, s ne imaginm c ne-am dori o clas care a a a a s retin o pereche de obiecte. Fr template-uri, am avut doar dou solutii, a , a aa a , destul de incomode amndoua: a

STANDARD TEMPLATE LIBRARY

39

S punem clas doi pointeri generici (de tip (void*)) si s lsm tratarea a n a a aa , tipurilor n ntregime seama programatorului, dar astfel nu am mai n putut face vericarea tipurilor la compilare S scriem cte o clas separat pentru ecare combinatie de tipuri de date. a a a a , Aceast solutie nu merge mod evident, deoarece mereu se scriu clase a n , noi, si ar un de neconceput s a ntretinem cteva mii de versiuni diferite a , , de clas care difer doar prin tipurile de dat ale unor membrii. a a a Solutia pe care o ofer C++ prin template-uri este s scriem codul din a a , metodele clasei folosind niste tipuri parametrizate, urmnd ca acestea s e a a , nlocuite dup nevoie de compilator. Un template care implementeaz clasa din a a aceste exemplu poate urmtorul (chiar dac exist deja clasa std::pair<T,V>) a a a
1 2 3 4 5 #include <iostream> // Consideram doua tipuri distincte, la care le spunem generic T si V template<class T, class V> class Pair{

6 public: 7 T tData; 8 V vData; 9 Pair<T,V>(T tData, V vData); 10 }; 11 12 13 14 15 16 17 // Pentru a exemplica sintaxa, am ales sa nu denesc // constructorul inline template<class T, class V> Pair<T,V>::Pair(T tData, V vData) : tData(tData), vData(vData) { }

18 19 template<class T, class V> 20 std::ostream& operator<< (std::ostream& out, Pair<T,V>& pair){ 21 return out << "(" << pair.tData << "," << pair.vData << ")\n"; 22 } 23 24 int main() 25 { 26 // Instantiem un obiect de tip Pair<int,int> 27 Pair<int,int> pair1(1,3); 28 std::cout << pair1; 29 30 31 32 33 34 // Instantiem un obiect de tip Pair<std::string,double> Pair<std::string,double> pair2(std::string("Eric Cartman"), 10.0f); std::cout << pair2; return 0;

STANDARD TEMPLATE LIBRARY

40

35 }

Primul lucru pe care trebuie s precizm aici este c att Pair<int,int> a l a a a ct si Pair<std::string,double> sunt clase distincte! Ele sunt generate de a , ctre compilator folosind substitutie de tipuri dac se depisteaz cod o nou a a a n a , combinatie de tipuri utilizat. Dup substitutia tipurilor, codul template-ului a a , , n se compileaz separat pentru ecare specializare parte (o specializare este o a particularizare a template-ului pentru anumite tipuri de date concrete) ca si , cum am scris clase distincte. Acest lucru ncetineste mult compilarea si poate , , genera erori la compilare neasteptate dac tipurile de date nu implementeaz a a , n toate metodele invocate template ( C++ nu exist interfete mod explcit, n n a , este responsabilitatea programatorului s se asigure c o anumit clas ima a a a plementeaz toate metodele pe care trebuie s le pun la dispozitie). De asemea a a , nea, copierea repetat a codului de numeste code bloating si proiectele mari a , , n ncetineste compilarea foarte mult si duce la binare foarte voluminoase. , , Al doilea lucru care trebuie remarcat este acela c template-ul de operator<< a presupune c tipurile T si V au, la rndul lor, denit operatorul << pentru a a , ostream-uri. caz constrar, nu veti putea compila codul. Nu am putut scrie n , ceva similar C, deoarece printf trebuie s tin cont de tipurile de date si n a , a , formatele pe care le aseaz. a , O facilitate folosit destul de mult STL este aceea de supra arcare a a n nc specializrilor. Asta a nseamn c pentru anumite cazuri particulare de tipuri a a de date, se pot specica implementri mai eciente. Cel mai clar exemplu este a template-ul std::vector<bool>, care loc s e administrat sub forma unui n a array ca toate celelalte specializari de std::vector, este comprimat pe biti. , Comparativ, Java nu exist efectiv template-uri, chiar dac exist ceva n a a a similar: Generics. Mecanismul de clase generice permite specicarea de tipuri de date concrete cod, de exmeplu ne asteptm ca un obiect care este o instant de n a , ,a java.util.Vector<String> s contin doar obiecte de tip String. realitate, a a In , a, Java face aceste vericri dinamic la runtime, din cauza polimorsmului ns a implicit. Din aceast cauz, codul nu se multiplic realitate, ci doar apar a a a n niste restrictii care s v scape de cast-uri inutile. Puteti trisa acest mecanism a a , , , , foarte usor, dac v puneti mintea. a a , , O observatie undeva la limita dintre comic si serios se refer la cei care a , , declar Java obiecte de tipul java.util.Vector<Object>. Chiar dac nu a n a este nimic gresit la aceast declaratie, ea nici nu face nimic special, pentru ca a , , Java orice obiect este implicit derivat din tipul Object :). n

6.3

Componentele STL

principiu, STL pune la dispozitie trei tipuri de implementri pe care s le In a a , puteti folosi de-a gata cod: n ,

STANDARD TEMPLATE LIBRARY

41

Containere Adaptori Algoritmi cele ce urmeaz vom detalia cu exemple practice ecare categorie. In a

6.4

Containere: vector, list, slist, deque, set, map

Clasa template std::vector<T> nu este cu nimic mai mult dect un wrapper a peste un array alocat dinamic, si functioneaz similar cu echivalentul din Java a , , pe care l-am dat exemplu mod repetat. Cu toate acestea, C++ exist o n n a serie de diferente specice limbajului: , Insertia la sfrsit se face cu ajutorul metodei push back(). a , Incercarea , de a insera elemente pe pozitii nealocate poate duce la SegFault, pentru , c nu vi se garanteaz nicieri capacitatea array-ului din interior (folositi a a a , resize() pentru asta). Accesul la elemente se poate face e prin operatorul [], fr vericarea aa limitelor, e prin functia membru at(), cu vericarea limitelor. Avantajul , functiei at() este c nu primiti SegFault dac accesati indecsi invalizi a a , , , , (atunci cnd masina virtual Java ar aruncat dulcele mesaj de exceptie a a , , Array Index Out of Bounds). Dezavantajul este c timpul de acces este a mai mare (trebuie executat un if suplimentar). Pentru c politica C++ a este s v lase s v alegeti singuri ce vreti s folositi, operatorul [] v d a a a a a a a , , , acces direct si rapid la array-ul din interiorul clasei, dar pe propria voastr a , rspundere! a Puteti manipula memoria alocata unui vector folosind una din metodele , resize() sau reserve(). Din pcate, exist mult confuzie cu privire la a a a folosirea lor, care se traduce adesea SegFault si vandalizarea tastaturii. n , Metoda resize() redimensioneaz capacitatea array-ului la capaca itatea dat ca parametru. Dac noua dimensiune este mai mare a a dect vechea dimensiune, se invoc constructorul tipului pentru noile a a obiecte de la sfrsit, astfel at acestea pot folosite direct! a , nc Metoda reserve() face cu totul altceva. Ea nu redimensioneaz a neaprat array-ul alocat, ci mai degrab se asigur c acesta are o a a a a capacitate cel putin egal cu parametrul. Dac este nevoie de o realoa a , care pentru a spori capacitatea vectorului, NU se invoc constructorul a spatiul de memorie de la sfrsit! Dac veti n a , a , ncerca s folositi acele a , , obiecte, veti avea tot felul de surprize neplcute. a ,

STANDARD TEMPLATE LIBRARY

42

Atribuirea dintre doi vectori se trauce prin dezalocarea memoriei primului vector si clonarea tuturor obiectelor din vectorul atribuit vectorul la care n , se atribuite. Chiar dac STL face aceste operatii batch, interschimbarea a n , a doi vectori prin trei atribuiri dureaz mai mult sau mai putin o eternitate a , termeni de timp de executie. Solutia inteligent este s folosim metoda n a a , , swap(), care interschimb mod inteligent doar pointerii ctre zonele de a n a memorie din interior si variabilele de statistic. a , Exist specializri de vector STL care sunt reimplementate mai ecient. a a n Mai exact, este vorba de specializarea std::vector<bool> care tine ele, mentele pe biti, nu pe char-uri. Teoretic, acest lucru duce la o economie , de memorie de 1/8, dar practic alocarea se face pasi incrementali de n , sizeof(int) din motive de performant de vitez. Dac acest lucru vi se a a ,a pare deranjant, aveti libertatea s instantiati vectorii cu alocatori scrisi a , , , , de voi (v asigur eu, NU se merit efortul). a a Acestea ind zise, lipseste doar un exemplu concret de folosire. Nu am pre, cizat mod deosebit numele altor metode din vector, deoarece le puteti gsi n a , documentati ocial foarte rapid. Am ales schimb s pun accentul pe n a n a , , elegerea functionrii. nt a ,

STANDARD TEMPLATE LIBRARY

43

1 2 3 4 5

#include <iostream> #include <string> // Headerul care contine template-ul pentru vector #include <vector>

6 7 int main() 8 { 9 int arrayInt[4] = { 0, 1, 2, 3}; 10 // Cream un vector din intregii intre doua adrese dintr-un array 11 std::vector<int> vInt(arrayInt, arrayInt+4); 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 } // Cream un vector de stringuri std::vector<std::string> v; char cifre[2] = { 0, 0 }; for (int i = 0; i < 10; i++){ cifre[0] = i + 0; // Inseram la sfarsit instante de stringuri create din cifre v.push back(std::string(cifre)); } // Asam 0, in doua moduri: cu si fara bounds checking std::cout << v[0] << " " << v.at(0) << "\n"; // Redimensionam vectorul v la 3 elemente // (celelalte 7 se distrug automat) v.resize(3); // Il reextindem la 8 elemente cu reserve() // (cele 5 elemente in plus nu se instantiaza automat) // Reserve se foloseste pentru a evita in avans realocari in cascada v.reserve(8); // Cream un vector nou, gol, si il interschimbam cu cel vechi std::vector<std::string> w; w.swap(v); // O sa cauzeze exceptie pentru ca elementul de pe pozitia 6 nu este instantiat std::cout << w.at(6) << "\n"; return 0;

STANDARD TEMPLATE LIBRARY

44

Clasele template std::list<T> si std::slist<T> implementeaz liste dublue, a , respectiv simplu antuite de obiecte generice. Metodele de insertie sunt push back() nl , , si push front() functie de captul la care dorim s inserm, iar cele de elimn a a a , , inare de elemente sunt pop back() si pop front(). , Aici trebuie de remarcat de asemenea c nu mai este permis accesul prin a subscripting, ca la vector (complexitatea ar O(N)). schimb, accesul se face In mai degrab la elementele de la captele listelor prin metodele front() si back(). a , rest, modul de folosire este foarte intuitiv. Un exemplu similar cu cel de In la vectori regsim mai jos: a
1 2 3 4 5 6 #include <iostream> #include <string> // Headerul care contine template-urile pentru liste #include <list>

7 int main() 8 { 9 std::list<std::string> l; 10 11 char cifre[2] = { 0, 0 }; 12 for (int i = 0; i < 10; i++){ 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 } cifre[0] = i + 0; l.push back(std::string(cifre)); } // Asam elementele de la inceputul si sfarsitul listei std::cout << l.front() << " ... " << l.back() << "\n"; // Sortam lista l.sort(); // Golim lista de la coada la cap si asam elementele // din ea pe masura ce le scoatem while (!l.empty()){ std::cout << l.back() << " "; l.pop back(); } std::cout << "\n"; return 0;

STANDARD TEMPLATE LIBRARY

45

Un std::deque<T> functioneaz ca o list dublu antuit, dar permite a a nl , a , niste operatii suplimentare datorit modului de reprezentare intern. Cea mai a a , , important operatie suplimentar este accesul la elemente prin subscripting a a , (adic prin operatorul []). Din acest punct de vedere seamn mai mult cu a a a un vector. Aceast structur de date este alocatorul implicit pentru o serie de a a adaptori de containere, dar rest nu are facilitti deosebite asa c nu voi insista n a, a , asupra ei. Un std::set<T> este o implementare a unei multimi sens matematic. n Tipul de date cu care se specializeaz un template trebuie s suporte comparatie a a , (operatorul < s e bine denit a ntre dou obiecte de tip T). Inserarea a ntr-un set se realizeaz cu ajutorul metodei insert(), iar cutarea se face cu ajutorul a a metodei nd() care ntoarce un iterator (mai multe despre iteratori sectiunea n , corespunztoare). Ambele operatii se fac timp amortizat O(logN). Trebuie a n , de asemenea de retinut c seturile sunt implementate cu ajutorul arborilor bia , colori (numiti si red-black). 1 #include <iostream> , ,
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 } // Headerul care contine template-ul pentru set #include <set> int main() { // Cream un set de intregi. Comparatorul este implicit < std::set<int> s; // Inseram un element s.insert(1); // Vericam existenta lui. nd() intoarce un iterator if (s.find(1) != s.end()){ std::cout << "Da!\n"; } else { std::cout << "Nu!\n"; } return 0;

STANDARD TEMPLATE LIBRARY

46

Un std::map<T,V> este un template cu doi parametrii de instantiere: tipul cheilor si tipul valorilor asociate. Functionarea este bine cunoscut din a , , Java, dar sunt cteva detalii de implementare unice pentru C++. Ca si std::vector, a , un map implementeaz operatorul de indexare [], dar atentie: folosirea opeartorua , lui conduce automat la inserarea map a cheii! Dac nu se atribuie o valoare n a cadrul instructiunii, cheii va corespunde ca valoarea un obiect construit cu n i , constructorul fr parametri. Functia alternativ la inserare se numeste chiar aa a , , insert(), iar functia de stergere se numeste erase(), ca la seturi. De fapt, un , , , map este realitate doar un set de obiecte de tipul std::pair<T,V> sortate n dup elementul T. Cheile sunt, din nou, tinute sub form de arbore bicolor. a a ,
1 #include <iostream> 2 3 4 5 6 7 #include <string> // In acest header se aa template-ul pentru map #include<map> int main() std::map<std::string, int> numere; // Inseram implicit un numar prin indexare numere[std::string("unu")] = 1; // Inseram prin apel de functie numere.insert(std::pair<std::string, int>(std::string("doi"), 2)); // Stergem un numar din map numere.erase(std::string("unu")); // Asam un numar din map std::cout << numere[std::string("doi")] << "\n"; return 0;

8 { 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 }

Ce este interesant si util la un map este c functioneaz aproape ca un vector a a , , cu indici de orice tip posibil de dat, a cu niste penalizri de complexitate. a ns a ,

STANDARD TEMPLATE LIBRARY

47

6.5

Adaptori: queue, stack, priority queue

Adaptorii sunt niste clase care au rolul de a ncapsula interior alte containere n , mai generalizate, cu scopul de a restrictiona accesul la ele. Voi lua drept exem, plu adaptorul pentru stiv deoarece este una dintre cele mai folosite structuri a de date. principiu, STL nu exist o clas special care s implementeze In n a a a a doar stive. Cei care au proiectat limbajul si-au dat seama c acest efort ar a , inutil, dar totusi cum ar putut prevenit programatorii s apeleze metode de a , acces ale elementelor de la baza stivei? Solutia oferit de adaptori este foarte simpl: crem o clas nou care s a a a a a a , contin interior un membru privat de alt tip (implicit este vorba de un a n , std::deque, dar putem da si alti alocatori), iar apelurile la obiectul exterior s a , , e directinoate ctre apeluri de la obiectul intern astfel at s rmn vizibil a nc a a a a a , doar functionalitatea care ne intereseaz (de exemplu, la o stiv ne initereseaz a a a , doar push(), pop() si top(). Principalii adaptori din C++ sunt: , queue - reprezint o structur de tip FIFO (rst in, rst out) a a stack - reprezint o structur de tip LIFO (last in, rst out) a a a a priority queue - reprezint o structur de tip HEAP (cel mai mic element este primul care este scos)

6.6

Iteratori

Avnd vedere multitudinea de containere si adaptori pe care C++ o pune la a n , dispozitie biblioteca standard, a fost nevoie de un alt concept pentru a abn , stractiza traversarea colectiilor: iteratorii. Un iterator C++ se deosebeste n , , destul de mult conceptual fat de un iterator din Java. esent, C++ In n ,a ,a conceptia de la care s-a pornit a fost c un iterator este orice clas care se a a , comport ca un pointer: a Suport operatorul ++ (eventual si ) pentru a trece la urmtorul a a , (eventual precedentul) element din colectie, respectiv adunarea cu ntregi , Atunci cnd referentiem (supra arcm operatorul *), returneaz un a l nc a a , obiect de tipul celor din clasa pe care o deserveste. , Conform cu aceast denitie, un pointer la int este un iterator perfect legal a , pentru un array (se pot aplica pe el operatorii ++, + si *). Cu toate , astea a, din cauz c toate colectiile au de-a face cu iteratori, standardul de ns a a , facto este o structur de date s aib interior un tip de dat care s implea a a n a a menteze un iterator. Deoarece tipul iterator este denit intern colectii, acestea pun de obicei n , la dispozitie dou metode prin care se pot obtine iteratori: a , ,

STANDARD TEMPLATE LIBRARY

48

begin() - aceast metod ne ofer primul element din colectie a a a , end() - aceast metod ne ofer elementul din colectie imediat dup ula a a a , timul element. De remarcat c end() nu a ntoarce un iterator la ultimul element din colectie, ci primul element din afara colectiei, cu alte cuvinte , , nu poate niciodata referentiat, ci foloseste la comparatii ca s stim unde a, , , , s ne oprim. a rbegin(), rend() - aceste metode sunt oglinditele celor de mai sus, doar c dinspre coad spre cap. Anumite structuri de date pun la dispozitie a a , traversri ambele directii, doar c cazul acesta, rend() o sa e a n a n , prima locatie dinainte primul element din colectie si din nou, nu poate , , , referentiat (pentru c oricum nu exist). a a , Justicarea foarte clar pentru folosirea operatorilor este aceea c poti para a , curge orice colectie dac stii a , , de unde s a ncepi unde s te opresti a , cum s treci de la un element la altul a De asemenea, mai exist si iteratori de tip const iterator, care garana , teaz c nu modic elementele structurii pe msurs ce se deplaseaz a a a a a a prin ea.
1 2 3 4 5 6 #include #include #include #include <iostream> <vector> <set> <map>

int main() // Cream un vector, un map si o multime de intregi std::vector<int> v; std::set<int> s; std::map<int,int> m; // Inseram in amandoua numerele de la 0 la 9 for (int i = 0; i < 10; i++){ v.push back(i); s.insert(i); m[i] = -i; // Cheia e numarul, valoarea e -numarul } // Iteram tot setul, in ordine, si asam for (std::set<int>::iterator it = s.begin(); it != s.end(); it++){ // it este iterator de set<int>, deci functioneaza identic cu un pointer

7 { 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

STANDARD TEMPLATE LIBRARY

49

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 } 47

// de int (chiar daca nu este un pointer de int, are aceeasi operatori) std::cout << (*it) << " "; } std::cout << "\n"; // Stergem din vector primul si al doilea element. Obsevatie: // v.begin()+2 este, ca si v.end(), primul element care NU participa la stergere v.erase(v.begin(), v.begin()+2); // Asam ce a ramas din vector in ordine inversa std::vector<int>::reverse iterator rit; for (rit = v.rbegin(); rit != v.rend(); rit++){ std::cout << (*rit) << " "; } std::cout << "\n"; // Iteram prin map si asam. Observatie: un map este un set de pair<Key,Value> // deci un iterator de map functioneaza ca un pointer la un pair<Key,Value> for (std::map<int,int>::iterator it = m.begin(); it != m.end(); it++){ std::cout << "(" << it->first << "," << it->second << ")\n"; } return 0;

STANDARD TEMPLATE LIBRARY

50

6.7

Algoritmi

Probabil cel mai mare avantaj din folosirea STL reprezint existenta algol a , ritmilor gata implementati pentru conainerele denite. Lista acestor algoritmi , este foarte variat. Astfel, STL pune la dispozitie algoritmi pentru: a , Cutare si statistici : nd, count, mismatch, equal, search a , Modicarea ordinii elementelor : swap, copy, reverse, replace, rotate, partition Sortare : sort, partial sort, nth element Cutare binar : binary search, lower bound, upper bound a a Interclasare : merge, set intersection, set dierence Gestiunea datelor : make heap, sort heap, push heap, pop heap Minim si maxim : min, min element, lexicographical compare , Din motive de pstrare a generalittii, algoritmii din STL se folosesc de itera a, atori. Desi initial folosirea acestor implementri ar putea prea greoaie, odat a a a , , stpnite conceptele care stau la baza lor, veti reusi s realizati implementri a a a a , , , foarte rapide. continuare, vom da dou exemple de folosire ale sortrii, respectiv cutrii In a a a a binare din STL
1 #include <algorithm> 2 #include <vector> 3 #include <iostream> 4 5 int main() 6 { 7 // Cream un vector cu niste intregi oarecare 8 int myInts[] = { 32, 71, 12, 45, 26, 80, 53, 33 }; 9 std::vector<int> v(myInts, myInts+8); 10 11 12 13 14 15 16 17 18 19 20 21 // Sortam intre ele elementele de pe pozitiile 0, 1, 2, 3 si 4 // Vom specica capetele intre care sortam prin doi iteratori std::sort(v.begin(), v.begin()+5); // Ca sa gasim rapid un element in vector, vom folosi cautarea binara // Intai si intai, terbuie sa sortam tot vectorul pentru a cauta binar std::sort(v.begin(), v.end()); // Vericam daca elementul 15 se aa in vector if (binary search(v.begin(), v.end(), 15)){ std::cout << "15 exista in vector!\n";

STANDARD TEMPLATE LIBRARY

51

22 23 24 25 26 27 }

} else { std::cout << "15 nu exista in vector!\n"; } return 0;

STANDARD TEMPLATE LIBRARY

52

6.8

Greseli frecvente ,

Programatorii de Java tind s fac la a a nceput o serie de greseli folosirea conn , tainerelor din STL, datorate asemnrii , eltoare dintre sintaxa celor dou a a ns a a limbaje. Derivarea containerelor. Java, o practic uzual este derivarea colectiilor In a a , de date pentru a le modica functionalitatea. Aceast practic este foarte gresit a a a , , C++. Limbajul nu v interzice s faceti asta, dar derivarea claselor rezuln a a , tate din specializarea template-urilor de STL v poate cauza memory leaka uri si comportamente neasteptate, deoarece destructorii containerelor din C++ , , NU sunt virtuali. Ei au fost lsati intentionat nevirtuali limbaj din motive a , n , de performant. Chiar dac sunt coincidente care teoretic ar functiona corect a n ,a , , si derivarea, singura practic corect de a extinde un container de C++ este a a , scrierea unui wrapper (poart si numele de adaptor, dar nu trebuie confundati a, , cu adaptorii din Java). Un wrapper este o clas care contine interior un a n , obiect pe care manipuleaz l a ntr-un anumit mod. Inserarea prin copiere. Java, atunci cnd inseram un obiect In a ntr-o colectie, de fapt ceea ce inseram noi era o referint la un obiect, si nu obiectul , ,a , sine! C++, ambele comportamente sunt posibile. Putem avea: n In containere de instante de obiecte (trebuie s e atribuibile). Ex: std::vector<MyClass> a , containere de pointeri la obiecte (se comport cam ca Java). a n std::vector<MyClass*> Ex:

ATENTIE!. Atunci cnd avem containere de obiecte, insertia se face prin a , , copiere! Cu alte cuvinte, dac scriem v.push back(someObject);, realitate a n se va instantia un obiect nou care se va depune container, iar someObject va n , rmne neatins. Obiectele din containere trebuie s poat asignabile (dac a a a a a au constante interior, va trebui s redeniti operatorul de atribuire)! n a , Iteratorii inconsisteti si SegFault-urile. Ar trebui ca de ecare dat a , , cnd folositi iteratori, s considerati c acestia sunt un tip de pointei, si deci a a a , , , , v pot cauza SegFault. principiu, un iterator rmne valid, sau safe, atta a In a a a timp ct colectia din care a fost creat nu se modic. Ca s , elegem mai bine a a a nt , aceast problem, s presupunem urmtorul scenariu: Avem un vector<int> a a a a care simulm o coad (de exemplu, la parcurgerea ltime a unui graf). n a a n a , In cursul simulrii, parcurgem vectorul cu un iterator si pentru ecare element viza , itat adugm, eventual, alte elemente la nalul vectorului. Aparent, am putea a a spune c nimic ru nu se poate ampla, dar trebuie s ne gndim la ce se a a nt a a ampl interior. De ecare dat cnd adugm la nalul vectorului un nou nt a n a a a a element, exist posibilitatea s e nevoie de o realocare array-ul din interior. a a n Dac realocarea nu se poate face in-place din diverse motive, atunci se va aloca a memorie alt parte si se va muta continutul noua locatie. Vechea locatie n a n , , , , de memorie devine invalid (cu alte cuvinte, a fugit memoria de sub iteraa tor). La urmtoarea referentiere de operator, vom obtine SegFault. Un alt caz a , ,

STANDARD TEMPLATE LIBRARY

53

des alnit de SegFault este nt ncercarea de a sterge elemente din containere pe , msur ce acestea sunt parcurse de iteratori. C++, think pointer-style! a a In Containerele sortate. O greseal destul de frecvent este si presupunerea a a , , c un container sortat (de exemplu, un set sau un map) se reorganizeaz aua a tomat atunci cnd modicm continutul unui obiect din interior prin intermediul a a , unui pointer. Acest lucru nu este adevrat! Dac veti modifca prin pointeri a a , (sau iteratori) obiectele din interior, veti face containerul inconsistent. O abor, dare corect de a pstra continutul sortat este s stergeti din container obiectul a a a, , , pe care vreti s modicati si s reintroduceti versiunea modicat cu insert(). a l a a , , , , Sincronizarea. Chiar dac nu veti scrie anul 2 programe paralele, puteti a n , , retine despre containerele din C++ c NU sunt thread-safe! Dac veti vrea a a , , s le partajati a a , ntre thread-uri, trebuie s le protejati explicit cu mutex-uri sau , semafoare.

NAMESPACE-URI (***)

54

Namespace-uri

Un aspect al crei lips se simtea C si pe care C++ l-a introdus este posibila a n , , itatea separrii spatiilor de nume limbaj. a n , Un spatiu de nume poate comparat cu un context care declarm n a , nume (de clase, de tipuri, de variabile, de functii, etc.). interiorul unui spatiu In , , de nume, nu putem avea dou nume identice (de exemplu, nu putem avea dou a a variabile sau dou clase cu acelasi nume), dar spatii de nume diferite nu avem a n , , acest tip de restrictii. , Cel mai bun exemplu de spatiu de nume reprezint o clas (desi o clas l a a a , , nu este efectiv un spatiu de nume, doar se comport asemntor). Astfel, a a a n , interiorul clasei std::vector<int>, numele iterator specic mod unic un a n singur tip de dat, timp ce interiorul clasei std::map<int,int>, numele a n n iterator specic tot mod unic, un alt tip de dat. Chiar dac cele dou a n a a a tipuri de dat se numesc la fel, ele fac parte din spatii de nume diferite si nu se a , , confund a ntre ele. Spatiile de nume au structur ierarhic (de arbore) si sunt specicate sub a a , , form de prexe, urmate de operatorul de rezolutie de scop (::). Ati observat a , , c cadrul exemplelor din acest tutorial am scris aproape de ecare dat std:: a n a fata claselor si constantelor din STL. std:: este spatiul de nume standard n , n , , care sunt depuse toate numele care fac parte din standardul limbajului. Un alt spatiu de nume este spatiul global (sau implicit) de nume. Din acest spatiu fac , , , parte clasele denite de utilizator sau variabilele globale care nu sunt incluse n nici un alt spatiu de nume. , Un spatiu de nume se poate specica acnd codul nbr a ntr-un bloc de forma , namespace spatiul meu de nume { ...cod... } . , Uneori devine obositor, sau este inestetic s prexm toate clasele folosite a a cu namespace-ul din care acestea fac parte (mai ales dac este vorba de un a namespace lung). De exmeplu, dac avem o functie static numit makeCofa a a , fee clasa BestClassEver din namespace-ul best project ever care face n parte din namespace-ul best company ever, apelarea ei ar trebui s e de a forma best company ever::best project ever::BestClassEver::makeCoee(); Cu sigurant nu ne dorim s scriem asa ceva prea des. a ,a , Namespace-ul implicit cod este cel curent (prexarea cu el este redunn dant). El poate prescurtat si prin :: la nevoie, pentru a distinge a ntre , variabile globale si locale. Pentru accesul la numele din alte spatii, trebuie , , s precizm explicit acest lucru. Totusi, putem evita scrierea repetitiv a altor a a a , spatii de nume dac instruim compilatorul s fac merge a a a ntre dou spatii de a , , nume. Acest lucru se poate realiza cu ajutorul directivei using ca exemplul n de mai jos:

NAMESPACE-URI (***)

55

1 2 3 4 5 6 7 8 9 10 11 12

#include <vector> // Din acest moment, facem reuniunea spatiilor :: si std:: // Pentru toate clasele folosite, compilatorul va incerca sa caute singur // spatiul de nume corect (:: sau std::, dupa caz). In cazul in care exista // mai mult de o posibilitate, va trebuie sa scrieti namespace-ul explicit! using namespace std; // Se poate specica si reuniunea cu o singura clasa astfel: // using spatiu de nume::Clasa; int main() int numere[5] = { 4, 3, 2, 1, 0 }; vector<int> v(numere, numere+5); sort(v.begin(), v.end()); for (vector<int>::iterator it = v.begin(); it != v.end(); ++it){ cout << (*it) << " "; } cout << "\n"; return 0;

13 { 14 15 16 17 18 19 20 21 22 23 24 25 26 } 27

Chiar dac folosirea lui using scurteaz codul, este gresit s folosim using a a a , headere! Headerele sunt siere care pot incluse unele altele si siere n n , , n , surs orice combinatii. Existenta unui using ele poate duce accidental la a n n , , coliziuni de nume din dou namespace-uri diferite, coliziuni care nu ar fost a posibile dac nu foloseam using. a denitiv, folosirea lui using pentru spatiul std:: sierele de impleIn n , , mentare tine de gusturile ecruia. Chiar dac produce cod mai scurt si mai a a , , clar, anumiti programatori prefer s scrie mereu explicit spatiile de nume. a a , ,

TRATAREA EXCEPTIILOR (***) ,

56

Tratarea Exceptiilor ,

C++ a introdus la aparitia sa mecanismul de tratare al exceptiilor (try-catch). , , Aceast functionalitate a fost transpus Java, unde a devenit foarte impora a n , tant (stim deja c Java v oblig s tratati toate exceptiile). C++, a, a , a a a a In ns , , lucrurile stau mult diferit. Limbajul nu oblig tratarea exceptiilor. Dac o functie arunc o exceptie a a a , , , care nu poate tratat de nici o functie de deasupra ei din lantul de apeluri, a , , atunci se ncheie fortat executia programului. , , Mai mult, exceptiile C++, spre deosebire de Java, pot obiecte de orice n , tip de dat! Acest lucru ar fcut a a ngrozitoare tratarea tuturor tipurilor de exceptie de ecare dat, motiv pentru care s-a introdus operatorul ellipsis, a , notat prin trei puncte (...). Semnicatia acestui operator este orice exceptie, si , , , functioneaz similar cu superclasa Exception din Java. a , continuare se exemplic sintaxa de exceptii din C++: In a ,
1 #include <iostream> 2 3 int main() 4 { 5 6 7 8 9 10 11 12 13 14 15 16 17 } try{ // Asa se arunca o exceptie de tip int throw 5; } catch (int errCode) { // Care este prinsa aici std::cout << "Exceptie de tip int.\n"; } catch (...) { // Just to make sure, lasam si posibilitatea altor exceptii std::cout << "Orice alta exceptie.\n"; } return 0;

Observatie: Majoritatea coding guide-urilor renumite condamn folosirea a , exceptiilor cod deoarece duc la cod greu de n ntretinut. Acest lucru con, , trasteaz puternic cu mentalitatea din Java! a O alt observatie este aceea c stream-urile din STL nu arunc mod a a a n , obligatoriu exceptii. Dac dorim ca ele s arunce exceptii, acest lucru trebuie a a , , setat manual din cod.

RESURSE

57

Resurse

In ncheierea acestui tutorial, sper c ati reusit s v formati o prere asupra a , a a a , , limbajului. decursul sectiunilor, am In ncercat s ating toate chestiunile ima , portante, fr a merge mai detaliu dincolo de strictul necesar att scrierii de aa n a cod corect ct si , elegerii documentatiei si surselor din biblioteci. a , nt , , Dup cum am spus si introducere, acest tutorial nu tine nicidecum locul a , n , unui manual de C++. Celor care le place limbajul si care sunt interesati s a , , , eleag detaliu, le recomand special cartea The C++ Programming l nt a n n Language, scris de Bjarne Stroustrup, creatorul limbajului. Totusi, cartea a , este extrem de voluminoas si foarte detaliat, si s-ar putea s descurajeze proa , a , a gramatorii care sunt la primele contacte cu limbajul. continuare, am ales cteva surse foarte bune de documentatie care v vor In a a , de real ajutor de ecare dat cnd v loviti de probleme C++: a a a n , Resurse online: Sursa ociala de documentatie pt C++ (prima si cea mai accesibil surs a a , de informare despre limbaj!) Un tutorial de baz pentru STL a Un tutorial ceva mai serios despre STL Introducere STL n Tutorialul de STL al www.decompile.com Site-ul ocial al bibliotecii BOOST Crti: a The C++ Programming Language - Special Edition, Bjarne Stroustrup C++, Danny Kalev, Michael J. Tobler, Jan Walter

You might also like