Download - Functii Inline. Date Si Functii Statice. C++
Introducere
Modalităţile (tehnicile, paradigmele) de programare au evoluat de-a lungul
anilor, reflectând trecerea de la programe de dimensiuni reduse, la programe şi
aplicaţii de dimensiuni foarte mari, pentru coordonarea cărora sunt necesare tehnici
evoluate.
Software-ul de dimensiuni mari, care nu poate fi realizat de o singură
persoană , intră în categoria sistemelor complexe, cu o mare diversitate de
aplicaţii. Situaţiile reale şi experiene ale psihologilor au relevat limitele capacităţii
umane în perceperea sistemelor complexe, adică imposibilitatea unei persoane de
a percepe şi controla simultan un număr mare de entităţi de informaţie. De aceea,
este esenţială descompunerea şi organizarea sistemelor complexe, pentru a fi
percepute, proiectate sau conduse.
Modul în care este abordată programarea, din punct de vedere al
descompunerii programelor, defineşte mai multe tehnici de programare , care s-au
dezvoltat şi au evoluat odată cu evoluţia 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 (funcţii) care sunt apelate în ordinea de
desfăşurare a algoritmului. Limbajele care suportă această tehnică de programare
prevăd posibilităţi de transfer a argumentelor către funcţii ş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 ultimele
invenţii în acest domeniu.
Programarea modulară. În cursul evoluţiei programării procedurale,
accentul în proiectarea programelor s-a deplasat de la proiectarea procedurilor
către organizarea datelor, această deplasare reflectând creşterea dimensiunilor
programelor. O mulţime de proceduri corelate, împreună cu datele pe care le
3
manevrează , sunt organizate ca un modul. Tehnica de programare modulară
decide descompunerea unui program în module, care încorporează o parte din
datele programului şi funcţiile care le manevrează . Această tehnică este cunoscută
ca tehnică de ascundere a datelor (data-hiding). În programarea modulară stilul de
programare este în continuare procedural, iar datele şi procedurile sunt grupate în
module, existând posibilitatea de ascundere a unor informaţii definite într-un
modul, faţă de celelalte module. Gruparea de date şi proceduri în module nu
implică şi o asociere strictă între acestea.
Programarea orientată pe obiecte apelează la o modalitate nouã de gândire
a unei probleme. Spre deosebire de programarea proceduralã care se concentreazã
pe structuri de date si algoritmi, programarea orientatã pe obiecte se concentreazã
pe definirea de obiecte care modeleazã problema ce trebuie rezolvatã. În
programarea orientatã pe obiecte (POO) un program are rolul de a simula stãrile si
activitãtile obiectelor lumii reale. Pe lângã structuri de date (care descriu stãrile
obiectelor, atributele acestora) trebuie incluse si metodele asociate obiectelor, adicã
acele functii care modificã atributele obiectelor si care descriu comportamentul lor.
Concepte ale programării orientate pe obiecte
Conceptele programãrii orientate pe obiecte au apãrut din dezvoltãrile
realizate în cadrul limbajelor moderne de programare. Astfel, limbajele orientate
pe obiecte au noi structuri care îmbunãtãtesc întretinerea programului si fac ca
porţiuni mari de program sã fie reutilizabile, conducând astfel la scãderea costului
de dezvoltare a produselor software. Cele sase concepte de bazã ce caracterizeazã
programarea orientatã pe obiecte sunt :
Obiecte;
Clase;
Mesaje;
4
Incapsularea;
Polimorfismul;
Moştenirea.
Un obiect poate fi considerat ca fiind o entitate care încorporeazã atât
structuri de date (denumite atribute) cât si comportament (actiuni). Obiectele sunt
inteligente prin faptul cã realizeazã anumite actiuni si “stiu” cum sã execute aceste
actiuni. Inteligenta unui obiect reprezintã o formã de abstractizare prin faptul cã
presupune cã un obiect poate executa o actiune si ascunde detaliile referitoare la
modul în care se va realiza efectiv actiunea. Obiectele pot fi de tipuri diferite:
entitãti fizice, algoritmi, relatii sau subsisteme. Practic, obiectele sunt componente
de software reutilizabil care modeleazã elemente din lumea realã.
Clasele desemnează o colecţie de obiecte (de natură materială sau spirituală)
care au în comun faptul căpot fi caracterizate similar din punct de vedere
informational şi comportamental.
Obiectele pot comunica între ele prin intermediul mesajelor. Un mesaj este o
cerere adresată unui obiect pentru a invoca una din metodele sale. Astfel, un mesaj
conţine numele metodei şi argumentele metodei.
Încapsularea se referă la capacitatea de a separa aspectele externe ale unui
obiect (interfaţa), accesibile altor obiecte, de aspectele implementaţionale, interne
obiectului, care sunt ascunse faţă de celelalte obiecte. Utilizatorul unui obiect poate
accesa doar anumite metode ale acestuia, numite publice, în timp ce atributele şi
celelalte metode îi rămân inaccesibile (acestea se numesc private).
Moştenirea - mecanismul derivării, permite crearea facilă de noi clase, care
preiau caracteristicile unor clase de bază, deja definite. Derivarea are ca obiectiv
reutilizarea soft-ului, prin folosirea uneor funcţii deja scrise pentru clasele existente
5
şi eliminarea redundanţei descrierilor, în cazul claselor care au elemente comune,
funcţii sau date.
Termenul polimorfism se referă la comportamente alternative între clase
derivate înrudite. În cazul în care mai multe clase moştenesc atributele şi
comportamentele unei clase de bază, pot apărea situaţii în care comportamentul
unei clase derivate ar trebui să fie diferit de cel al clasei de bază sau de cel al clasei
derivate de tip frate (de pe acelaţi nivel). Aceasta înseamnă că un mesaj poate avea
efecte diferite în funcţie de clasa obiectului care primeşte mesajul.
Conceptul de tip de date este unul din conceptele fundamentale ale
programării. În matematică, ştiinţă, tehnică, economie şi alte domenii ale activităţii
umane se lucrează cu date, care sunt numere întregi, numere reale, şiruri de
caractere, valori logice sau structuri de date construite cu ajutorul acestora. Se
admite că, atît mulţimea numerelor întregi, cît şi cea a numerelor reale, sunt
mulţimi infinite. În programare, se are însă în vedere că este necesar să se respecte
anumite restricţii, legate de modul de reprezentare a datelor în calculator. Să
considerăm, de exemplu, mulţimea numerelor întregi. În matematică, aceasta este o
mulţime infinită. În calculator însă, numărul întreg se reprezintă într-o zonă de
memorie de lungime finită, de exemplu 4 octeţi (32 biţi), ceea ce înseamnă că
"mulţimea numerelor de tip întreg", care pot fi astfel reprezentate, este finită,
conţinînd 2 la puterea 32 valori. Trebuie avute în vedere, de asemenea, restric
ţiile privind operaţiile care pot fi efectuate pe calculator asupra acestei mulţimi de
valori.
Prin tip de date (engl.: data type) se înţelege o mulţime de valori, căreia i se
asociază o mulţime de operaţii asupra valorilor respective.
Tipul unei date precizează mărimea si organizarea locaţiei de memorie în
care este stocată data respectivă, precum şi comportamentul datei sub acţiunea
operatorilor. De exemplu, dacă x si y sunt două variabile de tip întreg cu valorile 7
6
şi respectiv 2, atunci expresia x/y are ca rezultat numărul întreg 3, iar dacă x şi y
sunt variabile în virgule mobilă cu aceleaşi valori numerice, rezultatul evaluării
expresiei x/y este 3,5.
În fiecare limbaj de programare există un set de tipuri de date primitive,
numite şi tipuri de date fundamentale sau elementare. Acestea sunt tipuri
predefinite în limbajul respectiv, deci ele nu trebuie sa fie definite de către
programator. Cu ajutorul acestor tipuri primitive, programatorul poate să
construiască propriile sale tipuri de date derivate. În acest scop, limbajul de
programare îi pune la dispoziţie anumite modalităţi de construire.
În principiu, majoritatea limbajelor de programare admit că tipuri de date
primitive numerele întregi şi reale, caracterele şi valorile logice (booleene). Pot
exista însă mai multe tipuri de numere întregi sau reale, care să difere între ele prin
lungimea zonei de memorie alocate. Pot exista, de exemplu, numere întregi
reprezentate pe unul, doi, patru sau opt octeţi, ele fiind considerate tipuri de date
diferite.
Funcţiile sunt tipuri derivate şi reprezintă una din cele mai importante
caracteristici ale limbajelor C şi C++. Forma generală de definire a unei funcţii
este:
tip_returnat nume_func(tip1 arg1,tip2 arg2,……,tipn
argn) {
//corpul functiei
}
Funcţia cu numele nume_func returnează o valoare de tip tip_returnat şi
are un număr n de argumente formale declarate ca tip şi nume în lista de
argumente formale. Argumentele formale din declaraţia unei funcţii se mai numesc
7
şi parametrii funcţiei. Dacă o funcţie nu are argumente, atunci lista din paranteze
le rotunde este vidă .
O funcţie este un nume global, dacă nu este declarată de tip static. O funcţie
declarată static are domeniul de vizibilitate restrâns la fişierul în care a fost
definită.
Corpul funcţiei este propriuei şi nu poate fi accesat din afara acesteia (nici
printr-o instrucţiune goto). Corpul unei funcţii este o instrucţiune compusă , adică
o succesiune de instrucţiuni şi declaraţii incluse între acolade. În corpul funcţiei se
pot defini variabile, care sunt locale şi se memorează în segmentul de stivă al
programului. Dacă nu sunt declarate static, variabilele locale se crează la fiecare
apel al funcţiei şi se distrug atunci când este părăsit blocul în care au fost definite.
Nu se pot defini funcţii în interiorul unei funcţii. Argumentele formale ale unei
funcţii sunt considerate variabile locale ale funcţiei, ca orice altă variabilă definită
în funcţia respectivă.
Dacă o funcţie nu are de returnat nici o valoare, atunci tip_returnat din
declaraţia funcţiei este tipul void şi nu este necesară o instrucţiune de returnare
(return) în funcţie. În toate celelalte cazuri, în corpul funcţiei trebuie să fie
prevăzută returnarea unei variabile de tipul tip_returnat, folosind instrucţiunea
return. Dacă în definiţie nu este prevăzut un tip_returnat, se consideră implicit
eeturnarea unei valori de tip întreg.
8
Funcţii Inline
La apelul unei funcţii obişnuite se întrerupe execuţia funcţiei apelante şi se
execută un salt la adresa de memorie la care se găseşte corpul funcţiei apelate. La
terminarea execuţiei funcţiei apelate se revine în funcţia apelantă, reluându-se
execuţia cu instrucţiunea imediat următoare apelului de funcţie. În situaţiile în care
corpul funcţiei apelate este format din câteva instrucţiuni, operaţiile descrise
anterior (implicate în apel şi revenire) pot fi mai complicate decât un apel prin
expandare (în care apelul funcţiei este înlocuit cu însuşi corpul funcţiei apelate).
Pentru eliminarea acestor dezavantaje, se folosesc funcţiile inline.
Prezenţa funcţiilor inline anunţă compilatorul să nu mai genereze
instrucţiunile în cod maşină necesare apelului şi revenirii, ceea ce conduce la
mărirea timpului de compilare în favoarea micşorării timpului de execuţie.
Utilizarea funcţiilor inline se justifică doar în situaţiile în care codul generat de
compilator pentru execuţia corpului funcţiei este mai mic decât codul generat
pentru apel şi revenire.
Practic, funcţiile care au corpul format din maximum trei instrucţiuni şi nu
conţin instrucţiuni repetitive (for, while, do-while), pot fi declarate
inline.
Declararea unei funcţii inline se realizează explicit, specificând în antetul funcţiei
respective cuvântul cheie inline.
inline tip_val_ret nume_fct (lista_declar_par_formali);
În cazul metodelor unei clase, dacă acestea sunt definite în interiorul clasei,
ele sunt considerate, implicit, funcţii inline (în exerciţiul anterior, funcţiile
arată_Lung, arată_Lat şi calcul_arie sunt, implicit, funcţii inline).
Există şi posibilitatea de a declara metoda la declararea clasei şi de a specifica,
explicit, că este funcţie inline la definirea funcţiei.
9
Exerciţiu: Să se definească tipul de date complex, cu datele membru parte
reală şi parte imaginară. Operaţiile care pot fi realizate asupra datelor de acest tip,
vor fi: citirea unei date de tip complex (citirea valorilor pentru partea reală şi cea
imaginară); afişarea unei date de tip complex; calculul modulului unui complex;
calculul argumentului unui complex; incrementarea părţii imaginare;
decrementarea părţii imaginare; funcţii care returnează valoarea părţii reale şi a
părţii imaginare a unei date de tip complex; adunarea a două date de tip complex;
înmulţirea a două date de tip complex.
#include <iostream.h>
#include <math.h>
#define PI 3.14159
class complex{
double real, imag;
public:
int citire();
void afisare();
double modul();
double arg();
void incrpi()
10
//incrementeaza partea imaginara; FUNCŢIE INLINE,
implicit, fiind definită în interiorul clasei
{ imag++;}
inline void decrpi();//decrementarea partii
imaginare
double retreal(); //returneaza partea reala
double retimag(); //returneaza partea imaginara
void adun_c(complex, complex);//aduna 2 numere
complexe
void inm_c(complex*, complex*);//produsul a 2
numere complexe
};
inline double complex::modul()
{ return sqrt(real*real+imag*imag);}
int complex::citire()
{ cout<<"P. reala:"; if (!(cin>>real)) return 0;
cout<<"P. imag:";if (!(cin>>imag)) return 0 ;
return 1; }
void complex::afisare()
{ if (imag>=0)
11
cout<<real<<"+"<<imag<<"*i"<<"\n";
else cout<<real<<imag<<"*i\n";}
double complex::arg()
{if (real==0 && imag==0) return 0.0;
if (imag==0) //z=p. reala
if (real>0) return 0.0;
else return PI;
if (real==0)
if (imag>0) return PI/2;
else return (3*PI)/2;
double x=atan(imag/real);
if (real<0) return PI+x;
if (imag<0) return 2*PI+x; return x;}
inline void complex::decrpi()
{ imag--;}
double complex::retreal()
{ return real;}
double complex::retimag()
{ return imag; }
12
void complex::adun_c (complex x1, complex x2)
{real=x1.real+x2.real;
imag=x1.imag+x2.imag;}
void complex::inm_c(complex *x1, complex *x2)
{real=x1->real*x2->real-x1->imag*x2->imag;
imag=x1->real*x2->imag+x1->imag*x2->real;}
void main()
{complex z1;z1.citire();
cout<<"z1=";z1.afisare();
complex z2;z2.citire();cout<<"z2=";z2.afisare();
cout<<"Modulul z2="<<z2.modul()<<'\n';
cout<<"Agument z2="<<z2.arg()<<'\n';
cout<<"P. reala z2="<<z2.retreal()<<"P imag
z2="<<z2.retimag()<<'\n';
z2.incrpi();cout<<"Dupa increm p
imag="<<z2.retimag()<<'\n';z2.afisare();
complex z3;z3.adun_c(z1,z2);cout<<"Adunare
z1+z2=";z3.afisare();
complex*pz1=&z1,*pz2=&z2;z3.inm_c(pz1,pz2);
cout<<"Inmultire z1+z2=";z3.afisare();
13
Date şi Funcţii statice
Implicit membrii de date sunt alocati pe obiecte. De exemplul fiecare
angajat al unei firme are propiul sau nume, cnp etc. Exista, insa unele propietati
care sunt impartite de catre toate obiectele unei clase, cum ar fi de exemplu, pentru
clasa angajat, numarul total de angajati ai unei firme, salariul mediu al firmei etc.
O varinata posibila ar fi stocarea acestor informatii intr-o variabila globală
uzuală. De exemplu, am putea utiliza o variabilă de tip întreg pentru a păstra
numărul de obiecte angajat. Problema acestei soluţii este ca variabilele globale
sunt declarate in afara clasei; pentru putea fi aduse în interiorul calsei aceste
variabile trebuie sa fie declarate de tip static.
Datele statice nu se regasesc în fiecare set de valori ale clasei, ci într-un
singur exemplar, pentru toate obiectele clasei. Datele ce apar in toate obiectele se
alocă de catre un constructor , dar cum un membru static nu face parte din nici un
obiect, nu se aloca prin constructor. Asfel, la definirea clasei, o data statică nu se
consideră definita, ci doar declarată, urmand a avea o definitie externă clasei.
Legatura cu declaraţia din interiorul clasei se face prin operatorul de rezolutie ::
precedat de numele clasei din care face parte precum si de tipul variabilei statice,
asfel:
class angajat
{
//...
Static int total_ang;
//...
}
int angajat::total_ang=0;;
Variabila total_ang va fi unica pentru toti angajatii. Se poate observa ca o
data cu definirea varbilei statice s-a realizat si inţializarea acesteia.
14
Functiile membre statice efectueaza prelucrari care nu sunt individualizate
pe obiecte, ci prelucrări care au loc la nivelul clasei. Funcţiile statice nu aparţin
unui obiect anume si deci nu beneficiaza de referinta implicita a obiectului asociat
( pointerul this). Daca funcţia membră statică operează pe o data nestatică a clasei,
obiectul trebuie transmis explicit ca parametru, in timp ce cu datele membre
statice, lucreaza in mod direct.
Programul următor redefineşte clasa angajat, prin adaugarea datelor membre
statice total_ang si total_b care vor retine numarul total de angajati si numarul
total de barbati. Clasa va contine si trei functii membre statice care vor returna
valorile reţinute în cele două date statice.
class angajat
{
private:
int varsta;
float salariul;
char gen;
static int total_ang;
static int total_b;
public:
char nume[20];
angajat()
{strcpy(nume,”Anonim”);
varsta=0;
salariul=0;
total_ang++;
}
~angajat(){total_ang--;}
15
void seteaza_valori()
{cout<<”Numele:”
cin>>nume;
cout>>”salariul:”
cin>>salariul;
cout<<”varsta”
cin>>varsta;
}
static int spune_total_ang() {return total_ang;}
static int spune_total_b(){return total_b;}
static int numara_total_b (angajat *ang)
{if(ang->gen==”B”) total_b++;}
void afisare()
{cout<<”nume: ”<<nume<<endl;
cout<<”varsta: ”<<varsta<<endl;
cout<<”salariul: ” <<salariul;
}
}
int angajat::total_ang=0;
int angajat::total_b=0;
void main()
{ angajat tab[20];
int i, n;
cout<<"\n Cate obiecte doriti sa creati?";
cin>>n;
for (i=0;i<n;i++)
{tab[i].seteaza_valori();
16
angajat::numara_total_b(&tab[i]);
}
cout<<”firma are un total de
angajati”<<angajat::spune_total_ang;
cout<<”din care”<<angajat::spune_total_b<<” sunt
barbati”;
}
In programul de mai sus se remarca faptul ca intretinerea variabilei total_ang
cade in sarcina constructorilor si a destructorului, care incrementeaza sau
decrementreaza acestă variabilă. De asemenea, funcţia membră statică
numara_total_b() primeste ca parametru referinţa la un obiect de tip angajat pentru
că utilizează data membră gen care nu este statică. Întrucât variabilele membru
statice nu apartin unui obiect anume, ele pot fi accesate si prin prefixarea numelui
clasei de operatorul “::”.
17
Concluzii
Pe măsuta evoluării metodelor şi tehinicilor de programare se observă o
tendinţă tot mai mare spre crearea unor programe de volume foarte mari, care
cuprind şi operează cu masive de date imense. La momentul actual marea
majoritate a proiectelor realizate atît de profesionişti cît şi de amatori sunt niste
sisteme complexe care nu pot fi create şi gestionate de o singura persoană. Ca o
soluţie pentu situaţia creată a fost descompunerea programelor în mai multe părţi
componente pentru a putea fi mai uşor de creat şi administrat. În vederea acestui
fapt, au fost concepute, pe măsura dezvoltării tehnologiilor şi a gîndirii inginereşti,
diferite metode de programare precum programarea procedurlă, modulară şi
orientată pe obiecte. Fiecare din aceste metode îşi găseşte aplicare în practică în
dependenţă de fiecare caz în parte cu care se confruntă programatorii. Programarea
orientată pe obiecte reprezintă o viziune totalmente nouă asupra gîndirii unei
probleme, fiind cea mai eficientă metodă de programare atunci cînd este vorba de
proiecte masive.
În C++ se observă un şir de îmbunătăţiri faţă de versiune mai veche, C,
printre care fiins şi aplicarea funcţiilor inline, care se utilizează atunci cînd se
lucrează cu funcţii complicate, corpul cărora are mai multe instrucţiuni. Utilizarea
acestui tip de funcţii anunţă compilatorul să nu mai genereze instrucţiunile în cod
maşină necesare apelului şi revenirii, ceea ce conduce la mărirea timpului de
compilare în favoarea micşorării timpului de execuţie. Utilizarea funcţiilor inline
se justifică doar în situaţiile în care codul generat de compilator pentru execuţia
corpului funcţiei este mai mic decât codul generat pentru apel şi revenire.
Datele statice se utilizează pentru ca toate obiectele clasei să aiba acces la
datele care sunt comune, şi în acelaşi timp sa fie economisit spaţiul ocupat de date
în caz ca ar fi fost nevoie sa fie cîte un set de date aparte pentru fiecare obiect al
clasei. Astfel aceste elemente noi ale tehnicilor de programare permit eficientizarea
18
procesului prin simplificarea operaţiunilor şi micşorarea timpului de execuţie.
Functiile membre statice efectueaza prelucrari care nu sunt individualizate pe
obiecte, ci prelucrări care au loc la nivelul clasei. Funcţiile statice nu aparţin unui
obiect anume si deci nu beneficiaza de referinta implicita a obiectului asociat
( pointerul this). Daca funcţia membră statică operează pe o data nestatică a clasei,
obiectul trebuie transmis explicit ca parametru, in timp ce cu datele membre
statice, lucreaza in mod direct.
Bibliografie
Oprea M., Programare orientată pe obiecte - Exemple în limbajul C+
+, Editura Matrix Rom.
Dr. Kris Jamasa, Totul despre C si C++, Editura Teora.
Ion Smeureanu, Programare orientată pe obiecte în Limbajul C++,
Editura CISON, Bucureşti 2005.
Liviu Negrescu, Limbajul C++ , Editura ALBASTRA , Cluj 2000.
Luminita Duţa, Programarea calculatoarelor în limbajul C++ ,
Editura Cetatea de Scaun 2006.
19