Sunteți pe pagina 1din 33

1

Concepte de baz ale limbajului C++

Modalitile (tehnici, paradigme) de programare au evoluat de-a lungul anilor, reflectnd trecerea de la programe de dimensiuni reduse la programe i aplicaii de dimensiuni foarte mari, pentru coordonarea crora sunt necesare tehnici evoluate. Software-ul de dimensiuni mari, care nu poate fi realizat de o singur persoan, intr n categoria sistemelor complexe, alturi de alte sisteme complexe din cele mai variate domenii, cum sunt organizarea materiei sau organizrile sociale. Situaiile reale i experiene ale psihologilor au relevat limitele capacitii umane n percepia sistemelor complexe, adic imposibilitatea unei persoane de percepe i controla un numr mare de entiti de informaie simultan. De aceea, descompunerea i organizarea sistemelor complexe, n scopul de putea fi percepute, proiectate sau conduse este esenial. Ordinea n sistemele complexe este introdus n general printr-o organizare ierarhic, pe mai multe tipuri i nivele de ierarhie. n reprezentarea ierarhic a sistemelor complexe se evideniaz dou tipuri de ierarhii: ierarhia structural sau de compoziie i ierarhia de tip. Ierarhia structural este o ierarhie n care se poate afirma despre un obiect c este o parte a altui obiect, mai complex. Exemple de astfel de descompuneri se pot da oricte, din orice domeniu. De exemplu, un calculator poate fi studiat prin descompunerea lui n subansamble componente: placa de baz, placa video, monitor, etc; la rndul ei, placa de baz este compus din placheta de circuit imprimat, procesor, memorie, etc. Aceasta este o ierarhie de tipul este o parte din. Pe de alt parte, fiecare obiect poate fi ncadrat ntr-o categorie (clas, tip) mai larg, care conine mai multe obiecte care au proprieti comune. De exemplu, procesorul este de o component electronic; monitorul este un dispozitiv de afiare, etc. Aceast ierarhie se refer la apartenena obiectelor la o anumit clas (sau tip este de tipul). Este esenial de a privi sistemele complexe din ambele perspective, studiindule att din perspectiva ierarhiei structurale, deci a obiectelor care le compun, ct i a ierarhiei de tip, deci a claselor crora le aparin. La rndul lor, clasele din care fac parte obiectele pot fi organizate sau studiate ca elemente componente ale unei ierarhii, prin care o clas este considerat ca primitiv (printe) a unei alte clase. Cele dou tipuri de ierarhii, ierarhia de clase i ierarhia de obiecte nu sunt independente, i,

mpreun, pot s reprezinte un sistem complex. La fel ca oricare sistem complex, software-ul poate fi controlat prin descompunerea lui. Rolul descompunerii unui sistem n general (i al programelor n special) este de a permite nelegerea i manevrarea acestuia: sistemul este descompus n pri din ce n ce mai mici, fiecare dintre ele putnd fi rafinat i dezvoltat independent. Principiul divide-et-impera, care se aplic n multe situaii, este util i n programare. Se pot identifica dou tipuri de descompunere a programelor: descompunerea algoritmic i descompunerea orientat pe obiecte. Descompunerea algoritmic permite abordarea structurat a programrii. Programul este mprit n module, n funcie de aciunile pe care trebuie s le efectueze, fiecare modul se mparte n elemente funcionale (blocuri, proceduri, funcii), ntr-o structurare de sus n jos (top-down), care urmrete diagrma de trecere a datelor n cursul execuiei. Descompunerea orientat pe obiecte este o alternativ de descompunere prin care programul se descompune dup obiectele care pot fi identificate, fiecare obiect avnd asociate o mulime de operaii care sunt apelate n cursul desfurrii programului. O problem nu poate fi abordat simultan prin ambele metode; se alege fie una, fie cealalt metod. Care dintre ele este cea mai bun? Nu exist un rspuns absolut i universal valabil, dar experiena a dovedit c pentru sisteme de dimensiuni mari, descompunerea orientat pe obiecte este mai eficient, mai sigur i mai flexibil.

1.1 Tehnici de programare


Modul n care este abordat programarea din punct de vedere al descompunerii programelor definete mai multe tehnici de programare (paradigme), care s-au dezvoltat i au evoluat odat cu evoluia sistemelor de calcul. Programarea procedural este prima modalitate de programare care a fost i este nc frecvent folosit. n programarea procedural accentul se pune pe descompunerea programului n proceduri (funcii) care sunt apelate n ordinea de desfurare a algoritmului. Limbajele care suport aceast tehnic de programare prevd posibiliti de transfer a argumentelor ctre funcii i de returnare a valorilor rezultate. Limbajul Fortran a fost primul limbaj de programare procedural. Au urmat Algol60, Algol68, Pascal, iar C este unul din ultimile invenii n acest domeniu. Programarea modular. n cursul evoluiei programrii procedurale accentul n proiectarea programelor s-a deplasat de la proiectarea procedurilor ctre organizarea datelor, aceast deplasare reflectnd creterea dimensiunilor programelor. O mulime de proceduri corelate, mpreun cu datele pe care le manevreaz sunt organizate ca un modul. Tehnica de programare modular decide descompunerea unui program n module, care ncorporeaz o parte din datele programului i funciile care le

manevreaz. Aceast tehnic este cunoscut ca tehnic de ascundere a datelor ( datahiding). n programarea modular stilul de programare este n continuare procedural i nu exist o asociere strict ntre date i procedurile care opereaz asupra acestora, ci doar gruparea n module cu posibilitatea de a ascunde anumite informaii (date, nume de variabile, funcii) definite ntr-un modul, fa de celelate module. Modularitatea i ascunderea informaiilor sunt caracteristici implicite n programarea orientat pe obiecte. Programarea orientat pe obiecte reprezint aplicarea n domeniul programrii a unei metode larg rspndite n tehnic, numit tehnologia orientat pe obiecte, care se bazeaz pe modelul obiect. Modelul obiect al unei aplicaii implic patru principii importante i anume: abstractizare; ncapsularea; modularitate; ierarhie (motenire). n afara acestor principii, modelul obiect mai presupune: tipizare, concuren i persisten. Aproape nici unul dintre aceste principii nu este nou, dar utilizarea lor mpreun n modelul obiect, are un rol sinergetic, de potenare reciproc. Primele aplicaii ale acestor principii au fost introduse n limbajul Simula, care a stat la baza dezvoltrii ulterioare i a altor limbaje care permit abordarea modelului obiect: Smaltalk, Object Pascal, C++, Clos, Ada, Eifel. n momentul de fa, modelul obiect se dovedete a fi un concept unificator n tiina calculatoarelor n general, fiind aplicabil nu numai n programare, ci i n arhitectura calculatoarelor, n proiecatarea interfeelor utilizator, n baze de date. Programarea orientat pe obiecte-POO- (object-oriented programming) este o metod de programare n care programele sunt organizate ca i colecii de obiecte cooperante, fiecare dintre ele reprezentnd o instan a unei clase oarecare, iar clasele sunt membre ale unei ierarhii de clase, corelate ntre ele prin relaii de motenire. Exist trei pri importante ale acestei definiii: Se folosesc obiecte, nu algoritmi ca uniti constructive de baz, obiectele fiind componente ale unei ierarhii structurale. Fiecare obiect este o instan (un exemplar) al unei clase. Clasele sunt componente ale unei ierarhii de tip (este de tipul), fiind corelate ntre ele prin relaii de motenire. Dac lipsete una din aceste caracteristici, programarea nu se mai numete orientat pe obiecte, ci programare prin abstactizarea datelor, deoarece o clas este un tip de date abstract (sau programare bazat pe obiecte). Un limbaj este considerat un limbaj de programare orientat pe obiecte dac satisface mai multe cerine, ca de exemplu: Suport obiecte (instane ale unor clase), clasele fiind tipuri definite de utilizator (numite i tipuri abstracte de date). Tipurile (clasele) pot moteni atribute de la alte clase, numite clase de baz.

Dac un limbaj nu suport direct motenirea ntre clase se numete limbaj de programare bazat pe obiecte (object-based), cum este, de exemplu, limbajul Ada. Principiile modelului obiect aplicate n programare, enunate mai sus, vor fi prezentate pe scurt n continuare, detalierea lor fiind reluat n cursul expunerii, dup descrierea suportului oferit de limbajul C++ fiecreia dintre acestea. Abstractizarea nseamn identificarea similitudinilor ntre diferite entiti, situaii sau procese din lumea real i decizia de a se concentra atenia asupra acestor aspecte comune i ignorarea pentru nceput a celorlate aspecte. Abstractizarea denot caracteristicile eseniale ale unui obiect, care l deosebete de toate celelalte feluri de obiecte. ncapsularea este procesul de compartimentare a elementelor unei abstractizri n dou pri: structura i comportarea; ncapsularea separ comportarea (accesat prin interfa) de structur, definit prin implementare. ncapsularea prevede granie ntre diferite abstractizri, ceea ce conduce la o separare clar a acestora. Modularizarea este procesul de partiionare a unui program n componente individuale (module) ceea ce permite reducerea complexitii programului prin definirea unor granie bine stabilite i documentate n program. n practica programrii, modularitzarea const n partiionarea programului n module care pot fi compilate separat, dar care au conexiuni cu alte module ale programului. Modulele servesc ca i containere n care sunt declarate clasele i obiectele programului. Ierarhia este o modalitate de a ordona abstactizrile (tipurile abstacte de date). Ierarhiile denot o relaie de tip. De exemplu, un trandafir este o floare (un tip de floare), algoritmul quicksort este un algoritm de sortare, etc. Ierarhiile definesc relaii ntre clase prin care o clas (clasa derivat) partajeaz structura sau comportarea definit n alt clas (clasa de baz). Tipizarea (typing). Tipul este o caracterizare precis, structural i comportamental a unei colecii de entiti. Dei conceptul tipul este similar conceptului de clas, tipizarea se refer la un element diferit al modelului obiect de ct cel la care se refer clasele. Tipizarea este o accentuare a clasei unui obiect, astfel nct obiecte de clase diferite nu pot fi intershimbate ntre ele sau, cel mult, pot fi interschimbate ntr-un mod foarte restrictiv. Din punct de vedere a tipizrii, exist limbaje puternic tipizate (ca de exemplu, Eiffel), n care nu este admis nici o operaie asupra unui operand dac aceasta nu a fost exact prevzut pentru obiectul respectiv. Alte limbaje, cum este Smalltalk, sunt limbaje netipizate, care admit la compilare orice fel de operaii asupra operanzilor, eroarea violrii de tip manifestndu-se n timpul execuiei. Limbajul C++ este hibrid. El are tendina de tipizare puternic, dar este posibil ignorarea sau inhibarea regulilor de tipizare. Dei nu face parte din principiile fundamentale ale modelului obiect, tipizarea aduce beneficii importante n progrmarea obiect orientat. Concurena permite ca mai multe obiecte diferite s fie n execuie n acelai timp. Sistemele care implic concurena sunt sisteme cu procesoare multiple, n care mai multe procese sau thread-uri (fire de execuie) pot fi executate concurent pe procesoare diferite. Persistena. Un obiect n software ocup un anumit spaiu n memorie i are o existen ntr-o perioad determinat de timp din timpul de execuie al programului. De multe ori este necesar ca datele s supravieuiasc programului (exemplul tipic fiind bazele de date), iar definirea de ctre fiecare programator a modului cum sunt

salvate datele pentru a fi pstrate dup terminarea programului poate conduce c[tre scheme de memoare dintre cele mai diferite i greu de a fi utilizate interschimbabil. Prin conceptul de persisten se introduce un mod unitar de salcare i restaurare a datelor coninute de obiecte ale programului. Modelul obiect n domeniul programrii implic, pe lng implementarea programului ntr-un limbaj anume, etape premergtoare de analiz i proiectare care pregtesc structurarea efectiv a programului. Analiza orientat pe obiecte este o metod de analiz i examinare a unei aplicaii din perspectiva claselor i a obiectelor din domeniul problemei respective. Proiectarea orientat pe obiecte este o metod de proiectare a sistemelor care implic descompunerea orientat pe obiecte i asigur o modalitate de reprezentare att static ct i dinamic a modelului sistemului proiectat. Produsele unei analize orientate pe obiecte servesc ca modele ale proiectrii orientate pe obiecte, iar rezultatele acesteia folosesc direct n programarea orientat pe obiecte. Ca suport al modelului final, orientat pe obiecte al unei aplicaii, programarea i limbajele orientate pe obiecte sunt acelea care se recomand a fi studiate mai nti, pentru a beneficia de toate facilitile i tehnicile puse la dispoziie prioectanilor, analitilor i programatorilor. Dintre limbajele de programare orientate pe obiecte, limbajul C++ este unul dintre cele mai utilizate; compilatoare, biblioteci i instrumente de dezoltare a programelor C++ sunt disponibile att pentru calculatoare personale ct i pentru cele mai dezvoltate sisteme i staii de lucru. Limbajul C++ este o versiune extins a limbajului C, extensiile acestuia fiind elaborate de ctre Biarne Stroustrup n anul 1980 n laboratoarele Bell din Murray Hill, New Jersey. Extensiile dezvoltate de Stroustrup pentru limbajul C++ permit programarea orientat pe obiecte, pstrnd eficiena, flexibilitatea i concepia de baz a limbajului C. Numele iniial a fost C cu clase, numele de C++ fiindu-i atribuit n anul 1983. Scopul pentru care a fost creat C++ este acelai cu scopul pentru care este abordat n general programarea orientat pe obiecte: dezvoltarea i administrarea programelor foarte mari. Chiar dac necesitatea i superioritatea limbajului C++ este evident n cazul dezvoltrii programelor foarte mari, nu exist limitri n a fi folosit n orice fel de aplicaii, datorit faptului c C++ este un limbaj tot att de eficient ca i limbajul C. De la apariia sa, C++ a trecut prin trei revizii, n 1985, 1989 i ultima, prilejuit de definirea standardului ANSI pentru acest limbaj. O prim versiune a standardului a fost publicat n anul 1994, iar urmtoarea versiune este nc n lucru. n general, limbajul C++ prevede mai multe faciliti i mai puine restricii dect limbajul C, astfel nct majoritatea construciilor din C sunt legale i au aceeai semnificaie i n C++.

n acest capitol sunt prezentate unitar i concis conceptele de baz n programarea C++, att cele care sunt preluate din limbajul C ct i cele nou introduse. Multe dintre ele sunt reluate i dezvoltate pe parcursul seciunilor urmtoare. Cel mai scurt program C++ este:
main(){ }

Acesta definete o funcie numit main (), care nu primete nici un argument, nu execut nimic i nu returneaz nici o valoare. Dac se dorete ca programul s scrie un mesaj la consol (de tipul Hello, World!), pe lng utilizarea funciei obinuite din limbajul C (funcia printf() ), n C++ se poate utiliza i o funcie de scriere la ieirea standard (cout). Aceast funcie este funcia operator de scriere << care este definit n fiierul antet iostream.h. Un astfel de program este urmtorul:
#include <iostream.h> void main(){ cout << Hello, World!<< endl; }

Prima operaie de scriere afieaz la consol irul Hello, World!, iar urmtoarea (endl) introduce caracterul de linie nou. Operaia de citire de la tastatur poate fi realizat n C++ printr-o instruciune care folosete funcia operator de citire >>. De exemplu, n instruciunile care urmeaz se citete de la tastatur un numr ntreg:
int i; cin >> i;

Desigur, funcia scanf() de citire de la tastatur din limbajul C poate fi n continuare folosit, dar pe parcursul exemplificrilor se vor folosi mai mult aceste funcii C++ de citire i de scriere la consol, iar descrierea detaliat a operaiilor de intrare-ieire din C++ este prezentat n seciunea 6. Un program C++ const dintr-unul sau mai multe fiiere (files). Un fiier este o bucat de text care conine cod surs C++ i comenzi de preprocesare i este translatat din punct de vedere conceptual n mai multe faze. Prima faz este faza de preprocesare care execut includerea altor fiiere i substituirea macro-urilor. Preprocesarea este controlat prin intermediul directivelor introduse prin linii care au primul caracter #. Rezultatul preprocesrii este secvena de entiti (token semne). O astfel de secven de entiti, adic un fiier rezultat dup preprocesare, se numeste unitate de translatare (translation unit).

1.2 Convenii lexicale


Exist cinci tipuri de entiti: identificatori (identifiers), cuvinte-cheie (keywords), literale (literals), operatori (operators) i separatori (separators).

Caracterele: spaiu (blank), tabulatori orizontali i verticali (tabs), linie nou (new line), caractere de formatare (formfeeds) i comentariile, numite n ansamblu spaii albe (white spaces) sunt ignorate cu excepia situaiei cnd folosesc pentru separarea entitilor. Un spaiu alb este necesar pentru separarea identificatorilor, a cuvintelorcheie i a constantelor. Comentariile din C, considerate ntre perechile de caractere /* i */ sunt valabile la fel, fr posibilitatea de a fi incluse unele n altele (imbricate). Caracterele // marcheaz nceputul unui comentariu care dureaz pn la sfritul liniei. Identificatori. Un identificator este o secven de lungime oarecare de litere i cifre, cu condiia ca primul caracter s fie liter sau caracterul de subliniere _ (underscore). Literele mici sunt diferite de literele mari corespunztoare. Toate caracterele ntr-un identificator sunt semnificative. Cuvinte-cheie. Urmtorii identificatori sunt rezervai pentru a fi utilizai ca i cuvinte-cheie, i nu pot avea alt utilizare:
asm auto break case catch char class const continuos default delete do double else enum extern float for friend goto if inline int long new operator private protected public register return short signed sizeof static struct switch template this hrow try typedef union unsigned virtual void volatile while

n plus, identificatori care conin dou caractere de subliniere (underscore) sau ncep cu un astfel de caracter sunt utilizai n implementrile C++ i a bibliotecilor i se recomand s fie evitai. Literale (constante). Constantele se refer la valori fixe, pe care programul nu le poate modifica. Exit mai multe feluri de constante: constante ntregi, constante caracter, constante reale i constante ir de caractere. O constant ntreg este reprezentat ca o succesiune de cifre; ea este considerat n baz zecimal dac prima cifr este diferit de 0, n baza hexazecimal dac ncepe cu 0x sau 0X, sau n baz octal, dac ncepe cu 0 (i acesta nu este urmat de x sau X). Tipul unei constante ntregi depinde de valoarea acesteia i este astfel stabilit nct s poat cuprinde valoarea dat. O constant caracter este compus din unul sau mai multe caractere cuprinse ntre ghilimele simple (de exemplu, x). O constant de un singur caracter este de tipul char. Constantele multicaracter au tipul ntreg. Trebuie remarcat diferena fa ce limbajul C, n care constantele caracter au tipul int. O constant real (flotant) este alctuit dintr-o parte ntreag, punctul zecimal, o parte fracionar i opional, un exponent ntreg cu semn precedat de caracterul e sau E. Fie partea ntrag, fie partea fracionar, (dar nu amndou) poate lipsi. Tipul constantei flotante este duble, dac nu conine un sufix care s specifice explicit tipul (f sau F, pentru float; l sau L pentru long double).

O constant ir de caractere este o secven de caractere cuprins ntre ghilimele duble i este de tipul vector de caractere. O constant ir de caractere are clasa de memorare static i se iniializeaz cu caracterele date, la care se adaug caracterul \0 (care are valoarea 0). Este de asemenea posibil s fie definite constante simbolice (constante cu nume). Acestea sunt prezentate n subseciunea urmtoare.

1.3 Declaraii, definiii


Un nume (name) poate reprezenta un obiect, un tip, o enumeraie, o funcie, o mulime de funcii, un membru al unei clase, o valoare sau o etichet. Un nume este introdus n program printr-o declaraie (declaration). Un nume poate fi folosit numai ntr-o zon a programului, denumit domeniul numelui (scope). Un obiect (object) este o regiune de memorie, iar semnificaia valorilor care se afl memorate ntr-un obiect depind de tipul lui. Orice obiect are o durat de via (lifetime) care depinde de clasa de memorare creia i aparine. O declaraie (declaration) introduce unul sau mai multe nume ntr-un program. ntr-o declaraie a unui nume se poate introduce, opional, o expresie de iniialzare (iniializator) pentru acel nume (de exemplu: int i = 1;) O definiie (definition) stabilete entitatea la care se refer numele respectiv. Ea rezerv cantitatea necesar de memorie obiectului respectiv i execut iniializrile corespunztoare. O declaraie este i o definiie, cu excepia urmtoarelor situaii: conine specificatorul extern fr o expresie de iniializare sau corp al funciei; este o declaraie a unei funcii fr specificarea corpului acesteia; este o declaraie a numelui unei clase; este o declaraie typedef; este o declaraie a unui membru de tip static al unei clase.

De exemplu, urmtoarele sunt definiii:


int a; extern const c = 1; int f(int x) {return x+1;} enum {up, down};

n timp ce urmtoarele sunt doar declaraii:


extern int a; extern const c; int f(int); struct S; typedef int Int;

Trebuie s existe o singur definiie a fiecrui obiect, funcie, clas sau enumeraie folosit n program. Dac o funcie nu este niciodat apelat sau dac numele unei clase nu este niciodat folosit ntr-un mod care s necesite definiia acesteia, ele pot s nu fie definite. O declaraie poate fi repetat de mai multe ori n program, n timp ce definiiile nu pot fi repertate. De exemplu:
struct S; struct S; int a; int a; // corect, redeclarare // corect, redeclarare // eroare, redefinire

Domeniul de definiie (scope) al unui nume este zona din program n care numele este cunoscut i poate fi folosit. Dat fiind c un nume este fcut cunoscut printr-o declaraie, domeniile numelor se difereniaz n funcie de locul n care este introdus declaraia n program. Exist patru categorii de domenii: local, funcie, clas i fiier. Domeniul local: un nume declarat ntr-un bloc (o secven de instruciuni cuprins ntre dou acolade) este local blocului i poate fi folosit numai n acel bloc, ncepnd din locul declaraiei i pn la sfritul bocului i n toate blocurile incluse dup punctul de declaraie. Argumentele formale ale unei funcii sunt tratate ca i cnd ar fi declarate n blocul cel mai exterior al funciei repective. Domeniul funcie: un nume declarat ntr-o funcie poate fi folosit n funcia respectiv, ncepnd din punctul de declaraie i pn la sfritul blocului n care se afl declaraia. Domeniul funcie poate fi considerat un cay particula de domeniu local. Domeniul clas: Un nume al unui membru al unei clase este local clasei respective. Posibilitile de utilizare al acestor tipuri de nume vor fi prezentate ulterior. Domeniul fiier: Un nume declarat n afara oricrui bloc sau clas are ca domeniu fiierul n care a fost declarat i poate fi utilizat din punctul declaraiei pn la sfritul fiierului. Numele cu domeniu fiier se numesc globale. Domeniu de vizibilitate. Un nume este vizibil n ntregul su domeniu de definiie dac nu este redefinit ntr-un bloc inclus n domeniul respectiv. Dac ntr-un bloc interior domeniului unui nume se redefinete (sau se redeclar) acelai nume, atunci numele iniial (din blocul exterior) este parial ascuns, i anume n tot domeniul redeclarrii. De exemplu:
void fv1() { int i = 10; { // exterior // este ascunsa datorit redefinirii int i = 100; cout << i << endl; // afiseaza 100 } cout << i << endl; } // afiseaza 10 in acest

// definitie variabila i bloc var. i din blocul

Un nume cu domeniu fiier poate fi accesat ntr-un domeniu n care este ascuns prin redefinire, dac se folosete operatorul de rezoluie pentru nume globale (operatorul :: fr nici un nume n faa lui). Redefinirea unui nume este admis numai n domenii diferite. Dac unul din domenii este inclus n cellalt domeniu, atunci redefinirea provoac ascunderea numelui din domeniul exterior n domeniul interior. Redefinirea n domenii identice produce eroare de compilare. Durata de via a obiectelor. Un obiect este creat atunci cnd se ntlnete definiia lui (care este unic) i este distrus (n mod automat) atunci cnd se prsete domeniul lui de definiie. Un obiect cu un nume global se creaz i se iniializeaz o singur dat i are durata de via pn la terminarea programului. Obiectele locale se creaz de fiecare dat cnd execuia programului ajunge n punctul de definire a acestora, cu excepia variabilelor locale declarate de tip static. O variabil (obiect) se declar de tip static prin prefixarea declaraiei acesteia cu cuvntul-cheie static. De exemplu, execuia programului:
#include <iostream.h> int a = 0; initializeaza void f(){ int b = 1; // variabila globala, se

// inainte de nceperea executiei // se creaza si se initializeaza // la fiecare apel al functiei f()

a++; b++; static int c = a; //se initializeaza o singura data //la prima executie a instruct. c += 2; cout << a = <<a << b = << b<< c =<< c<<endl; } void main (){ for (int i=0;i<3;i++) f(); }

produce mesajele:
a = 1 a = 2 a = 3 b b b = 2 = 2 = 2 c = 3 c = 5 c = 7

O variabil (obiect) global sau static, care nu este iniializat explicit, este iniializat automat cu 0. n afara acestui mod de creare a obiectelor, se mai pot crea obiecte n memoria liber (heap), a cror durat de via este controlat explicit folosind funcii sau operatori de alocare. Acest mod de creare a obiectelor este prezentat n subseciunea

2.4. Categorii de memorare. Exist patru specificatori de categorii de memorare a obiectelor: auto, static, register, extern. Obiectele care sunt declarate automatice (folosind specificatorul auto la nceputul declaraiei) sunt obiecte locale (declarate ntr-un bloc) care se iniializeaz la fiecare invocare a blocului. Utilizarea acestui specificator este redundant i rareori este folosit. O declaraie register este o declaraie auto care mai indic n plus compilatorului faptul c acel obiect este intens folosit i, ca urmare, compilatorul va ncerca s-i aloce spaiu de memorare ntr-un registru al procesorului. Un obiect global poate fi declarat o singur dat ntr-unul din fiierele programului. Pentru a fi cunoscut i n alte fiiere din program, n celelate fiiere se declar acel obiect folosind specificatorul extern. Un obiect declarat folosind specificatorul static este memorat permanent i nu este iniializat dect o singur dat. Un astfel de obiect nu este cunoscut dect n interiorul fiierului sau funciei n care este declarat i nu poate fi declarat extern. Se pot remarca cele dou semnificaii ale specificatorului static: o semnificaie se refer la domeniul de vizibilitate, pe care l restrnge la nivel de funcie, respectiv fiier; cealalt semnificaie se refer la clasa de memorare i asigur o memorare permanent a obiectului i o durat de via egal cu durata de execuie a programului. Aceast dubl aciune a specificatorului static difereniaz comportamentul unui obiect declarat static dup domeniul lui de definiie. Astfel, un obiect global de tip static este memorat permanent, dar este cunoscut numai n fiierul n care a fost declarat. n aceast situaie, specificatorul static are doar rolul de a restrnge demeniul de vizibilitate al obiectului. Pentru un obiect local specificatorul static are rolul de a modifica clasa de memorare: obiectul are domeniu de definiie local (este cunoscut numai n blocul n care a fost declarat), dar este memorat permanent i si pstreaz valorile ntre invocri succesive ale blocului respectiv.

1.4 Tipuri
Fiecare nume ntr-un program C++ are un tip asociat lui, care determin ce operaii se pot aplica entitii la care se refer acest nume. Un nume folosit pentru a specifica tipul unui alt nume ntr-o declaraie este un nume de tip. Singurele operaii care se pot aplica unui nume de tip sunt: sizeof, (care determin cantitatea de memorie necesar memorrii unuii obiect de acel tip) i new (operaia de alocare n memoria liber a unui obiect de tipul respectiv).

1.4.1 Tipuri fundamentale


n C++ sunt definite urmtoarele tipuri fundamentale:

Tipuri de ntreg, pentru definirea numerelor ntregi de diferite dimensiuni (ca numr de octei ocupai n memorie):
char short int int long 1 octet 2 octei 2 sau 4 octei 4 sau 8 octei

Tipuri de numere flotante, pentru definirea numerelor reale (reprezentate ca numere cu virgul flotant):
float double long double 4 octei 8 octei 12 sau 16 octei

Aceste tipuri sunt denumite mpreun tipuri aritmetice. Pentru tipurile ntreg, exist variante de declaraie cu semn (signed) i fr semn (unsigned). Tipul void specific o mulime vid de valori. Nu se poate declara un obiect cu acest tip, dar acest tip poate fi utilizat n conversii de pointeri i ca tip de returnare a unei funcii

1.4.2 Tipuri derivate


Se pot defini conceptual un numr infinit de tipuri derivate pornind de la tipurile fundamentale. Tipurile derivate sunt: tablouri de obiecte, pointeri la obiecte, referine, funcii, constante, clase, structuri, uniuni, pointeri la membrii claselor.

n continuare se vor prezenta primele cinci tipuri derivate, iar celelate vor fi introduse pe parcursul seciunii urmtoare. Ca terminologie, clasele (mpreun cu structurile i uniunile) sunt denumite tipuri definite de utilizator (user-defined types), iar celelate tipuri sunt denumite tipuri predefinite (built-in types)

1.4.2.1 Tablouri de obiecte Un tablou (array) de obiecte poate fi construit din obiecte dintr-un tip fundamental (cu excepia tipului void), din pointeri, din enumeraii sau din alte tablouri. n traducere, pentru array se mai ntlnesc termenii vector i matrice. n acest text sunt folosii termenii tablou (pentru array multidimensional) i vector (pentru

array unidimensional). Declaraia: T D[expresie] introduce un tablou de obiecte de tipul T, cu numele D i cu un numr de elemente al tabloului dat de valoarea expresiei, care trebuie s fie de tip constant ntreg. Pentru valoarea N a acestei expresii, tabloul are N elemente, numerotate de la 0 la N-1. Un tablou bidimensiunal se poate construi printr-o declaraie de forma:
T D[dim1][dim2];

i reprezint dim1 tablouri unidimensionale, fiecare de dimensiune dim2. Elementele tabloului bidimensional se memoreaz cu valori succesive pentru indicele din dreapta astfel:
D[0][0], D[0][1], D[0][dim2-1], D[1][0], D[1][1], D[1][dim2-1],. D[dim1-1][0], D[dim1-1][1], D[dim1-1][dim2-1].

ntr-un mod asemntor se pot construi tablouri multidimensionale, cu o limitare a numrului de dimensiuni care depinde de impelementare. 1.4.2.2 Pointeri Pentru majoritatea tipurilor T, T* este un tip denumit pointer la T, adic o variabil de tipul T* memoreaz adresa unui obiect de tipul T. Operaia fundamental asupra unui pointer este operaia de derefereniere (dereferencing), adic accesarea obiectului a crui adres o reprezint pointerul respectiv. Operatorul de derefereniere este operatorul unar *. De exemplu:
char c1 = a; char* p1 = &c1; char c2 = *p1; // p memoreaz adresa lui c1 // dereferentiere, c2 = a;

Operatorul & este operatorul adres, care se utilizeaz pentru a se obine adresa unei variabile. Tipul void* este folosit pentru a indica adresa unui obiect de tip necunoscut. Asupra pointerilor sunt admise unele operaii aritmetice. Ca exemplu, se consider un vector de caractere dintre care ultimul este caracterul 0 (se mai numete ir de caractere terminat cu 0). Pentru calculul numrului de caractere se pot folosi operaii cu pointeri astfel:
int strlen(char* p){ int i = 0; while (*p++) i++; return i; }

Funcia strlen() returneaz numrul de caractere ale irului, fr caracterul terminal 0, folosind operaia de incrementare a pointerului i operaia de derefereniere pentru a testa valoarea caracterului. O alt implementare posibil a

funciei este urmtoarea:


int strlen(char* p){ char* q = p; while (*q++); return q-p-1; }

n C++, ca i n limbajul C, pointerii i tablourile sunt puternic corelate. Un nume al unui tablou poate fi folosit ca un pointer la primul element al tabloului. De exemplu, se poate scrie:
char alpha[] = abcdef; char* p = alpha; char* q = &alpha[0];

Rezultatul aplicrii operatorilor aritmetici +, -, ++, -- asupra pointerilor depinde de tipul obiectului indicat. Atunci cnd se aplic un operator aritmetic unui pointer p de tip T*, se consider c p indic un element al unui tablou de obiecte de tip T; p+1 va indica urmtorul element al tabloului, iar p-1 va indica elementul precedent al tabloului. Acest lucru nseamn c valoarea lui p+1 este cu sizeof(T) octei mai mare dect valoarea lui p. Pointeri la funcii vor fi prezentai la paragraful de descriere a funciilor. Pointerii la membrii nestatici ai claselor nu sunt considerai pointeri la obiecte i vor fi prezentai n seciunea urmtoare. 1.4.2.3 Referine O referin (reference) este un nume alternativ al unui obiect. Utilizarea principal a referinelor este n specificarea argumentelor i a valorilor returnate de funcii, n general i pentru suprancrcarea operatorilor n special. Notaia X& nseamn referin la obiectul cu numele X. De exemplu:
int i = 1; int& r = i; int x = r; r++; // r i i se refer la aceeai entitate // x = 1 // i = 2;

Implementarea obinuit a unei referine este printr-un pointer constant care este derefereniat de fiecare dat cnd este utilizat. Aa cum se poate observa, pentru definirea unei referine se folosete operatorul adres &, dar difer tipul construciei n care este folosit. De exemplu:
int a = 5; int* pi = &a; int& r = a; // & calculeaz adresa; // pi este adresa lui a // & introduce o referinta;

// r este o referin (alt nume) pt. a

O referin este utilizat ca argument pentru o funcie care poate s modifice valoarea acestui argument. De exemplu:
void incr(int& x) {x++;} void f(){ int i = 1; incr(i); // i = 2; }

O alt utilizare important a referinelor este pentru definirea funciilor care pot fi folosite att ca membru drept ct i ca membru stng al unei expresii de asignare. De exemplu:
#include <iostream.h> int& fr(int v[], int i){ return v[i]; } void main(){ int x[] = {1,2,3,4}; fr(x,2) = 7; cout <<fr(x,0)<<fr(x,1)<<fr(x,2)<< fr(x,3)<<endl; }

La execuia acestui program se obine mesajul:


1 2 7 4

Deoarece valoarea returnat de funcie este referina (numele) unui element al vectorului, acesta poate fi modificat prin folosirea funciei fr() ca membru stng al egalitii. 1.4.2.4 Funcii Funciile sunt tipuri derivate i reprezint una din cele mai importante caracteristici ale limbajelor C i C++. Forma general de definire a unei funcii este:
tip_returnat nume_func(tip1 arg1,tip2 arg2,,tipn argn) { //corpul functiei }

Funcia cu numele nume_func returneaz o valoare de tip tip_returnat i are un numr n de argumente formale declarate ca tip i nume n lista de argumente formale. Argumentele formale din declaraia unei funcii se mai numesc i parametrii funciei. Dac o funcia nu are argumente, atunci lista din parantezele rotunde este vid. Notaia din C: f(void) este redundant.

O funcie este un nume global, dac nu este declarat de tip static. O funcie declarat static are domeniul de vizibilitate restrns la fiierul n care a fost definit. Corpul funciei este propriu ei i nu poate fi accesat din afara acesteia (nici printr-o instruciune goto). Corpul unei funcii este o instruciune compus, adic o succesiune de instruciuni i declaraii incluse ntre acolade. n corpul funciei se pot defini variabile, care sunt locale i se memoreaz n segmentul de stiv al programului. Dac nu sunt declarate de tip static, variabilele locale se creaz la fiecare apel al funciei i se distrug atunci cnd este prsit blocul n care au fost definite. Nu se pot defini funcii n interiorul unei funcii. Argumentele formale ale unei funcii sunt considerate variabile locale ale funciei, ca orice alt variabil definit n funcia respectiv. Dac o funcie nu are de returnat nici o valoare, atunci tip_returnat din declaraia funciei este tipul void, i nu este necesar o instruciune de returnare (return) n funcie. n toate celelalte cazuri, n corpul funciei trebuie s fie prevzut returnarea unei variabile de tipul tip_returnat, folosind instruciunea return. Dac n definiie nu este prevzut un tip_returnat, se consider implicit returnarea unei valori de tip ntreg. Prototipurile funciilor. Pentru apelul unei funcii este necesar cunoaterea definiiei sau a prototipului acesteia. Prototipul unei funcii este de forma:
tip_returnat nume_func(tip1 arg1,., tipn argn);

Numele argumentelor formale sunt opionale n prototipul unei funcii. Prototipurile permit compilatorului s verifice tipurile argumentelor de apel i s semnaleze eroare la conversii ilegale. Spre deosebire de limbajul C, unde este admis i simpla declaraie a numelui funciei (fr tipurile argumentelor de apel), utilizarea prototipurilor este obligatorie n C++. De exemplu:
double f2(int, double); // prototip functie f2 double f2(int a, double f){ // definitie functie f3 /*..*/ double t = f/a; return t; } void fp(){ double r1 = f1(7, 8.9); // eroare, // identificator nedeclarat double r2 = f2(7, 8.9); // corect, fol. prototipul char str[] = "abcde"; double r3 = f3(7, str); // eroare de tip argument } double f1(int a, double f){ /*..*/ double t = a + f; return t; } double f2(int a, double f){

/*...*/ double t = a*f; return t; }

La compilare apare o eroare datorit apelului funciei f1(), care nu este definit, nici declarat prin prototip n domeniul funciei apelante fp() i o eroare datorat apelului funciei f3() cu un argument (argumentul al doilea) care nu poate fi convertit la tipul argumentului formal. Transferul argumentelor funciilor. La apelul unei funcii, argumentele reale (se mai numesc i efective) de apel iniializeaz argumentele formale din declaraia funciei, n ordinea din declaraie. Argumentele unei funcii se pot transfera n dou moduri: apelul prin valoare i apelul prin referin. n apelul prin valoare se copiaz valoarea argumentului real n argumentul formal corespunztor al funciei. n acest caz, modificrile efectuate asupra argumentului funciei nu modific argumentul real. n apelul prin referin este accesat direct variabila din argumentul real transmis funciei, care poate fi deci modificat. Ca exemplificare, se presupune c se dorete o funcie swap() care s realizeze intershimbul ntre valorile a dou variabile. Dac nu se folosesc referine, argumentele de apel ale funciei trebuie s fie pointeri la variabilele respective, pentru a fi accesate i modificate. Funcia cu argumente pointeri arat astfel:
void swap(int* x, int* y){ int t; t = *x; // dereferentiere *x = *y; *y = t; }

Aceeai funcie swap(), folosind argumente de tip referin, arat astfel:


void swap(int& x, int& y){ int t; t = x; x = y; y = t; }

Se poate observa perfecta simetrie ntre cele dou implementri i c, n mod evident, referina folosete adresa variabilei pentru a o putea modifica (deci un pointer). Dar, n cazul referinelor, pointerul i defererenierea sunt ascunse, programatorul nu trebuie s le introduc explicit, programul rezultat este mai concis i mai clar. Referinele sunt deosebit de utile n apelul funciilor a cror argumente sunt obiecte de dimensiuni mari i copierea lor n argumentele formale (plasate n segmentul stiv al programului) ar fi foarte ineficient.

Argumente implicite ale funciilor. Se ntmpl frecvent ca o funcie s aib un numr mai mare de argumente dect sunt necesare n cazurile simple dar frecvente de apel. Dac nu este necesar s fie transmis ntotdeauna valoarea real a unui argument i acesta poate lua, de cele mai multe ori, o valoare implicit, atunci n declaraia funciei se prevede o expresie de iniializare a acestui argument, iar din apel poate s lipseasc valoarea argumentului corespunztor. De exemplu, o funcie pentru stabilirea datei calendaristice, care prevede valori implicite pentru argumentele luna i an:
struct data{ int zi; int luna; int an; } g_data; void setdata(int zi, int luna=9, int an =1999){ g_data.zi = zi; g_data.luna = luna; g_data.an = an; } void main(){ setdata(15); // 15 9 1999 setdata(21,7); // 21 7 1999 setdata(20,1,2000); // 21 1 2000 }

Numai argumentele de la sfritul listei pot fi argumente implicite. De exemplu, este eronat urmtoarea declaraie:
void setdata(int zi, int luna=9, int an); // eroare

Pointeri la funcii. Dat fiind c o funcie are o localizare n memorie, aceast valoare poate fi atribuit unui pointer. Adresa unei funciei este punctul de intrare n funcie i, de aceea, funcia poate fi apelat folosind un pointer. Tipul unui pointer la o funcie se definete folosind tipul valorii returnate i tipurile argumentelor formale ale funciei. nainte de apelul unei funcii prin pointer, trebuie s fie definit valoarea pointerului folosind numele funciei. De exemplu:
float func1(int x, float y){ return x + y; } float func2(int x, float y){ return x * y; } void main(){ float (*pf)(int, float); // definire tip pointer pf = func1; // definire valoare pointer float z = (*pf)(3, 1.2f); // apel functie

cout << z << endl; // afiseaza 4.2 pf = func2; // definire valoare pointer z = (*pf)(3, 1.2f); // apel functie cout << z << endl; // afiseaza 3.6 }

Dei din acest exemplu simplu nu reiese care ar putea fi avantajul folosirii apelului prin pointer a unei funcii, exist totusi situaii cnd apelul prin pointeri este mai avantajos. De exemplu, poate s fie nlocuit o instruciune switch care selecteaz dintre mai multe apeluri de funcii, cu un vector de funcii care se pot apela prin pointerii corespunztori. Suprancrcarea funciilor. n limbajul C, fiecare funcie definit n program trebuie s aib un nume diferit. n C++, mai multe funcii pot avea acelai nume n acelai domeniu de definiie, dac se pot diferenia prin numrul sau tipul argumentelor de apel. Acest mecanism se numete suprancrcarea funciilor (function overloading). n traducere, se mai ntrebuineaz i denumirile de suprapunere, redefinire, supradefinire; n textul de fa este preferat termenul de suprancrcare. Suprancrcarea se poate aplica att funciilor ct i operatorilor; n seciunea aceasta este prezentat suprancrcarea funciilor, iar suprancrcarea operatorilor este descris n seciunea 4. Dac n acelai domeniu sunt definite mai multe funcii cu acelai nume, la fiecare apel se selecteaz funcia corect prin compararea tipurilor argumentelor reale de apel cu tipurile argumentelor formale ale funciei. De exemplu,
double abs(double); int abs(int); abs(1); abs(1.0); // apeleaza abs(int) // apeleza abs(double)

Acceptarea mai multor versiuni ale unei funcii cu acelai nume este condiionat de posibilitatea selectrii fr ambiguitate a uneia dintre acestea dup tipul sau numrul argumentelor. Nu este admis ca funciile s difere doar prin tipul returnat. Dou funcii declarate cu acelai nume se refer la aceeai funcie dac sunt n acelai domeniu i au numr i tipuri identice de argumente. O funcie declarat local nu este n acelai domeniu cu o funcie cu domeniul la nivel de fiier. De exemplu:
int f(char*); void g(){ extern f(int); f(abcd); // eroare, f(int) ascunde f(char*) // deci nu exista f(char*) n acest domeniu }

Situaii asemntoare, de ascundere i nu de suprancrcare n domenii diferite vor mai fi prezentate n seciunea 5, dedicat claselor derivate. Selecia funciei la apel se bazeaz pe cea mai bun potrivire a argumentelor (best argument matching). Dac printre funciile suprancrcate exist o funcie care are toate argumentele cu tipuri identice cu argumentele din apel, atunci aceast funcie prezint cea mai bun potrivire a argumentelor i este selectat. Se consider de exemplu funcia suprancrcat de calcul a ridicrii la o putere a unui numr, funcia power():
int power(int, int); long power(long,int); double power(double, int); double power(double, double);

n apelurile:
power(7, 8); power(5.6, 2); power(6.7, 2.5); // selecteaza power(int, int) // selecteaza power(double, int) // selecteaza power(double, double)

versiunea funciei power() este selectat pe baza coincidenei dintre argumentele de apel i argumentele formale. n cazul n care nu exist o astfel de coinciden, se fac conversii ale argumentelor efective (de apel) ctre tipul argumentelor formale. Compilatorul ncearc cea mai bun potrivire pentru fiecare argument n parte prin conversii pentru tipuri predefinite care s nu conduc la trunchieri (pierderi de informaie). De exemplu:
long a = 100; int b = 10; float c = 2.3f; float d = 5.6f; double e = 7.0; double f = 2; power(a,b); power((int)a, b); power(c,a); power(c,d); power(e,d);

// // // // //

selecteaza selecteaza selecteaza selecteaza selecteaza

power(long, int) power(int, int) power(double, int) power(double, double) power(double, double)

Dac prin astfel de conversii nu se poate stabili cea mai bun potrivire a argumentelor, atunci sunt ncercate i conversii pentru tipuri definite de utilizatori. Apelul este acceptat numai dac selecia unei versiuni a funciei (pe baza criteriului de cea mai bun potrivire a argumentelor) este unic. Conversiile ntre date de tipuri definite de utilizatori sunt prezentate n seciunea 4, dedicat suprancrcrii operatorilor. 1.4.2.5 Constante simbolice

O constant simbolic (sau constant cu nume) este un nume a crui valoare nu poate fi modificat n cursul programului. n C++ exist trei modaliti de a defini constante simbolice: Orice valoare, de orice tip care poate primi un nume, poate fi folosit ca o constant simbolic prin adugarea cuvntului-cheie const n declaraia acesteia. Orice nume de funcie sau de tablou este o constant simbolic. O mulime de constante ntregi poate fi definit ca o enumeraie.

De exemplu, urmtoarele declaraii introduc constante simbolice prin folosirea cuvntului-cheie const:
const int val = 100; const double d[] = {1.2, 2.8, 9.5};

Deoarece constantele nu pot fi modificate, ele trebuie s fie iniializate n declaraie. ncercarea de modificare ulterioar este detectat ca eroare n timpul compilrii:
val++; d = 200; // eroare // eroare

Cuvntul-cheie const modific tipul obiectului, restricionnd modul n care acesta poate fi folosit. Un aspect mai aparte i intens folosit n programare, este acela de a declara pointeri la constante. Atunci cnd se folosete un pointer, sunt implicate dou obiecte: pointerul nsui i obiectul ctre care indic pointerul. Prin prefixarea declaraiei unui pointer cu cuvntul const, obiectul indicat este fcut constant, nu pointerul nsui. De exemplu:
const char* pc = abcd;// pc este pointer la o constant pc[2] = m; // eroare, nu se poate modifica // obiectul indicat pc = ghij; // corect, este modificat // valoarea pointerului pc++; // corect

Pentru ca pointerul nsui s fie constant, se folosete operatorul *const:


char *const cp = abcd;// cp este pointer constant; cp[2] = m; // corect, modifica valoarea cp = ++; // eroare, pointer constant

Posibilitatea de declarare a pointerilor la constante este intens folosit n transmiterea argumentelor funciilor. Prin declararea unui argument de tip pointer la constant, este interzis modificarea de ctre funcie a obiectului indicat. De exemplu:
char* strcpy(char* d, const char* s);

n aceast funcie irul s nu poate fi modificat. n mod asemntor, specificatorul const care nsoete un argument tip referin la apelul unei funcii, mpiedic modificarea acestuia de ctre funcia respectiv. Enumeraii. O alt metod de a defini constante ntregi este prin enumeraie. De exemplu, enumeraia:
enum { INPUT, OUTPUT, INOUT, };

este echivalent cu declararea urmtoarelor constante:


const INPUT = 0; const OUTPUT = 1; const INOUT = 2;

Valorile asignate implicit constantelor ncep de la 0 i sunt n ordine cresctoare. Enumeraiile pot avea nume. De exemplu:
enum states{ good, bad, fail }; void fe (states st ){ if (st != good){ //. } }

Un nume declarat de tipul unei enumeraii poate lua orice valoare particular cuprins n enumeraie. n sfrit, este posibil iniialzarea elementelor unei enumeraii:
enum states{ good = 0x00, bad = 0x01, fail = 0x10 };

Valorile de iniializare nu trebuie neaprat s ia valori succesive. 1.4.2.6 Specificatorul typedef Declaraiile care conin cuvntul-cheie typedef declar identificatori care pot fi folosii pentru denumirea tipurilor fundamentale sau derivate n domeniul de

definiie al acestuia. O declaraie care conine specificatorul typedef nu introduce nume noi n program, ci realizeaz o echivalen sintactic a unui nou nume cu un alt nume, fundamental sau derivat. De exemplu, n declaraiile:
typedef int KM, *PKM; KM dist; PKM pd;

KM este de tip ntreg, iar pd este pointer la ntreg. O structur fr nume definit printr-un specificator typedef capt numele introdus de acesta:
typedef struct { /**/} S; //structura este denumit S. S struct1;

Acesta este modul obinuit de declaraie n C, unde numele unei structuri nu poate fi folosit ca un nume de tip fr s fie nsoit de cuvntul-cheie struct. Acest stil este admis i n C++, pentru a se asigura compatibilitatea. Dar stilul mai normal pentru C++ este de a declara o structur cu nume i fr typedef:
struct SC{/*...*/}; SC struct2;

1.4.2.7 Specificatorul volatile Specificatorul volatile indic faptul c valoarea obiectului poate fi modificat pe alte ci dect cele declarate explicit n program. De exemplu, adresa unei variabile globale poate fi transmis unei rutine de interfa a sistemului, care modific valoarea acesteia, fr o instruciune explicit din program. Acest lucru este important, deoarece majoritatea compilatoarelor de C i C++ optimizeaz automat evaluarea expresiilor prin presupunearea c o variabil rmne neschimbat atta timp ct nu apare n partea stng a unei operaii de asignare. Specificatorul volatile mpiedic aplicarea unor astfel de optimizri. 1.4.2.8 Clase, structuri, uniuni Pe lng tipurile de date fundamentale ale limbajului, n C++ se pot defini tipuri de date noi prin definirea unor clase. Aceste tipuri se numesc tipuri definite de utilizator (user-defined types) i se pot utiliza ntr-un mod asemntor utilizrii tipurilor predefinite. Aceste tipuri mai sunt denumite i tipuri de date abstracte (abstract data types), dar n aceast lucrare s-a preferat termenul de tip definit de utilizator, aa cum l prefer autorul limbajului [Stroustrup 94]. De exemplu, declaraia:
class X{ // corpul clasei X // declaraii de date i funcii membre

};

este i o definiie a clasei X, care introduce un tip nou de date. Prin aceast definiie se asociaz numele X cu entitatea definit n corpul clasei. Dup definirea unui astfel de tip de date, se pot declara (defini) obiecte de tipul respectiv, la fel ca i variabilele (obiecte) de tipuri predefinite:
X obx1; // obiect obx1 de tipul X

Clasele, structurile i uniunile n C++, precum i pointeri la membrii claselor, vor fi prezentate detaliat n seciunea urmtoare. n acest punct au fost introduse doar pentru precizrile legate de tipuri, declaraii i definiii.

1.5 Expresii i operatori


O expresie este o secven de operatori i operanzi care specific executarea unor calcule. O expresie poate produce un rezultat sau efecte laterale (side effects). n C++ operatorii pot fi suprancrcai, adic li se poate atribui un anumit mod de operare atunci cnd se aplic tipurilor definite de utilizator (clase). Operatorii suprancrcai urmeaz aceleai reguli sintactice ca i operatorii normali, dar operaiile efectuate pot s difere de ale acestora. Suprancrcarea operatorilor nu poate modifica regulile de aplicare ale acestora asupra tipurilor pentru care ei sunt definii n limbaj. Ordinea de evaluare a unei subexpresii este determinat de precedena (precedence) i gruparea operatorilor. Regulile matematice obinuite de asociativitate i comutativitate se aplic pentru acei operatori care sunt n mod real asociativi sau comutativi. O expresie care se refer la un obiect sau funcie este denumit valoare stnga (left value, prescurtat lvalue). Aceast denumire provine din faptul c o astfel de valoare poate fi folosit ca membru stng al unei expresii de asignare: E1 = E2. Operandul E1 trebuie s fie expresie lvalue. n expresii, unii operatori produc expresii lvalue, alii necesit expresii lvalue. O expresie lvalue este modificabil dac nu este numele unei funcii, numele unui tablou sau constant.

1.5.1 Operatori specifici C++


Majoritatea operatorilor C++ sunt preluai din limbajul C, cu aceeai sintax i reguli de operare. n plus fa de operatorii C, n C++ mai sunt introdui urmtorii operatori: operatorul de rezoluie (::), operatorul de lansare excepie (throw), operatorii de alocare-eliberare dinamic a memoriei new i delete.

Operatorul de lansare excepie (throw) este descris n seciunea 8. Ceilali operatori sunt descrii n continuare.

1.5.1.1 Operatorul de rezoluie Operatorul de rezoluie (::) este folosit pentru modificarea domeniului de vizibilitate al unui nume. Pentru acest operator (scope resolution operator), n traduceri se mai ntlnesc termenii de operator de domeniu sau operator de acces. Operatorul de rezoluie permite folosirea unui identificator ntr-un bloc n care el nu este vizibil. Dac operatorul de rezoluie nu este precedat de nici un nume de clas, atunci este accesat numele global care urmeaz acestui operator. De exemplu:
int g = 10; int f(){ int g = 20; return ::g; } void main(){ cout << f() << endl; // afiseaza 10 }

n acest exemplu operatorul de rezoluie a fost folosit pentru a accesa variabila global g, ascuns de variabila local cu acelai nume din funcie. Deoarece utilizarea cea mai extins a operatorului de rezoluie este legat de utilizarea claselor, el va fi reluat pe parcursul seciunilor urmtoare. 1.5.1.2 Operatorii de alocare-eliberare dinamic a memoriei. n limbajul C se pot aloca dinamic zone de memorie n memoria liber ( heap) folosind funcii de bibliotec (de exemplu, malloc(), calloc(), realloc() ) i se pot elibera folosind funcia free(). La aceste posibiliti, care se pstreaz n continuare n C++, se adaug posibilitatea folosirii operatorilor de alocare i eliberare dinamic a memoriei, new i delete. Aceti operatori unari prezint avantaje substaniale fa de funciile de alocare din C i de aceea sunt n mod evident preferai n programele scrise n C++. Pentru alocarea unei singure date (obiect), operatorul new are urmtoarea form general:
tip_data* p = new tip_data(initializare);

unde tip_data este un tip de date predefinit sau definit de utilizator (clas), p este pointerul (adresa de nceput) a zonei alocate n memoria heap, returnat la execuia operatorului new, iar initializare este o expresie care depinde de tipul datei i permite iniializarea zonei de memorie alocate. Dac alocarea nu este posibil, pointerul returnat este NULL. Forma de utilizare a operatorului new pentru alocarea unui vector de date (tablou unidimensional) de dimensiune dim, este urmtoarea: tip_data* p = new tip_data[dim];

La alocarea unui vector nu se poate transmite o expresie de iniializare a zonei de memorie alocat. Operatorul delete elibereaz o zon din memoria heap. El poate avea una din urmtoarele forme:
delete p; delete []p;

Prima form se utilizeaz pentru eliberarea unei zone de memorie ocupat de o singur dat (obiect), nu un vector. Pointerul p este un pointer la o zon de memorie alocat anterior printr-un operator new. Operatorul delete trebuie s fie folosit doar cu un pointer valid, alocat numai cu new i care nu a fost modificat sau nu a mai fost eliberat zona de memorie mai nainte (cu un alt operator delete sau prin apelul unei funcii free()). Folosirea operatorului delete cu un pointer invalid este o operaie cu rezultat nedefinit, cel mai adesea producnd erori de execuie grave. Cea de-a doua form a operatorului delete[] se folosete pentru eliberarea unei zone de memorie ocupat de un vector de date. Pentru tipurile de date predefinite ale limbajului, se poate folosi i prima form pentru eliberarea unui vector, dar, n cazul obiectelor de tipuri definite de utilizator, acest lucru nu mai este valabil. Aceast situaie va fi detaliat n seciunea urmtoare. Cteva exemple de utilizare a operatorilor new i delete:
int *pi = new int(3); double *pd = new double; char *pc1 = new char[12]; char *pc2 = new char[20]; delete pi; delete pd; delete pc1; delete []pc2; // // // // alocare int i iniializare alocare double neinitializat vector de 12 caractere vector de 20 caractere

n legtur cu existena celor dou modaliti de alocare dinamic, prin operatorii new-delete sau prin funciile de bibliotec malloc-free, fr s fie o regul precis, combinarea acestora este bine s fie evitat, deoarece nu exist garania compatibilitii ntre ele.

1.5.2 Precedena operatorilor


n tabelul urmtor sunt prezentai concis operatorii din limbajul C++ grupai n ordinea precedenei lor. n fiecare compartiment sunt trecui operatorii cu aceeai preceden. Un operator dat are o preceden mai ridicat dect un altul dintr-un compartiment aflat mai jos n tabel. Operatorii se aplic n ordinea precedenei lor. De exemplu, a+b*c nseamn a+(b*c), deoarece nmulirea (*) are o preceden mai ridicat dect adunarea (+).

Operatori C++

:: :: . -> [] () () sizeof sizeof ++ ++ --~ ! + & * new delete delete[] () .* ->* * / % + << >>

operator rezoluie nume global selecie membru selecie membru indexare apel funcie conversie explicit dimensiune obiect dimensiune tip post incrementare pre incrementare post decrementare pre decrementare complement negaie minus unar plus unar adres derefeniere alocare eliberare(dezalocare) eliberare tablou conversie cast selecie membru selecie membru nmulire mprire modulo adunare scdere depl. logic stnga depl. logic dreapta

nume_clas::membru ::nume obiect.membru pointer->membru pointer[expr] expr (lista_expr) tip(list_expr) sizeof expr sizeof (tip) lvalue++ ++lvalue lvalue---lvalue ~expr !expr -expr +expr &lvalue *expr new tip delete pointer delete[]pointer (tip)expr obiect.*pointer_la_membru pointer->*pointer_la_membru expr * expr expr / expr expr % expr expr + expr expr - expr expr << expr expr >> expr

< <= > >= == != & ^ | && || ?: = *= /= %= += -= <<= >>= &= |= ^= throw ,

mai mic mai mic sau egal mai mare mai mare sau egal egal diferit AND orientat pe bii XOR orientat pe bii OR orientat pe bii AND logic (I) OR losic (SAU) expresie condi. asignare simpl nmulire i asignare mprire i asignare modulo i asignare adunare i asignare scdere i asignare depl. stg i asignare depl. dr. i asignare AND i asignare OR i asignare XOR i asignare lansare excepie virgul(secveniere)

expr < expr expr <= expr expr > expr expr >=expr expr == expr expr != expr expr & expr expr ^ expr expr | expr expr && expr expr || expr expr ? expr : expr lvalue = expr expr *= expr expr /= expr expr %= expr expr += expr expr -= expr expr <<= expr expr >>= expr expr &= expr expr |= expr expr ^= expr throw expresie expr, expr

1.6 Conversii standard


Unii operatori pot provoca conversia valorii unui operand de la un tip la altul, n funcie de tipul operanzilor. Conversiile standard executate pentru tipurile predefinite n C++ sunt identice cu cele din limbajul C i de aceea vor fi descrise foarte succint. Astfel de conversii intervin n urmtoarele situaii: Conversia unor operanzi de tipuri diferite ntr-o expresie aritmetic.

Conversia unui argument de apel al unei funcii la tipul argumentului formal corespunztor. Conversia valorii returnate de o funcie din tipul folosit n instruciunea return n tipul din definiia funciei.

n toate aceste situaii se respect cteva reguli de conversie care sunt descrise n continuare.

1.6.1 Conversiile aritmetice


Atunci cnd ntr-o expresie sunt combinate constante i variabile de tipuri diferite, ele sunt convertite la acelai tip, n general n tipul cel mai mare, aciune care se numete promovarea tipului. Mai nti toate valorile de tip char i short int sunt convertite n int (promovare la ntreg). Dup aceasta, toate celelate conversii sunt efectuate dup urmtorul algoritm:
if (un element este long double) urmtorul este convertit n long double else if (un element este double) urmtorul este convertit n double else if (un element este float) urmtorul este convertit n float else if (un element este unsigned long) urmtorul este convertit n unsigned long else if (un element este long int i urmtorul este unsigned int){ if( long int poate reprezenta toate valorile unui unsigned int) unsigned int este convertit n long int else amndou sunt convertite n unsigned long int } else if (un element este unsigned int) urmtorul este convertit n unsigned int

Dup aplicarea acestor reguli de conversie, fiecare pereche de elemente este de aceslai tip, iar rezultatul fiecrei operaii este de acelai tip cu cel al ambelor elemente.

2.5.1 Conversia pointerilor


Urmtoarele conversii standard se aplic pointerilor: O expresie constant evaluat la zero este convertit ntr-un pointer, numit pointer nul. Un pointer nul nu este neaprat reprezentat ca un ntreg cu valoarea 0.

Un pointer la orice valoare neconstant i nevolatil poate fi convertit n void*. Un pointer la funcie poate fi convertit n void*, cu condiia ca un astfel de pointer s aib capacitate de reprezentare suficient de mare. Numele unui tablou poate fi convertit n pointer la primul element al tabloului.

Conversiile ntre pointerii claselor de baz i derivate, precum i conversia ntre referine la clase de baz i derivate sunt prezentate n seciunea 5. Alte conversii se obin prin: Iniializarea obiectelor de tipuri definite de utilizator (clase); Suprancrcarea operatorului de conversie.

Acestea sunt descrise n seciunile urmtoare. Conversiile ntre tipurile de date din C++ se supun principiului tipizrii propriu modelului obiect. Se poate observa faptul c C++ este un limbaj hibrid din punct de vedere al tipizrii: sunt verificate conversiile ntre tipurile de date i sunt rejectate acele conversii considerate neadmisibile. Pe de alt parte, limbajul admite forarea conversiilor (prin operatorul de conversie cast), situaie n care este inhibat verificarea de tip a operanzilor. Dar aceast forare se face pe rspunderea programatorului, care are posibilitatea s le prevad numai pe acelea care le consider corecte.

1.7 Fiierele componente ale unui program C++


Orice program C++ care nu este foarte simplu este alctuit din mai multe uniti de compilare, numite convenional fiiere. Din punct de vedere al limbajului C++, un fiier reprezint un domeniu de definiie (domeniul fiier), care este domeniul pentru funciile globale de tip static i inline i pentru variabilele globale de tip static. Un fiier este, de asemenea, o unitate de memorare n sistemul de fiiere i o unitate de compilare (un modul). Dezvoltarea unui program ntr-un singur fiier este practic imposibil, deoarece sunt apelate funcii din biblioteci i funcii ale sistemului de operare care sunt memorate n mai multe fiiere. Chiar i partea de program scris de utilizator este inconvenabil s fie toat cuprins ntr-un singur fiier, datorit dificultii de organizare i de evideniere a diferitelor pri al programului. Mai mult, dac programul este curins ntr-o singur unitate de compilare, orice modificare trebuie urmat de recompilarea ntregului fiier. n organizare pe mai multe fiiere a unui program, este necesar ca programatorul s prevad declaraii care s permit analiza de ctre compilator a fiecrei uniti de compilare luat izolat dar, n acelai timp o utilizare unitar a numelor i a tipurilor definite. Orice sistem de programare permite o astfel de organizare i legare a unitilor compilate separat, n principal prin programul de

linkare (linker).

1.7.1 Linkarea modulelor


Dac nu este specificat altfel, un nume care nu este local (definit ntr-o funcie sau clas) trebuie s se refere la aceeai entitate n oricare din unitile de compilare (fiiere) ale programului, adic trebuie s existe o singur funcie, valoare, tip sau obiect nelocal cu acelai nume. De exemplu, se consider fiierele:
// fisier1.cpp int x = 0; void f(){/* corpul functiei*/ //fisier2.cpp extern int x; void f(); int g() { x = f();}

Variabila x i funcia f() utilizate n funcia g() din fiierul fisier2.cpp sunt definite n fisier1.cpp; cuvntul-cheie extern din declaraia variabile x n fisier2.cpp este doar o declaraie, nu o definiie. La fel, prototipul funciei f()este declarat n fiier2.cpp, iar definiia acesteia se gsete n fiier1.cpp. Erorile de programare care se refer la definiii multiple (n fiiere diferite), la lipsa definiiilor sau la neconcordana ntre definiii i declaraii sunt detectate i semnalate de linker. Numele declarate static sunt locale n fiecare dintre fiiere. Obiectele sau funciile cu nume cunoscute numai ntr-un fiier au linkare intern (internal linkage) . Obiectele sau funciile cu nume care sunt cunoscute n mai mult de un fiier au linkare extern (external linkage). Numele tipurilor (numele claselor i ale enumeraiilor) nu au nici o linkare, dar ele trebuie s fie definite n mod unic n program.

2.5.2 Fiiere antet


Pentru a se asigura consistena declaraiilor ntr-un program compus din mai multe fiiere se folosesc fiiere antet (header files) care conin informaii de interfa ntre unitile programului i sunt incluse n fiierele surs. Includerea unui fiier (cel mai probabil fiier antet) ntr-un alt fiier surs se specific prin directiva:
#include nume_fisier

La ntlnirea acestei directive, se nlocuiete aceast linie surs cu ntreg fiierul nume_fisier. Numele fiierului inclus se ncadreaz ntre ghilimele dac este n directorul curent sau ntre paranteze ascuite <> dac este n directorul standard de includeri. Fiierele antet au extensia .h, iar celelate fiiere surs au una din extensiile admise de compilatorul respectiv (.C, .cpp, .cxx, .cc). Includerea fiierelor antet n fiierele surs produce recompilarea acestora la compilarea fiecrui fiier surs. Acest aspect poate s nu influieneze prea mult

eficiena de compilare, deoarece fiierele antet conin n general declaraii care nu necesit analiz intens n compilare. Se poate, de asemenea ca sistemul de programare s permit utilizarea fiierelor antet precompilate. Un fiier antet poate conine:
Directive de includere Macro definiii Definiii de tipuri Definiii de constante Enumeraii Definiii funcii inline Definiii template Declaraii de date Declaraii de nume Declaraii de funcii Comentarii #include antet2.h #define BOOL int struct point {double x, y;}; const double epsilon = 0.1; enum state{good, false}; inline char get(char*p) {return *p++;} template<class T> class V{/* */}; extern int x; class X; extern int func(char c); /* comentariu */

Nu se introduc n fiierele antet:


Definiii de funcii normale char get(char *p) {return *p++;} */};

Definiii de date int x; Definiii tablouri constante const int tb[] = {/*

Acestea nu sunt reguli impuse cu strictee, ci sugestii pentru un mod rezonabil de utilizare a fiierelor antet. Numrul de fiiere antet ale unui program poate s varieze de la unul singur (pentru programele de dimensiuni reduse) la mai multe fiiere antet, n general cte un fiier antet pentru fiecare fiier surs. Posibilitatea de mprire a unui program C++ n mai multe module care pot fi compilate separat, dar care au conexiuni cu alte module ale programului, reprezint caracteristica de modularitate a limbajului, care respect n acest fel principiul modularitii, propriu modelului obiect.

Exerciii propuse
E1.1 S se rescrie urmtoarea instruciune for printr-o instruciune echivalent while: for (i = 0; i < max_length; i++) if (line[i] == ?) count++; S se dea o nou implementare folosind un pointer.

E1.2 S se rescrie funciile strlen(), care returneaz lungimea unui ir, strcpy(), care compar dou iruri i strcpy() care copiaz un ir n alt ir. S se considere ce fel de tipuri de argumente sunt necesare, dup aceea s se compare cu versiunea standard declarat n <string.h>. E1.3 S se scrie o funcie strcat() care concateneaz dou siruri. Se va folosi operatorul new pentru alocarea spaiului necesar rezultatului. E1.4 S se scrie o funcie strrev() care inverseaz poziia caracterelor ntr-un ir dat. E1.5 S se scrie o funcie atoi() care returneaz valoare de tip ntreg a unui ir de cifre zecimale. S se scrie o funcie itoa() care genereaz irul de caractere care reprezint un numr ntreg dat. E1.6 S se scrie declaraiile pentru urmtoarele tipuri de variabile: pointer la un caracter, un vector de 10 valori ntregi, pointer la un vector de 10 valori ntregi, un pointer la un pointer la un caracter. E1.7 S se scrie un program care tiprete dimensiunea tipurilor fundamentale de date. Se va folosi operatorul sizeof. E1.8 Se d o matrice (tablou bidimensional) de numere ntregi, de dimensiune 3 x 4. S se scrie o funcie care afieaz la consol liniile tabloului.