CATEGORII DOCUMENTE |
Elemente de
programare orientata obiect
Programarea orientata obiect (Object Oriented Programming - OOP) reprezinta o tehnica ce s-a impus in anii '90, dovedindu-se benefica pentru realizarea sistemelor software de mare complexitate. Notiunea de obiect dateaza din anii '60, odata cu aparitia limbajului Simula. Exista limbaje - ca Smalltalk si Eiffel - care corespund natural cerintelor programarii orientate obiect, fiind concepute in acest spirit. Recent au fost dezvoltate si alte limbaje orientate obiect, fie pentru programare generala, fie pentru realizarea de scripturi - Java, Delphi, C++, Visual Basic .NET, C#, Python, Ruby. Unele dintre ele ofera in continuare si posibilitatea programari procedurale (Delphi, C++). Toate limbajele folosite in prezent ofera si facilitati de programare orientata obiect - ADA, Fortran, Cobol, PHP etc. In prezent exista in functiune sisteme software de mare anvergura realizate in tehnica programarii orientata obiect, principiile ei fiind suficient de bine clarificate, astfel incit sa se treaca din domeniul cercetarii in cel al productiei curente de programe.
Acest capitol prezinta o introducere in lucrul orientat obiect in limbajul C++, fara a acoperi toata problematica specifica.
1. Modelul de date orientat obiect
OOP reprezinta o abordare cu totul diferita fata de programarea procedurala, devenita deja "clasica". Daca in programarea clasica programatorul era preocupat sa raspunda la intrebarea "ce trebuie facut cu datele?", adica sa defineasca proceduri care sa transforme datele in rezultate, in OOP accentul cade asupra datelor si legaturilor intre acestea, ca elemente prin care se modeleaza obiectele lumii reale. Se poate afirma, intr-o prima analiza, ca OOP organizeaza un program ca o colectie de obiecte, modelate prin date si legaturi specifice, care interactioneaza dinamic, adica manifesta un anumit "comportament", producind rezultatul scontat. In general, pentru modelul de date orientat pe obiect, se considera definitorii urmatoarele concepte: abstractizare, obiect, atribut, metoda, clasa, spatiu propriu, spatiu extins, incapsulare, mostenire si polimorfism.
Abstractizarea constituie procesul de simplificare a realitatii prin retinerea caracteristicilor si comportamentelor esentiale si constituirea lor intr-un model adecvat rezolvarii problemelor.
Obiectul este un model informational al unei entitati reale, care poseda, la un anumit nivel, o multime de proprietati si care are, in timp, un anumit comportament, adica manifesta reactii specifice in relatiile cu alte entitati din mediul sau de existenta. Ca model, un obiect este o unitate individualizabila prin nume, care contine o multime de date si functii. Datele descriu proprietatile si nivelul acestora, iar functiile definesc comportamentul.
Avind in vedere proprietatile comune si comportamentul similar al entitatilor pe care le modeleaza, obiectele pot fi clasificate in multimi. O multime de obiecte de acelasi fel constituie o clasa de obiecte, descrisa prin modelul comun al obiectelor sale.
De exemplu, in figura 1, numerele complexe, ca perechi de numere reale de forma (parte reala, parte imaginara) pot fi descrise printr-un model comun, denumit ClasaComplex. Modelul arata ca orice obiect de acest fel se caracterizeaza printr-o pereche de numere intregi si ca pe aceasta multime sint definite operatii unare si binare care arata cum "interactioneaza" obiectele in interiorul multimii: un numar complex poate da nastere modulului si opusului sau, doua numere complexe pot produce un alt numar complex ca suma, produs etc.
Generalizind, se poate afirma ca o clasa de obiecte se manifesta ca un tip obiect, iar modelul comun al obiectelor este modelul de definire a tipului obiect. Astfel, obiectele individuale apar ca manifestari, realizari sau instantieri ale clasei, adica exemplare particulare generate dupa modelul dat de tipul obiect. Altfel spus, o clasa poate fi considerata ca un tip special de data, iar obiectele sale ca date de acest tip.
Fig.1. Clasa si obiecte - multimea numerelor complexe
Acceptarea acestei semnificatii pentru clase de obiecte este de natura sa simplifice descrierea obiectelor si sa asigure un tratament al acestora similar tipurilor structurate de date din limbajele de programare: este suficienta o descriere a tipului obiect si apoi se pot declara constante si variabile de acest tip. Datele care reprezinta proprietatile obiectelor se numesc atribute si sint de un anumit tip (de exemplu intregi, reale, caractere etc.). Setul de valori ale atributelor unui obiect la un moment dat formeaza starea curenta a obiectului respectiv. Functiile care definesc comportamentul obiectelor sint cunoscute ca metode ale clasei. Impreuna, atributele si metodele sint membrii clasei, identificabili prin nume. Pentru a pune in evidenta faptul ca un membru apartine unui obiect se utilizeaza calificarea, astfel: nume_obiect.nume_membru. In figura 1, a.P_reala refera valoarea 1.0, iar a.Modul refera metoda Modul a obiectului a pentru a produce obiectul rezultat.
Asa cum sugereaza figura 1, fiecare obiect trebuie sa contina valorile atributelor sale, deoarece ele definesc starea obiectului respectiv. Spatiul de memorie ocupat de atributele unui obiect se numeste spatiu propriu al obiectului. In multe cazuri, intre atribute se afla pointeri care indica anumite zone de memorie alocate dinamic pentru obiect (de exemplu clasa lista are ca membru atributul cap care contine adresa primului nod al unei liste dinamice simplu inlantuite). Acest spatiu alocat dinamic apartine tot obiectului, dar el se numeste spatiu extins al obiectului. Gestiunea acestui spatiu extins trebuie asigurata de metodele clasei.
Metodele, care descriu actiuni identice pentru toate obiectele clasei, sint memorate o singura data, intr-o zona comuna tuturor obiectelor clasei. Intrucit metodele descriu comportamentele obiectelor, ele nu pot fi apelate independent, ci numai in legatura cu un anumit obiect. Despre o metoda apelata pentru un anumit obiect se spune ca se executa in contextul obiectului respectiv, iar acesta este numit obiect curent. Apelarea metodei este considerata ca trimitere de mesaj catre obiectul curent, iar executia metodei reprezinta raspunsul (reactia) obiectului curent la mesajul primit.
Faptul ca o metoda se executa in contextul obiectului curent inseamna ca are, in mod implicit, acces la toate atributele si metodele obiectului. Acestea nu trebuie sa apara ca parametri ai metodei. Pentru a utiliza alte obiecte, din aceeasi clasa sau din clase diferite, metoda trebuie sa aiba parametri corespunzatori. De asemenea, pentru a simplifica scrierea, in interiorul unei metode referirea la membrii obiectului curent se face fara calificare.
Pe baza acestor conventii, in functiile Conjugat, Suma si Modul, scrise in pseudocod, s-a specificat cu un parametru mai putin decit numarul de operanzi pe care ii presupune operatia respectiva, deoarece un operand este obiectul curent. Referirea la atributele obiectului curent se distinge de celelalte prin lipsa calificarii. Descrierea in pseudocod a metodelor Conjugat, Suma si Modul din clasa CComplex (figura 1) poate fi facuta astfel:
void Conjugat(b);
begin b.p_reala:=p_reala;
b.p_imaginara:=-p_imaginara;
end;
void Suma(b,c);
begin
c.p_reala:=p_reala+b.p_reala;
c.p_imaginara:=-p_imaginara+b.p_imaginara;
end;
float Modul();
begin
Modul=sqrt(p_reala*p_reala+p_imaginara*p_imaginara);
end;
Deoarece o clasa este un tip de data, in definirea unei clase B se pot declara atribute de tip A, unde A este la rindul ei o clasa. Mai mult, o clasa A poate defini atribute de tip A. De exemplu clasa Carte, din figura 2 are atributul Autor de tipul Persoana care este, de asemenea, o clasa. Mai mult, Persoana are atributul Sef care este de acelasi tip (Persoana).
Fig.2. Atribute de tip clasa
Definirea atributelor unei clase ca tipuri ale altei clase pune in evidenta o relatie intre clase si deci intre obiectele acestora.
Din punct de vedere functional, metodele unei clase au destinatii diverse. In multe cazuri si depinzind de limbaj, unei clase i se poate defini o metoda (sau mai multe) constructor si o metoda destructor. Un constructor este o metoda care creeaza un obiect, in sensul ca ii aloca spatiu si/sau initializeaza atributele acestuia. Destructorul este o metoda care incheie ciclul de viata al unui obiect, eliberind spatiul pe care acesta l-a ocupat.
Incapsularea exprima proprietatea de opacitate a obiectelor cu privire la structura lor interna si la modul de implementare a metodelor. Ea este legata de securitatea programarii, furnizind un mecanism care asigura accesul controlat la starea si functionalitatea obiectelor. Se evita astfel modificari ale atributelor obiectelor si transformari ale acestora care pot sa le "deterioreze". Potrivit acestui mecanism, o clasa trebuie sa aiba membrii impartiti in doua sectiuni: partea publica si partea privata.
Partea publica este constituita din membri (atribute si metode) pe care obiectele le ofera spre utilizare altor obiecte. Ea este interfata obiectelor clasei respective cu "lumea exterioara" si depinde de proiectantul clasei. Modalitatea extrema de constituire a interfetei este aceea a unei interfete compusa numai din metode. Daca se doreste ca utilizatorii obiectelor clasei sa poata prelua si/sau stabili valorile unor atribute ale acestora, interfata trebuie sa prevada metode speciale, numite accesorii, care au ca unic rol accesul la atribute.
Partea privata cuprinde membri (atribute si/sau metode) care servesc exclusiv obiectelor clasei respective. De regula, in aceasta parte se includ atribute si metode care faciliteaza implementarea interfetei si a functionalitatii interne a obiectului.
De exemplu, o stiva, ca tip de data poate fi descrisa de o clasa Stiva in care interfata este constituita din metodele Push, Pop, Top, Empty, in timp ce pointerul la capul stivei, Cap si numaratorul de noduri, Contor, ca atribute, sint "ascunse" in partea privata. Ea se serveste de obiectele altei clase, denumita Nod, ale carei obiecte le inlantuieste in stiva (figura 3)
Fig.3. Interfata obiectelor
Trebuie remarcat ca incapsularea inseamna si faptul ca utilizatorul metodelor nu trebuie sa cunoasca codul metodelor si nici nu trebuie sa fie dependent de eventuala schimbare a acestuia, interfata fiind aceea care ii ofera functionalitatea obiectelor in conditii neschimbate de apelare.
Mostenirea reprezinta o relatie intre clase si este, probabil, elementul definitoriu al OOP. Relatia permite constituirea unei noi clase, numita derivata (sau fiu) pornind de la clase existente, denumite de baza (sau parinte). Daca in procesul de construire participa o singura clasa de baza, mostenirea este simpla, altfel este multipla.
Se spune ca o clasa D mosteneste o clasa A, daca obiectele din clasa D contin toate atributele clasei A si au acces la toate metodele acestei clase. Din aceasta definitie, daca D mosteneste A, atunci obiectele din D vor avea toate atributele si acces la toate metodele lui A, dar in plus:
- D poate defini noi atribute si metode;
- D poate redefini metode ale clasei de baza;
- metodele noi si cele redefinite au acces la toate atributele dobindite sau nou definite.
In figura 4, clasa Cerc mosteneste clasa Punct, deci un obiect de tipul Cerc va avea ca membri coordonatele x,y mostenite si ca atribut propriu Raza. Functia Distanta, definita pentru calculul distantei dintre punctul curent si punctul p, dat ca parametru, este accesibila si pentru obiectele Cerc si va calcula distanta dintre centrul cercului si un alt punct, primit ca parametru. Functia Arie calculeaza aria din interiorul cercului, fiind nou definita. Functia Deseneaza este redeclarata de clasa Cerc, lucru impus de codul diferit pe care trebuie sa-l aiba pentru desenarea obiectelor din aceasta clasa (cerc sau alta figura).
Fig.4. Mostenirea simpla.
Daca se au in vedere multimi de clase, atunci se observa ca relatia de mostenire simpla induce un arbore ierarhic de mostenire pe aceasta multime. Exista o singura clasa initiala, radacina arborelui, fiecare clasa are un singur ascendent (parinte) si orice clasa care nu este frunza poate avea unul sau mai multi descendenti (fii). In fine, cu privire la mostenirea simpla se pot face urmatoarele observatii: daca se aduc modificari in clasa de baza, prin adaugarea de atribute si/sau metode, nu este necesar sa se modifice si clasa derivata; mostenirea permite specializarea si imbogatirea claselor, ceea ce inseamna ca, prin redefinire si adaugare de noi membri, clasa derivata are, in parte, functionalitatea clasei de baza, la care se adauga elemente functionale noi; mostenirea este mecanismul prin care se asigura reutilizarea codului sporind productivitatea muncii de programare.
Coborind (de obicei, in reprezentarile grafice ale arborilor de clase, radacina se afla in partea superioara) in arborele ierarhic al claselor de la radacina catre frunze, se poate spune ca intilnim clase din ce in ce mai specializate. Prin mostenire se realizeaza o specializare a claselor. In sens invers, de la frunza catre radacina, clasele sint din ce in ce mai generale, avem o relatie de generalizare. Clasa aflata la baza ierarhiei este cea mai generala.
Limbajele de programare orientate obiect au implementate ierarhii standard extinse de clase, care corespund necesitatilor generale ale programarii. Utilizatorii pot deriva clase noi din cele standard.
Polimorfismul este un concept mai vechi al programarii, cu diferite implementari in limbajele de programare care se bazeaza pe tipuri de date (limbaje cu tip). El si-a gasit extensia naturala si in modelul orientat pe date, implementat prin limbaje cu tip, in care clasa reprezinta tipul de date obiect.
Polimorfismul in limbajele de programare cu tip. Notiunea de polimorfism exprima capacitatea unui limbaj de programare cu tip de a exprima comportamentul unei proceduri independent de natura (tipul) parametrilor sai. De exemplu, o functie care determina cea mai mare valoare dintr-un sir de valori este polimorfica daca poate fi scrisa independent de tipul acestor valori. In functie de modul de implementare, se disting mai multe tipuri de polimorfism.
Polimorfismul ad-hoc se materializeaza sub forma unor functii care au toate acelasi nume, dar se disting prin numarul si/sau tipul parametrilor. Acest polimorfism este denumit si supraincarcare, avind in vedere semantica specifica fiecarei functii in parte.
Polimorfismul de incluziune se bazeaza pe o relatie de ordine partiala intre tipurile de date, denumita relatie de incluziune sau inferioritate. Daca un tip A este inclus (inferior) intr-un tip B, atunci se poate trimite un parametru de tip A unei functii care asteapta un parametru de tip B. Astfel, un singur subprogram defineste functional o familie de functii pentru toate tipurile inferioare celor declarate ca parametri. Un exemplu clasic este cazul tipului int, inferior tipului float in toate operatiile de calcul.
Polimorfism parametric consta in definirea unui model de procedura pentru care chiar tipurile sint parametri. Acest polimorfism, denumit si genericitate, presupune ca procedura se genereaza pentru fiecare tip transmis la apel ca parametru.
Cele trei tipuri de polimorfism exista (toate sau numai o parte din ele) in limbajele clasice de programare, dar unele pot sa nu fie accesibile programatorului.
Polimorfismul in limbajele orientate obiect. Limbajele orientate obiect sau extensiile obiect ale unor limbaje cu tip ofera, in mod natural, polimorfismul ad-hoc si de incluziune. Polimorfismul ad-hoc intrinsec reprezinta posibilitatea de a defini in doua clase independente metode cu acelasi nume, cu parametri identici sau diferiti. Acest polimorfism nu necesita mecanisme speciale si decurge simplu, din faptul ca fiecare obiect este responsabil de tratarea mesajelor pe care le primeste. Polimorfismul este de aceeasi natura si in cazul in care intre clase exista o relatie de mostenire, cu precizarea ca, in cazul in care o metoda din clasa derivata are parametrii identici cu ai metodei cu acelasi nume din clasa de baza, nu mai este supraincarcare, ci redefinire, dupa cum s-a precizat mai sus.
Polimorfimsul de incluziune este legat de relatia de mostenire si de aceea se numeste polimorfism de mostenire. Intr-adevar, relatia de mostenire este o relatie de ordine partiala, astfel incit daca clasa D mosteneste direct sau indirect clasa A, atunci D este inferior lui A. In aceste conditii, orice metoda a lui A este aplicabila la obiectele de clasa D si orice metoda, indiferent de context, care are definit un parametru de tip A (parinte) poate primi ca argument corespunzator (parametru actual) un obiect de clasa D (fiu).
Observatie: un obiect de clasa A nu poate lua locul unui obiect de clasa D, deoarece A "acopera" numai partial pe D, care este o extensie si o specializare a lui A.
Legare statica si dinamica a metodelor. Legarea statica a metodelor se regaseste atit in limbajele orientate obiect cit si in cele clasice. Compilatorul poate determina care metoda si din care clasa este efectiv apelata intr-un anumit context si poate genera codul de apel corespunzator.
Fie o clasa A si o clasa D, unde D este derivata din A. Fie o metoda din clasa A, numita calculeaza, care este redefinita in clasa derivata, D. Atunci cind metoda este apelata in contextul unui obiect static, compilatorul poate determina tipul acelui obiect (ca fiind parte a clasei A sau D). Astfel, el va sti ce metoda sa apeleze (a clasei de baza sau cea redefinita, a clasei derivate). In acest caz are loc o legare statica a metodelor (decizia este luata in momentul compilarii).
Fie un pointer p, definit ca pointer spre clasa A. Datorita polimorfismului, in limbajele orientate obiect unui obiect din clasa parinte, desemnat indirect prin referinta (pointer) si nu prin nume, i se poate atribui un obiect fiu. In acest context, p poate primi ca valoare, in timpul executiei programului, adresa unui obiect din clasa A sau din clasa D. Nu se poate sti la momentul compilarii ce se va intimpla in timpul executiei programului, ca urmare nu se poate determina daca, in contextul dat, trebuie apelata metoda clasei de baza sau metoda clasei derivate. De aceea, in locul din program in care este apelata metoda, compilatorul adauga o secventa de cod care, la momentul executiei, va verifica tipul efectiv al obiectului si, dupa caz, va realiza apelarea metodei adecvate. In acest caz are loc legarea dinamica a metodelor (sau la momentul executiei). Legarea dinamica este evident mai costisitoare decit cea statica, dar reprezinta o necesitate pentru a asigura elasticitatea necesara in realizarea programelor OOP, obiectele putind avea caracter de variabile dinamice.
2. Definirea claselor
Definitia unei clase este asemanatoare cu definitia unui articol, insa in locul cuvintului rezervat struct se foloseste cuvintul class:
class nume ;
Membrii unei clase pot fi atribute sau metode. Atributele sint descrise asemanator declaratiilor de variabile independente (si asemanator cimpurilor unui articol - struct), specificind tipul si numele atributului respectiv. Membrii unei clase pot fi de orice tip, mai putin de acelasi tip cu clasa descrisa (dar pot fi pointeri catre clasa descrisa).
Metodele sint descrise asemanator functiilor independente. Ele pot fi descrise integral in interiorul clasei (descriind antetul si corpul lor) sau specificind in interiorul clasei doar prototipul functiei, corpul urmind sa fie descris ulterior, in afara clasei. Este preferata a doua varianta, deoarece descrierea clasei este mai compacta decit in primul caz. Atunci cind se descrie ulterior corpul unei metode, pentru a specifica apartenenta sa la clasa respectiva, numele metodei este prefixat cu numele clasei din care face parte, folosind operatorul de rezolutie (::), astfel:
tip_rezultat nume_clasa::nume_metoda(lista parametrilor)
corp metoda
Mai mult, functiile care sint integral descrise in interiorul clasei sint considerate functii inline[1], de aceea ele trebuie sa fie simple. Pentru functiile mai complexe, intotdeauna se recomanda sa fie descrise folosind a doua varianta.
Intrucit o clasa este un tip de data, declararea unui obiect se face asemanator oricarei declaratii de data:
nume_clasa nume_obiect;
Atunci cind se doreste lucrul cu obiecte dinamice, se poate declara o variabila de tip pointer catre clasa: nume_clasa* p_obiect;
Declararea unui obiect mai este numita si instantierea clasei, in sensul ca se creeaza o instanta a acelei clase, o entitate concreta din multimea descrisa de clasa respectiva..
Exemplu
Definirea clasei Complex, care implementeaza entitatea matematica numar complex. Clasa are atributele p_reala si p_imaginara si o metoda pentru afisarea valorii obiectului - afiseaza.
class Complex ;
void Complex::Afiseaza()
Complex tc;
Metoda afiseaza tine cont de semnul partii imaginare. Daca aceasta este negativa, semnul minus este afisat inaintea simbolului i al partii imaginare. Se declara obiectul tc de tipul Complex.
Accesul la membrii obiectului se face folosind operatorul de calificare:
nume_obiect.nume_membru
unde numele obiectului specifica din ce obiect este accesat atributul respectiv sau in contextul carui obiect se executa metoda respectiva. Acest mod de accesare este folosit atunci cind se lucreaza cu obiecte statice. In cazul in care nu avem un obiect ci un pointer catre un obiect, este necesara si dereferentierea pointerului, inainte de accesul la membri. Acest lucru este realizat folosind operatorul -> in locul operatorului de calificare:
p_obiect -> nume_membru
Pentru implementarea conceptului de incapsulare, in interiorul unei definitii de clasa pot fi folositi modificatori de acces. Acestia sint private, protected si public (urmati de caracterul : "doua puncte"). Domeniul de actiune al unul modificator de acces incepe in locul unde apare el si se incheie la aparitia altui modificator sau la terminarea descrierii clasei. Implicit, toti membri sint considerati sub influenta modificatorului private, deci orice membru aflat in afara domeniului de actiune al unui modificator este considerat privat. Modificatorul public face ca toti membri aflati in domeniul sau de actiune sa poata fi accesati atit de catre metodele clasei cit si de catre orice entitate din afara clasei (membri publici). Modificatorul private face ca membrii aflati in domeniul sau de actiune sa poata fi accesati numai de catre metodele clasei respective (membri privati). Modificatorul protected este similar cu modificatorul private, dar membrii respectivi pot fi accesati si de catre metodele claselor derivate, dar numai in obiecte apartinind claselor derivate.
De obicei atributele unei clase sint declarate ca fiind private iar metodele sint impartite, unele fiind publice (interfata clasei) si unele private (detalii si mecanisme interne de implementare a clasei. Desi este tehnic posibil ca toti membrii unei clase sa fie privati, un obiect de acest tip nu poate fi folosit, neavind o interfata cu mediul exterior lui. De asemenea, toti membrii unei clase pot fi publici, dar nu este recomandata aceasta tehnica din motive de protectie si securitate.
Exemplu
In acest context, clasa Complex definita in exemplul anterior nu poate fi folosita, toti membrii ei fiind privati. Pentru a putea folosi obiecte de tipul Complex, metoda afiseaza trebuie sa fie publica. Descrierea clasei devine:
class Complex ;
void Complex::Afiseaza()
Prin adaugarea modificatorului de acces public, metoda afiseaza este pusa la dispozitia mediului extern, ca interfata a obiectului.
Pentru accesul controlat la atributele private, clasele pot pune la dispozitia mediului extern metode publice de acces, numite uzual metode accesorii. Acestea au rolul de a prezenta mediului extern valorile unora dintre atribute (acelea care pot prezenta interes) sau de a modifica valorile unora dintre atribute, in mod controlat (numai acele atribute pentru care modificarea la initiativa mediului extern are sens). Controlul depinde in fiecare caz de scopul clasei respective.
Exemplu
Adaugind metode accesorii clasei Complex, descrierea acesteia devine:
class Complex ;
void Complex::Afiseaza()
float Complex::GetR()
float Complex::GetI()
void Complex::SetR(float r)
void Complex::SetI(float i)
Metodele accesorii definite mai sus (GetR, GetI, SetR, SetI) au rolul de a prezenta valorile atributelor si respectiv de a stabili noi valori pentru ele. In acest exemplu nu se face nici un fel de control asupra modului in care sint stabilite noile valori.
Folosind descrierile de mai sus, urmatoarea secventa de program:
void main()
produce pe ecran urmatorul rezultat:
5.00-i* 4.00
5.00-i* 4.00
-2.00+i* 3.00
Pentru a evita eventualele confuzii, in interiorul corpului metodelor poate fi folosit pointerul implicit this atunci cind se refera membrii obiectului curent. Acesta este gestionat automat si are ca valoare adresa obiectului curent. Ca urmare, metoda SetR ar putea fi rescrisa astfel:
void Complex::SetR(float r)
Pointerul this poate fi folosit si pentru accesarea metodelor obiectului curent, in acelasi mod.
Tinind cont de domeniul de valabilitate al declaratiilor de tipuri de date, descrierea unei clase se face de obicei la inceputul fisierului sursa in care urmeaza a fi folosita. Pentru o mai mare generalitate si reutilizare mai usoara, se prefera ca fiecare clasa noua sa fie descrisa intr-un fisier sursa separat, care sa fie inclus folosind directiva #include in programe.
3. Constructori
Declararea obiectelor are ca efect alocarea de spatiu in memorie, la fel ca in cazul declararii oricarei variabile. Acest spatiu nu este initializat insa. Mai mult, in cazul in care obiectele clasei au si spatiu extins de memorie, acesta nu este alocat automat, obiectul declarat fiind astfel incomplet. Atributele unui obiect nu pot fi initializate la declarare intr-o maniera asemanatoare datelor de tip articol (struct), deoarece de obicei atributele sint private, deci inaccesibile din exteriorul obiectului. Pentru rezolvarea problemei initializarii obiectelor exista posibilitatea utilizarii unor metode speciale, numite constructori. La terminarea ciclului de viata al obiectelor, este necesara dezalocarea lor. In general aceasta se realizeaza automat, dar in cazul lucrului cu spatiu extins, ea trebuie gestionata in mod explicit. Problema incheierii ciclului de viata al obiectelor este rezolvata prin utilizarea unor metode speciale numite destructori. Constructorii si destructorii nu intorc nici un rezultat prin numele lor si antetele lor nu precizeaza nici un tip pentru rezultat (nici macar void).
Constructorii sint metode care au acelasi nume cu clasa careia ii apartin. O clasa poate avea mai multi constructori, cu liste diferite de parametri (ca tip si/sau numar) - metode supraincarcate. Daca nu este definit nici un constructor pentru o clasa, compilatorul va genera un constructor implicit, care nu face decit alocarea spatiului propriu al obiectului, in momentul in care acesta a fost declarat. Ca urmare, in acest caz vom avea obiecte neinitializate, urmind ca initializarea atributelor sa se faca ulterior, prin intermediul metodelor accesorii. In exemplul anterior, pentru clasa Complex s-a generat un constructor implicit care aloca spatiu pentru atributele p_reala si p_imaginara. Initializarea s-a facut prin intermediul metodelor SetR si SetI. In cazul in care clasa prezinta cel putin un constructor explicit, compilatorul nu mai genereaza constructorul implicit. Ca urmare nu se vor putea declara obiecte neinitializate daca parametrii constructorului nu au valori implicite.
Constructorii nu pot fi apelati explicit, precum metodele obisnuite. Apelul lor se realizeaza numai la declararea obiectelor. De asemenea, nu se poate determina adresa constructorilor, asa cum se poate face in cazul functiilor obisnuite. Am vazut mai sus ca declaratia unui obiect care nu are constructor explicit este identica cu declaratia unei variabile simple. In cazul in care clasa prezinta constructori expliciti valorile pentru initializare sint transmise acestuia la declararea obiectului, asemanator listei de parametri reali la apelul unei functii:
nume_clasa nume_obiect(lista_valori);
Exemplu
Pentru clasa Complex se poate defini un constructor care sa initializeze cei doi membri astfel:
class Complex ;
Complex::Complex(float a,float b)
Avind acest constructor in cadrul clasei, nu putem declara obiecte neinitializate (ca in exemplele anterioare) ci doar obiecte initializate:
Complex a(3,4); //a reprezinta numarul 3+i*4
Complex b(-1,3.2); //b reprezinta numarul -1+i*3.2
Complex c; //incorect
Atunci cind exista un singur constructor explicit, se aplica regulile transferului parametrilor similar ca la functiile obisnuite (se realizeaza conversia parametrilor reali catre tipurile formale). Daca sint mai multi constructori, cu liste diferite de parametri, se alege acela a carui lista de parametri corespunde ca tip si numar cu lista valorilor (expresiilor) precizate la declararea obiectului.
Pentru constructori, ca si pentru orice alta functie, pot fi precizate valori implicite ale parametrilor. Acolo unde la apel lipsesc parametrii actuali, se folosesc valorile implicite. Ca urmare, la declararea obiectelor pot sa nu fie precizate valori pentru toate atributele.
Exemplu
Se defineste clasa Complex astfel:
class Complex ;
Complex::Complex(float a,float b)
Putem declara urmatoarele obiecte:
Complex a; //a reprezinta numarul 0+i*0
Complex b(-1); //b reprezinta numarul -1+i*0
Complex c(2,3); //c reprezinta numarul 2+i*3
Daca prototipul constructorului ar fi fost Complex(float a,float b=0); atunci la declaratia obiectului trebuie precizata obligatoriu valoarea pentru primul parametru (cel care va deveni valoarea atributului p_reala); ca urmare, dintre declaratiile anterioare ar fi fost corecte numai cele ale obiectelor b si c.
Acolo unde se poate folosi un singur parametru la declararea obiectului, se poate face initializarea asemanator initializarii variabilelor simple. Folosind oricare dintre cei doi constructori din exemplul anterior, putem declara un obiect si in modul urmator:
Complex a = 1; //numarul 1+i*0
Valoarea declarata este atribuita primului parametru al constructorului.
Pentru situatiile in care avem nevoie atit de obiecte initializate cit si de obiecte neinitializate, se adauga in descrierea clasei atit un constructor care realizeaza initializarea obiectului cit si un constructor vid, care simuleaza constructorul implicit, adaugat de compilator atunci cind nu se definesc constructori impliciti. Constructorul vid are urmatoarea forma:
Complex::Complex()
In acest context, declaratia Complex a; are ca efect crearea obiectului a neinitializat (se utilizeaza constructorul vid, nu constructorul cu valori implicite pentru parametri).
Parametrii unui constructor pot fi de orice tip, mai putin de tipul clasei respective (in exemplele anterioare, constructorul clasei Complex nu poate avea un parametru de tipul Complex. Este posibil insa sa avem un parametru de tip pointer catre clasa respectiva sau referinta[2] catre clasa respectiva. Un constructor care primeste ca parametru o referinta catre un obiect din acea clasa se numeste constructor de copiere. Prin utilizarea unui constructor de copiere se poate initializa un obiect nou cu atributele unui obiect existent (se realizeaza o copie a acelui obiect). Constructorii de copiere pot avea si alti parametri, dar acestia trebuie sa aiba valori implicite. Compilatorul genereaza automat un constructor de copiere pentru toate clasele care nu au un constructor de copiere definit explicit.
Exemplu
Clasa Complex contine un constructor de copiere:
class Complex ;
Complex::Complex(float a,float b)
Complex::Complex(Complex &x)
Putem declara urmatoarele obiecte:
Complex a(3,4); //numarul 3+i*4
Complex b = a; //numarul 3+i*4
Complex c(b); //numarul 3+i*4
Obiectul b este o copie a obiectului a si va avea aceeasi stare, imediat dupa declarare (aceleasi valori ale atributelor). De asemenea, obiectul c este o copie a obiectului b. Pentru ambele obiecte s-a apelat constructorul de copiere.
Constructorul implicit de copiere realizeaza o copie binara a spatiului propriu de memorie al obiectului sursa. Ca urmare, daca obiectele au spatiu extins de memorie, in urma copierii ambele obiecte refera acelasi spatiu extins de memorie. Pentru a evita aceasta situatie anormala, atunci cind clasele descriu obiecte care lucreaza si cu spatiu extins este necesara folosirea unui constructor de copiere explicit, care sa realizeze, pe linga copierea atributelor, alocarea de spatiu extins propriu pentru obiectul nou si copierea spatiului extins al obiectului sursa in acest spatiu nou.
Constructorii nu pot fi apelati in mod explicit in program. Singura situatie in care poate sa apara un astfel de apel este la declararea unui obiect, in modul urmator:
Complex a = Complex(2,5);
In cazul in care o clasa are ca membri obiecte, la declararea unui obiect al clasei se apeleaza intii constructorii pentru obiectele membru si apoi constructorul noului obiect.
4. Destructori
Destructorii sint metode speciale, asemanatoare constructorilor, care au rol invers: incheierea ciclului de viata al obiectelor. Asa cum pentru fiecare clasa se genereaza un constructor implicit (daca nu a fost prevazut unul explicit), compilatorul genereaza si un destructor implicit, daca nu a fost prevazut unul explicit. Spre deosebire de constructori, o clasa poate avea numai un destructor explicit. Ca si constructorii, destructorii nu intorc nici un rezultat. Numele destructorului este numele clasei precedat de caracterul ~ (tilda). Destructorii nu au parametri.
Exemplu
class Complex
Complex::~Complex()
Pentru clasa Complex destructorul nu are nimic de facut si nu e necesara descrierea unui destructor explicit. Acest exemplu urmareste doar sa arate cum se declara un destructor explicit.
Spre deosebire de constructori, destructorii pot fi apelati explicit, atunci cind este necesara stergerea unui obiect. Apelul se face la fel ca pentru orice alta metoda, in contextul obiectului care trebuie sters:
a.~Complex();
Utilizarea destructorilor este obligatorie atunci cind se lucreaza cu date dinamice, deoarece destructorii impliciti nu pot elibera spatiul alocat dinamic.
In cazul in care la crearea unui obiect au fost apelati mai multi constructori, la stergerea lui se apeleaza destructorii corespunzatori, in ordine inversa.
5. Functii prieten
In unele situatii este nevoie ca functii care nu sint membri ai unei clase sa poata accesa atributele protejate ale clasei. In acest scop a fost introdus conceptul de functie prieten in C++. O functie prieten nu face parte din clasa, dar poate accesa atributele protejate.
Pentru a specifica o functie prieten, in interiorul descrierii clasei se scrie prototipul functiei prieten, prefixat cu cuvintul rezervat friend. Intrucit functia prieten nu este membru al clasei, in interiorul sau nu este definit pointerul this, ceea ce face ca functia prieten sa nu poata accesa direct atributele obiectului. De aceea este necesar ca obiectul sa fie parametru al functiei prieten. Ca urmare, o functie prieten are un parametru in plus fata de o metoda.
Modificatorii de acces nu au nici o influenta asupra functiilor prieten, de aceea ele pot fi specificate oriunde in cadrul descrierii clasei.
Exemplu
In acest exemplu functia Afiseaza va fi scoasa in afara clasei Complex si va fi declarata ca functie prieten.
class Complex ;
void Afiseaza(Complex x)
void main()
Exemplu
Sa se implementeze clasa Stiva dinamica. O lista dinamica este formata din noduri, deci putem defini intii clasa Nod, urmind a folosi tipul Nod pentru a descrie clasa Stiva. Pentru acest exemplu, datele memorate in nodurile stivei sint de tip float.
#include <stdio.h>
typedef float TIP_INFO;
class Nod ;
float Nod::GetInfo()
Nod* Nod::GetNext()
Nod::Nod(float a, Nod* n)
class Stiva ;
void Stiva::Afiseaza()
}
Stiva::~Stiva()
}
float Stiva::Pop()
void Stiva::Push(float a)
int Stiva::Empty()
Stiva::Stiva(float a)
Stiva::Stiva()
void main()
Clasa Nod contine atributele info (informatia utila din nod) si next iar ca metode un constructor care initializeaza atributele obiectului si metode accesorii pentru accesarea valorilor atributelor.
Clasa Stiva contine un singur atribut, Cap care are ca valoare adresa primului nod al stivei (virful stivei). Constructorii clasei asigura crearea unei stive vide sau a unei stive cu un element. Metodele asigura adaugarea unei informatii in stiva, respectiv extragerea unei informatii. Metoda Afiseaza asigura afisarea pe ecran a informatiilor din stiva.
6. Derivarea claselor
Derivarea claselor este legata de implementarea conceptului de mostenire. In limbajul C++ este permisa mostenirea multipla. Pentru a defini o clasa fiu ca fiind derivata dintr-o clasa parinte (sau mai multe clase parinte), se procedeaza astfel:
class nume_clasa_fiu : lista_clase_parinte
;
In lista claselor parinte se specifica numele claselor parinte, separate prin virgula si, eventual, precedate de modificatori de acces - se pot folosi modificatorii public sau private. Acesti modificatori de acces definesc nivelul de protectie a membrilor clasei parinte in clasa fiu, conform tabelului urmator:
Nivel acces in clasa parinte |
Modificator de acces in lista claselor parinte |
Nivel acces in clasa fiu |
private |
public |
inaccesibil |
private |
inaccesibil |
|
protected |
public |
protected |
private |
private |
|
public |
public |
public |
private |
private |
Pentru fiecare clasa parinte se poate specifica un modificator de acces. Daca nu se specifica nici un modificator de acces atunci, implicit, se considera modificatorul public.
Se observa ca o clasa derivata are acces la membrii clasei parinte care au fost definiti ca fiind publici sau protejati si nu are acces la membrii privati. Prin derivare se construiesc ierarhii de clase, deci din clasa fiu se pot deriva alte clase noi. Daca la derivare s-a folosit modificatorul de acces private, atunci toti membrii clasei parinte vor deveni privati in clasa fiu si o derivare in continuare nu mai este posibila, ei fiind inaccesibili pentru orice alta clasa derivata din clasa fiu. Intrucit ierarhiile de clase nu sint definitive ci ofera posibilitatea extinderii prin adaugarea de noi clase derivate, se prefera ca la derivare sa se foloseasca modificatorul de acces public. De aceea aceste este modificatorul explicit.
Exemplu
Fie clasa Punct care implementeaza entitatea punct geometric. Aceasta are atributele x si y, care reprezinta coordonatele punctului in plan. Este inclusa o singura metoda, care deseneaza punctul pe ecran (aceasta metoda nu va implementata in acest exemplu).
class punct ;
void punct::deseneaza()
Fie clasa Cerc care implementeaza entitatea geometrica cerc. Aceasta este descrisa prin coordonatele centrului cercului si raza sa. Ca urmare clasa Cerc poate fi derivata din clasa Punct, adaugind un nou atribut (raza) si o noua metoda, pentru desenarea cercului.
class cerc: punct
;
void cerc::deseneaza()
Clasa cerc o sa aiba ca membri atributele x, y si raza si metodele deseneaza (mostenita de la clasa Punct, care va fi folosita pentru desenarea centrului cercului) si deseneaza (nou definita, care va fi folosita pentru desenarea cercului).
In lucrul cu ierarhii de clase se pune problema compatibilitatii tipurilor de date (clase) in cadrul atribuirilor si a conversiilor tipurilor de date. Ca principiu, un obiect al unei clase parinte poate primi ca valoare un obiect al unei clase derivate. Acelasi principiu este valabil si in cazul pointerilor catre obiecte. Utilizind exemplul de mai sus si declaratiile
punct a, *pp;
cerc b, *pc;
sint corecte atribuirile
pp=&a;
pc=&b;
a=b;
pp=pc;
pp=&b;
Nu sint corecte urmatoarele atribuiri:
pc=&a;
b=a;
pc=pp;
Pot fi realizate atribuiri folosind conversia explicita a tipurilor de date, astfel:
pc=(cerc *)pp;
pc=(cerc *)&a;
6.1.Redefinirea atributelor
Este posibil ca o clasa fiu sa redefineasca atribute mostenite de la clasa parinte (atribute publice sau protejate, intrucit cele private sint oricum inaccesibile in clasa fiu). In acest caz, clasa fiu va avea doua atribute cu acelasi nume. Implicit, utilizarea numelui atributului respectiv refera atributul redefinit. Pentru a accesa atributul mostenit, trebuie folosit operatorul de rezolutie, prefixind numele atributului cu numele clasei parinte.
Exemplu
Fie o clasa Clasa_parinte care are un atribut a de tip float, atribut protejat, si o clasa Clasa_fiu care redefineste atributul a, de tip double. x este un obiect de tipul Clasa_fiu.
class Clasa_parinte
;
Class Clasa_fiu: public Clasa_parinte
Clasa_fiu x;
Expresia
x.a
refera atributul a al clasei derivate, de tip double. Pentru a accesa atributul a mostenit de la clasa parinte, in cadrul unei metode a obiectului x trebuie folosita expresia
Clasa_parinte::a
6.2.Redefinirea metodelor
La fel ca in cazul atributelor, o clasa fiu poate sa redefineasca metodele mostenite de la clasa parinte, in cazul in care metoda mostenita nu corespunde necesitatilor. In exemplul anterior, clasa Cerc redefineste metoda deseneaza. Daca a este un obiect de tipul Cerc, atunci apelul a.deseneaza(); sau deseneaza(); - efectuat din interiorul clasei Cerc - va lansa in executie metoda redefinita, cea descrisa de clasa Cerc, care va desena conturul cercului. Atunci cind e nevoie sa se apeleze metoda mostenita numele acesteia se prefixeaza cu numele clasei parinte, folosind operatorul de rezolutie:
punct::deseneaza();
Un astfel de apel poate sa apara in interiorul metodei deseneaza a clasei Cerc pentru a desena centrul cercului.
6.3.Constructori si destructori in relatia de mostenire
Constructorii si destructorii nu se mostenesc precum alte metode. La crearea unui obiect al unei clase fiu se apeleaza intii constructorul clasei parinte si apoi constructorul clasei fiu. Daca sint mai multe clase parinte (mostenire multipla) se apeleaza constructorii claselor parinte, in ordinea in care acestea apar in lista claselor parinte. La stergerea unui obiect al unei clase fiu se apeleaza destructorii in ordine inversa fata de constructori: intii destructorul clasei fiu si apoi destructorii claselor parinte, in ordine inversa celei in care acestea apar in lista claselor parinte.
Pentru a preciza parametrii reali utilizati pentru fiecare din constructorii claselor parinte, antetul constructorului clasei derivate are o forma speciala:
class Clasa_fiu: clasa_p1, clasa_p2, clasa_p3
Clasa_fiu::clasa_fiu(.):clasa_p1(.),clasa_p2(.),clasa_p3(.)
Se observa ca in descrierea clasei, constructorul se descrie in mod obisnuit, dar ulterior, in antetul constructorului apar apeluri ale constructorilor claselor parinte. Ordinea in care apar aceste apeluri nu are nici o importanta, deoarece ordinea de apelare este data de ordinea in care sint specificate clasele parinte.
Daca una din clasele parinte nu are constructor, atunci nu o sa apara un apel corespunzator in antetul constructorului clasei fiu, pentru ea apelindu-se automat constructorul implicit.
Daca nici una dintre clase nu are constructor explicit, atunci se folosesc constructorii impliciti pentru toate clasele.
O situatie deosebita este aceea in care clasa fiu nu are constructor explicit, dar cel putin una din clasele parinte are un constructor explicit. Deoarece in aceasta situatie nu se pot descrie explicit parametrii pentru apelarea constructorilor claselor parinte, acesti constructori trebuie sa aiba valori implicite pentru toti parametrii.
6.4.Clase virtuale
In cazul mostenirii multiple pot sa apara situatii in care o clasa derivate mosteneste un atribut (sau mai multe) de mai multe ori, prin intermediul mai multor linii de mostenire. Aceste situatii produc ambiguitati legate de referirea atributului respectiv.
Limbajul C++ ofera un mecanism simplu prin care sa se revina astfel de situatii, prin utilizarea claselor virtuale. Fie urmatoare ierarhie, in care clasa cf este derivata din clasele cp1, cp2 si cp3, toate acestea fiind la rindul lor derivate din clasa cb. Clasa de la baza ierarhiei, cb are un atribut x. Acest atribut va fi mostenit in clasele cp1, cp2, cp3. Clasa cf va mosteni 3 exemplare ale atributului x.
Fig.5. Exemplu de mostenire multipla
Pentru a evita aceasta situatie, clasa cb poate fi declarata ca virtuala la descrierea claselor cp1, cp2 si cp3, astfel:
class cp1: virtual public cb
;
class cp2: virtual public cb
;
class cp3: virtual public cb
;
class cf: public cp1, public cp2, public cp3,
;
Considerind aceste declaratii, clasa cf mosteneste o singura data atributul x, prin intermediul clasei cp1. Ca principiu, atributul este mostenit prin intermediul clasei care apare prima in lista claselor parinte.
In cazul in care in lista claselor parinte apar si clase virtuale, se apeleaza intii constructorii claselor virtuale, in ordinea in care au fost specificate, apoi constructorii claselor nevirtuale, in ordinea in care sint acestea specificate.
6.5.Functii virtuale
Exista situatii in care nu se poate decide in momentul compilarii care este contextul curent in care se apeleaza o metoda, care a fost redefinita intr-o clasa fiu. Astfel de situatii apar atunci cind se lucreaza cu pointeri. Fie o clasa cp care contine metoda executa, si o clasa derivata din ea, numita cf, care redefineste metoda executa, cu aceeasi lista de parametri. Fie urmatoarele declaratii:
cp a; //a este obiect de tipul cp
cf b; //b este obiect de tipul cf
cp* po; // po este pointer catre clasa cp
Pointerul po poate lua ca valoare atit adresa unui obiect de tipul cp cit si adresa unui obiect de tipul cf.
Fie apelul
po->executa();
In momentul compilarii nu se poate stabili ce metoda sa se apeleze, a clasei parinte sau a clasei fiu. In astfel de situatii compilatorul genereaza un apel catre metoda clasei parinte.
Limbajul C++ ofera posibilitatea de a intirzia decizia pina la momentul executiei. In acest scop metoda clasei parinte se declara ca fiind virtuala prin scrierea cuvintului rezervat virtual inaintea antetului sau. Este suficient ca metoda clasei de baza sa fie declarata ca fiind virtuala, in mod automat si metodele claselor derivate vor fi virtuale.
Constructorii, destructorii si functiile inline nu pot fi virtuale.
6.6.Clase abstracte
In limbajul C++ este definit conceptul de functie virtuala pura. Acest concept este necesar in cazul ierarhiilor de clase. Exista situatii in care toate clasele ierarhiei trebuie sa contina o anumita metoda, implementarea ei fiind diferita in fiecare clasa derivata. Clasa de la baza ierarhiei este prea generala pentru a putea implementa metodele. In aceasta situatie in clasa de baza se includ metode virtuale pure. Prezenta unei astfel de metode obliga toate clasele derivate sa o contina, fie ca o redefinesc fie ca nu.
O metoda virtuala pura se declara asemanator cu o metoda virtuala, adaugind la sfirsitul antetului =0:
virtual tip_rezultat nume_metoda(lista parametri) =0;
Metodele virtuale pure nu au un corp, nefiind implementate.
O clasa care contine o metoda virtuala pura se numeste clasa abstracta. O clasa abstracta nu poate fi instantiata, ea continind metode care nu sint implementate.
Clasele derivate din clase abstracte pot sa redefineasca metodele virtuale pure sau nu. Daca metoda virtuala pura nu este implementata, atunci si clasa respectiva este clasa abstracta si nu poate fi instantiata.
De exemplu, intr-o ierarhie de clase care descriu figuri geometrice, fiecare clasa are nevoie de metode pentru desenarea figurii respective, calculul ariei sau perimetrului. La baza ierarhiei se afla o clasa abstracta care include metode virtuale pure pentru aceste operatii. Clasele derivate, vor implementa metodele conform specificului fiecarei figuri geometrice.
Bibliografie
1. [Aho, Hop sa] Aho A., Hopcroft J., Ullman J., Data Structures and Algorithms, Addison-Wesley, 1983
2. [Bras, Brat] Brassard G., Bratley P., Algoritmics: Theory and Practice, Prentice-Hall, 1988
3. [Cor, Lei sa] Cormen T., Leiserson C., Rivest R., Introduction to Algorithms, MIT Press, sixteenth printing, 1996
4. [Gon] Gonnet G.H., Handbook of Algorithms and Date Structures, Addison-Wesley, 1984
5. [Hor] Horowitz E., Sahni S., Fundamentals of Computer Algorithms, Computer Science Press, 1978
6. [Knu] Knuth D., Fundamental Algorithms, vol 1 of The Art of Computer Programming, Addison-Wesley, 1973
7. [Knu] Knuth D., Sorting and Searching, vol 3 of The Art of Computer Programming, Addison-Wesley, 1973
8. [Man] Manmber U., Introduction to Algorithms: A Creative Approach, Addison-Wesley, 1989
9. [Pop, Geo sa] Popovici Ct., Georgescu H., State L., Bazele informaticii, vol 1, Tip. Universitatii din Bucuresti, 1990
10. [Tom] Tomescu I.., Probleme de combinatorica si teoria grafurilor, Editura Didactica si Pedagogica, Bucuresti, 1981
11. [Tud] Tudor S., Tehnici de programare, Ed. Teora, 1994
12. [Wil] Wilf H., Algorithms and Complexity, Prentice-Hall, 1986
13. [Negrescu, 1994] Liviu Negrescu, Limbajele C si C++ pentru incepatori, Editura Microinfomatica, Cluj-Napoca, 1994
14. [Smeureanu, 1995] Ion Smeureanu, Ion Ivan, Marian Dardala, Limbajul C/C++ prin exemple, Editura Cison, Bucuresti 1995
15. [Ghilic, 2003] Bogdan Ghilic-Micu, Ion Gh. Rosca, Constantin Apostol, Marian Stoica, Catalina Lucia Cocianu, Algoritmi in programare, Editura ASE, Bucuresti 2003
[1] Apelul functiilor inline nu produce un salt in segmentul de cod catre codul executabil al functiei, asa cum se intimpla in cazul functiilor obisnuite. Pentru aceste functii, compilatorul insereaza in program, in locul apelului, secventa de cod corespunzatoare corpului functiei, inlocuind parametrii formali cu valorile actuale. Functiile inline au un comportament asemanator macrodefinitiilor.
[2] In C++ este implementat transferul parametrilor prin adresa. Pentru a transmite un parametru prin adresa, in lista de parametri se pune inaintea numelui sau operatorul de referentiere &
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 2128
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved