http://alpha-thema-online.uv.ro
Fa-ti si tu propriul forum, pe www.myforum.ro!
Pagina de start a forumului http://alpha-thema-online.uv.ro Autentificare Inregistrare FAQ Membri Cautare

Pagina de start a forumului http://alpha-thema-online.uv.ro » C / C++ / C# » [C++]Manual C++ part. 2
Creaza un subiect nou  Raspunde la subiect Subiectul anterior :: Subiectul urmator 
[C++]Manual C++ part. 2
MesajTrimis: 24/08/2007 13:44 Raspunde cu citat (quote)
Ad_Infinitum
Administrator
Administrator
Ad_Infinitum
Data inscrierii: 23/Iun/2007
Mesaje: 212




1.1.1.1Referinþe

O referinþã (reference) este un nume alternativ al unui obiect. întrebuințarea principalã a referinþelor este în specificarea argumentelor ºi a valorilor returnate de funcþii, în general ºi pentru supraîncãrcarea operatorilor în special. Notaþia X& înseamnã referinþã la obiectul cu numele X. De exemplu:

int i = 1;
int& r = i; // r ºi i se referã la aceeaºi entitate
int x = r; // x = 1
r++; // i = 2;

Implementarea obiºnuitã a unei referinþe este printr-un pointer constant care este dereferenþiat de fiecare datã când este utilizat.
Aºa cum se poate observa, pentru definirea unei referinþe se foloseºte operatorul adresã &, dar diferã tipul construcþiei în care este folosit. De exemplu:

int a = 5;
int* pi = &a; // & calculeazã adresa;
// pi este adresa lui a
int& r = a; // & introduce o referinta;
// r este o referinþã (alt nume) pt. a

O referinþã este utilizatã ca argument pentru o funcþie 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 referinþelor este pentru definirea funcþiilor care pot fi folosite atât ca membru drept cât ºi ca penis stâng 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 execuþia acestui program se obþine mesajul:
1 2 7 4
Deoarece valoarea returnatã de funcþie este referinþa (numele) unui element al vectorului, acesta poate fi modificat prin folosirea funcþiei fr() ca membru stâng al egalitãþii.

1.1.1.2Funcþii

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 ºi parametrii funcþiei. Dacã o funcþia nu are argumente, atunci lista din parantezele rotunde este vidã. Notaþia din C: f(void) este redundantã.
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 propriu ei ºi nu poate fi accesat din afara acesteia (nici printr-o instrucþiune goto). Trunchiul 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 de tip 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 returnarea unei valori de tip întreg.

Prototipurile funcþiilor. Pentru apelul unei funcþii este necesarã cunoaºterea definiþiei sau a prototipului acesteia. Prototipul unei funcþii este de forma:
tip_returnat nume_func(tip1 arg1,……., tipn argn);
Numele argumentelor formale sunt opþionale în prototipul unei funcþii. Prototipurile consimt compilatorului sã verifice tipurile argumentelor de apel ºi sã semnaleze eroare la conversii ilegale. Spre deosebire de limbajul C, unde este admisã ºi simpla declaraþie a numelui funcþiei (fãrã 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, dublu f){
/*.……..*/
double t = a*f;
return t;
}

La compilare apare o eroare datoritã apelului funcþiei f1(), care nu este definitã, nici declaratã prin prototip în domeniul funcþiei apelante fp() ºi o eroare datorat apelului funcþiei f3() cu un argument (argumentul al doilea) care nu poate fi convertit la tipul argumentului formal.

Transferul argumentelor funcþiilor. La apelul unei funcþii, argumentele naturale (se mai numesc ºi efective) de apel iniþializeazã argumentele formale din declaraþia funcþiei, în ordinea din declaraþie. Argumentele unei funcþii 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 corespunzãtor al funcþiei. În acest caz, modificãrile efectuate asupra argumentului funcþiei nu modificã argumentul real.
În apelul prin referinþã este accesatã direct variabila din argumentul real transmis funcþiei, care poate fi deci modificatã. Ca exemplificare, se presupune cã se doreºte o funcþie swap() care sã realizeze intershimbul între valorile a douã variabile. Dacã nu se folosesc referinþe, argumentele de apel ale funcþiei trebuie sã fie pointeri la variabilele respective, pentru a fi accesate ºi modificate. Funcþia cu argumente pointeri aratã astfel:

void swap(int* x, int* y){
int t;
t = *x; // dereferentiere
*x = *y;
*y = t;
}

Aceeaºi funcþie swap(), întrebuințând 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ã implementãri ºi cã, în mod evident, referinþa foloseºte adresa variabilei pentru a o putea modifica (deci un pointer). Dar, în cazul referinþelor, pointerul ºi defererenþierea sunt ascunse, programatorul nu trebuie sã le introducã explicit, programul rezultat este mai concis ºi mai clar.
Referinþele sunt deosebit de utile în apelul funcþiilor a cãror argumente sunt obiecte de mărimi mari ºi copierea lor în argumentele formale (plasate în segmentul stivã al programului) ar fi foarte ineficientã.

Argumente implicite ale funcþiilor. Se întâmplã frecvent ca o funcþie sã aibã un numãr mai mare de argumente decât 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 declaraþia funcþiei se prevede o expresie de iniþializare a acestui argument, iar din apel poate sã lipseascã valoarea argumentului corespunzãtor.
De exemplu, o funcþie 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 sfârºitul listei pot fi argumente implicite. De exemplu, este eronatã urmãtoarea declaraþie:

void setdata(int zi, int luna=9, int an); // eroare

Pointeri la funcþii. Dat fiind cã o funcþie are o localizare în memorie, aceastã valoare poate fi atribuitã unui pointer. Adresa unei funcþiei este punctul de intrare în funcþie ºi, de aceea, funcþia poate fi apelatã utilizând un pointer.
Tipul unui pointer la o funcþie se defineºte folosind tipul valorii returnate ºi tipurile argumentelor formale ale funcþiei. Înainte de apelul unei funcþii prin pointer, trebuie sã fie definitã valoarea pointerului folosind numele funcþiei. 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
}

Deºi din acest exemplu simplu nu reiese care ar putea fi avantajul întrebuințării apelului prin pointer a unei funcþii, existã totusi situaþii îndată ce apelul prin pointeri este mai avantajos. De exemplu, poate sã fie înlocuitã o instrucþiune switch care selecteazã dintre mai multe apeluri de funcþii, cu un vector de funcþii care se pot apela prin pointerii corespunzãtori.

Supraîncãrcarea funcþiilor. În limbajul C, fiecare funcþie definitã în program trebuie sã aibã un nume diferit. În C++, mai numeroase funcþii pot avea acelaºi nume în acelaºi domeniu de definiþie, dacã se pot diferenþia prin numãrul sau tipul argumentelor de apel. Acest mecanism se numeºte supraîncãrcarea funcþiilor (function overloading). În traducere, se mai întrebuinþeazã ºi denumirile de suprapunere, redefinire, supradefinire; în textul de faþã este preferat termenul de supraîncãrcare. Supraîncãrcarea se poate aplica atât funcþiilor cât ºi operatorilor; în secþiunea aceasta este prezentatã supraîncãrcarea funcþiilor, iar supraîncãrcarea operatorilor este descrisã în secþiunea 4.
Dacã în acelaºi domeniu sunt definite mai multe funcþii cu acelaºi nume, la fiecare apel se selecteazã funcþia corectã prin compararea tipurilor argumentelor reale de apel cu tipurile argumentelor formale ale funcþiei.
De exemplu,

double abs(double);
int abs(int);

abs(1); // apeleaza abs(int)
abs(1.0); // apeleza abs(double)

Acceptarea mai multor versiuni ale unei funcþii cu acelaºi nume este condiþionatã de posibilitatea selectãrii fãrã ambiguitate a uneia dintre astea dupã tipul sau numãrul argumentelor. Nu este admis ca funcþiile sã difere doar prin tipul returnat.
Douã funcþii declarate cu acelaºi nume se referã la aceeaºi funcþie dacã sunt în acelaºi domeniu ºi au numãr ºi modele identice de argumente. O funcþie declaratã local nu este în acelaºi domeniu cu o funcþie cu domeniul la nivel de fiºier. 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
}

Situaþii asemãnãtoare, de ascundere ºi nu de supraîncãrcare în domenii diferite vor mai fi prezentate în secþiunea 5, dedicatã claselor derivate.
Selecþia funcþiei la apel se bazeazã pe “cea mai bunã potrivire a argumentelor” (best argument matching). Dacã printre funcþiile supraîncãrcate existã o funcþie care are toate argumentele cu feluri identice cu argumentele din apel, atunci aceastã funcþie prezintã cea mai bunã potrivire a argumentelor ºi este selectatã.
Se considerã de exemplu funcþia supraîncãrcatã de calcul a ridicãrii la o vigoare a unui numãr, funcþia power():

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

În apelurile:

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

versiunea funcþiei power() este selectatã pe baza coincidenþei 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) cãtre 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 informaþie). 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); // selecteaza power(long, int)
power((int)a, b); // selecteaza power(int, int)
power(c,a); // selecteaza power(double, int)
power(c,d); // selecteaza power(double, double)
power(e,d); // selecteaza 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ã selecþia unei versiuni a funcþiei (pe baza criteriului de cea mai bunã potrivire a argumentelor) este unicã. Conversiile între date de tipuri definite de utilizatori sunt prezentate în secþiunea 4, dedicatã supraîncãrcãrii operatorilor.

1.1.1.3Constante simbolice

O constantã simbolicã (sau constantã cu nume) este un nume a cãrui valoare nu poate fi modificatã în cursul programului. În C++ existã trei modalitãþi de a defini constante simbolice:

Orice valoare, de orice tip care poate primi un nume, poate fi folositã ca o constantã simbolicã prin adãugarea cuvântului-cheie const în declaraþia acesteia.
Orice nume de funcþie sau de tablou este o constantã simbolicã.
O mulþime de constante întregi poate fi definitã ca o enumeraþie.

De exemplu, urmãtoarele declaraþii introduc constante simbolice prin folosirea cuvântului-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 iniþializate în declaraþie. Încercarea de modificare ulterioarã este detectatã ca eroare în momentul compilãrii:

val++; // eroare
d = 200; // eroare

Cuvântul-cheie const modificã tipul obiectului, restricþionând modul în care acesta poate fi folosit.
Un aspect mai aparte ºi intens întrebuințat în programare, este acela de a declara pointeri la constante. Atunci când se foloseºte un pointer, sunt implicate douã obiecte: pointerul însuºi ºi obiectul cãtre care indicã pointerul.
Prin prefixarea declaraþiei unui pointer cu cuvântul const, obiectul indicat este fãcut constant, nu pointerul însuºi. 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 însuºi sã fie constant, se foloseºte 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 funcþiilor. Prin declararea unui argument de tip pointer la constantã, este interzisã modificarea de cãtre funcþie a obiectului indicat. De exemplu:
char* strcpy(char* d, const char* s);
În aceastã funcþie ºirul s nu poate fi modificat.
În mod asemãnãtor, specificatorul const care însoþeºte un argument tip referinþã la apelul unei funcþii, împiedicã modificarea acestuia de cãtre funcþia respectivã.

Enumeraþii. O altã metodã de a defini constante întregi este prin enumeraþie. De exemplu, enumeraþia:

enum {
INPUT,
OUTPUT,
INOUT,
};

este echivalentã cu declararea urmãtoarelor constante:

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

Valorile asignate implicit constantelor încep de la 0 ºi sunt în ordine crescãtoare. Enumeraþiile pot avea nume. De exemplu:

enum states{
good,
bad,
fail
};
void fe (states st ){
if (st != good){
//….
}
}

Un nume declarat de tipul unei enumeraþii poate lua absolut orice valoare particularã cuprinsã în enumeraþie.
În sfârºit, este posibilã iniþialzarea elementelor unei enumeraþii:

enum states{
good = 0x00,
bad = 0x01,
fail = 0x10
};
Valorile de iniþializare nu trebuie neapãrat sã ia valori succesive.

1.1.1.4Specificatorul typedef

Declaraþiile care conþin cuvântul-cheie typedef declarã identificatori care pot fi folosiþi pentru denumirea tipurilor fundamentale sau derivate în domeniul de definiþie al acestuia. O declaraþie care conþine 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 declaraþiile:

typedef int KM, *PKM;
KM dist;
PKM pd;

KM este de tip întreg, iar pd este pointer la întreg.
O structurã fãrã nume definitã printr-un specificator typedef capãtã numele introdus de acesta:

typedef struct { /*……*/} S; //structura este denumitã S.
S struct1;

Acesta este modul obiºnuit de declaraþie în C, unde numele unei structuri nu poate fi folosit ca un nume de tip fãrã sã fie însoþit de cuvântul-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 fãrã typedef:

struct SC{/*...*/};
SC struct2;

1.1.1.5Specificatorul volatile

Specificatorul volatile indicã faptul cã valoarea obiectului poate fi modificatã pe alte cãi decât cele declarate explicit în program. De exemplu, adresa unei variabile globale poate fi transmisã unei rutine de interfaþã a sistemului, care modificã valoarea acesteia, fãrã o instrucþiune explicitã din program. Acest lucru este important, fiindcă majoritatea compilatoarelor de C ºi C++ optimizeazã automat evaluarea expresiilor prin presupunearea cã o variabilã rãmâne neschimbatã atâta timp cât nu apare în partea stângã a unei operaþii de asignare. Specificatorul volatile împiedicã aplicarea unor astfel de optimizãri.

1.1.1.6Clase, structuri, uniuni

Pe lângã tipurile de date fundamentale ale limbajului, în C++ se pot defini tipuri de date noi prin definirea unor clase. Aceste soiuri se numesc tipuri definite de utilizator (user-defined types) ºi se pot utiliza într-un mod asemãnãtor utilizãrii 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, aºa cum îl preferã autorul limbajului [Stroustrup 94]. De exemplu, declaraþia:

class X{
// trupul clasei X
// declaraþii de date ºi funcþii membre
};

este ºi o definiþie a clasei X, care introduce un tip nou de date. Prin aceastã definiþie 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 secþiunea urmãtoare. În acest punct au fost introduse doar pentru precizãrile legate de tipuri, declaraþii ºi definiþii.


Expresii ºi operatori

O formă este o secvenþã de operatori ºi operanzi care specificã executarea unor calcule. O expresie poate cauzează un rezultat sau urmări laterale (side effects).
În C++ operatorii pot fi supraîncãrcaþi, adicã li se poate atribui un anumit mod de operare atunci când se aplicã tipurilor definite de utilizator (clase). Operatorii supraîncãrcaþi urmeazã aceleaºi reguli sintactice ca ºi operatorii normali, dar operaþiile efectuate pot sã difere de ale acestora. Supraîncãrcarea operatorilor nu poate modifica regulile de aplicare ale acestora asupra tipurilor pentru care ei sunt definiþi în limbaj.
Ordinea de evaluare a unei subexpresii este determinatã de precedenþa (precedence) ºi gruparea operatorilor. Regulile matematice obiºnuite 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 funcþie este denumitã valoare stânga (left value, prescurtat lvalue). Aceastã denumire provine din faptul cã o astfel de valoare poate fi folositã ca membru stâng al unei expresii de asignare: E1 = E2. Operandul E1 trebuie sã fie expresie lvalue. În expresii, unii operatori produc expresii lvalue, alþii necesitã expresii lvalue. O expresie lvalue este modificabilã dacã nu este numele unei funcþii, numele unui tablou sau constantã.


1.1.2Operatori specifici C++

Majoritatea operatorilor C++ sunt preluaþi din limbajul C, cu aceeaºi sintaxã ºi reguli de operare. În plus faþã de operatorii C, în C++ mai sunt introduºi urmãtorii operatori:

operatorul de rezoluþie (:Smile,
operatorul de lansare excepþie (throw),
operatorii de alocare-eliberare dinamicã a memoriei new ºi delete.

Operatorul de lansare excepþie (throw) este descris în secþiunea 8. Ceilalþi operatori sunt descriºi în continuare.

1.1.2.1Operatorul de rezoluþie

Operatorul de rezoluþie (:Smile este folosit pentru modificarea domeniului de vizibilitate al unui nume. Pentru acest operator (scope resolution operator), în traduceri se mai întâlnesc termenii de operator de domeniu sau operator de acces. Operatorul de rezoluþie îngăduie folosirea unui identificator într-un bloc în care el nu este vizibil. Dacã operatorul de rezoluþie 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 rezoluþie a fost folosit pentru a accesa variabila globalã g, ascunsã de variabila localã cu acelaºi nume din funcþie. Pentru că utilizarea cea mai extinsã a operatorului de rezoluþie este legatã de utilizarea claselor, el va fi reluat pe parcursul secþiunilor urmãtoare.

1.1.2.2Operatorii de alocare-eliberare dinamicã a memoriei.

În limbajul C se pot aloca dinamic zone de memorie în memoria liberã (heap) folosind funcþii de bibliotecã (de exemplu, malloc(), calloc(), realloc()) ºi se pot elibera folosind funcþia free(). La aceste posibilitãþi, care se pãstreazã în continuare în C++, se adaugã posibilitatea folosirii operatorilor de alocare ºi eliberare dinamicã a memoriei, new ºi delete. Aceºti operatori unari prezintã avantaje substanþiale faþã de funcþiile de alocare din C ºi de aceea sunt în mod evident preferaþi în programele scrise în C++.
Pentru alocarea unei singure date (obiect), operatorul new are urmãtoarea 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 execuþia operatorului new, iar initializare este o expresie care depinde de tipul datei ºi permite iniþializarea 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 mărime dim, este urmãtoarea:
tip_data* p = new tip_data[dim];
La alocarea unui vector nu se poate transmite o expresie de iniþializare a zonei de memorie alocatã.
Operatorul delete elibereazã o zonã din memoria heap. El poate avea una din urmãtoarele 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 funcþii free()). Folosirea operatorului delete cu un pointer invalid este o operaþie cu rezultat nedefinit, cel mai adesea producând erori de execuþie grave.
Cea de-a doua formã a operatorului delete[] se foloseºte pentru eliberarea unei zone de memorie ocupatã de un vector de date. Pentru tipurile de date predefinite ale limbajului, se poate folosi ºi întâia formã pentru eliberarea unui vector, dar, în cazul obiectelor de tipuri definite de utilizator, acest lucru nu mai este valabil. Aceastã situaþie va fi detaliatã în secþiunea urmãtoare.
Câteva exemple de exploatare a operatorilor new ºi delete:

int *pi = new int(3); // alocare int ºi iniþializare
double *pd = new double; // alocare double neinitializat
char *pc1 = new char[12]; // vector de 12 caractere
char *pc2 = new char[20]; // vector de 20 caractere
delete pi;
delete pd;
delete pc1;
delete []pc2;

În legãturã cu existenþa celor douã modalitãþi de alocare dinamicã, prin operatorii new-delete sau prin funcþiile de bibliotecã malloc-free, fãrã sã fie o regulã precisã, combinarea acestora este bine sã fie evitatã, căci nu existã garanþia compatibilitãþii între ele.

1.1.3Precedenþa operatorilor

În tabelul urmãtor sunt prezentaþi concis operatorii din limbajul C++ grupaþi în ordinea precedenþei lor. În fiecare compartiment sunt trecuþi operatorii cu aceeaºi precedenþã. Un operator dat are o precedenþã mai ridicatã decât un altul dintr-un compartiment aflat mai jos în tabel. Operatorii se aplicã în ordinea precedenþei lor. De exemplu, a+b*c înseamnã a+(b*c), deoarece înmulþirea (*) are o precedenþã mai ridicatã decât adunarea (+).


Operatori C++
::
operator rezoluþie
nume_clasã::membru
::
nume global
::nume
.
selecþie membru
obiect.membru
->
selecþie membru
pointer->membru
[]
indexare
pointer[expr]
()
apel funcþie
expr (lista_expr)
()
conversie explicitã
tip(list_expr)
sizeof
dimensiune obiect
sizeof expr
sizeof
dimensiune tip
sizeof (tip)
++
post incrementare
lvalue++
++
pre incrementare
++lvalue
--
post decrementare
lvalue--
--
pre decrementare
--lvalue
~
complement
~expr
!
negaþie
!expr
-
minus unar
-expr
+
plus unar
+expr
&
adresã
&lvalue
*
derefenþiere
*expr
new
alocare
new tip
delete
eliberare(dezalocare)
delete pointer
delete[]
eliberare tablou
delete[]pointer
()
conversie cast
(tip)expr
.*
selecþie membru
obiect.*pointer_la_membru
->*
selecþie membru
pointer->*pointer_la_membru
*
înmulþire
expr * expr
/
împãrþire
expr / expr
%
modulo
expr % expr
+
adunare
expr + expr
-
scãdere
expr - expr
<<
depl. logicã stânga
expr << expr
>>
depl. logicã dreapta
expr >> expr
<
mai mic
expr < expr
<=
mai mic sau egal
expr <= expr
>
mai mare
expr > expr
>=
mai mare sau egal
expr >=expr
==
egal
expr == expr
!=
diferit
expr != expr
&
AND orientat pe biþi
expr & expr
^
XOR orientat pe biþi
expr ^ expr
|
OR orientat pe biþi
expr | expr
&&
AND logic (ªI)
expr && expr
||
OR losic (SAU)
expr || expr
?:
expresie condiþ.
expr ? expr : expr
=
asignare simplã
lvalue = expr
*=
înmulþire ºi asignare
expr *= expr
/=
împãrþire ºi asignare
expr /= expr
%=
modulo ºi asignare
expr %= expr
+=
adunare ºi asignare
expr += expr
-=
scãdere ºi asignare
expr -= expr
<<=
depl. stg ºi asignare
expr <<= expr
>>=
depl. dr. ºi asignare
expr >>= expr
&=
AND ºi asignare
expr &= expr
|=
OR ºi asignare
expr |= expr
^=
XOR ºi asignare
expr ^= expr
throw
lansare excepþie
throw expresie
,
virgulã(secvenþiere)
expr, expr



Conversii standard

Unii operatori pot provoca conversia valorii unui operand de la un tip la altul, în funcþie de tipul operanzilor. Conversiile standard executate pentru tipurile predefinite în C++ sunt identice cu cele din limbajul C ºi de aceea vor fi descrise teribil de succint. Astfel de conversii intervin în urmãtoarele situaþii:

Conversia unor operanzi de tipuri diferite într-o expresie aritmeticã.
Conversia unui argument de apel al unei funcþii la tipul argumentului formal corespunzãtor.
Conversia valorii returnate de o funcþie din tipul folosit în instrucþiunea return în tipul din definiþia funcþiei.

În toate aceste situaþii se respectã câteva reguli de conversie care sunt descrise în continuare.

1.1.4Conversiile aritmetice

Atunci când într-o expresie sunt combinate constante ºi variabile de tipuri diferite, ele sunt convertite la acelaºi tip, în general în tipul cel mai mare, acþiune care se numeºte promovarea tipului. Mai întâi toate valorile de tip char ºi short int sunt convertite în int (promovare la întreg). Dupã aceasta, toate celelate conversii sunt efectuate dupã urmãtorul algoritm:

if (un element este long double)
urmãtorul este convertit în long double
else if (un element este double)
urmãtorul este convertit în double
else if (un element este float)
urmãtorul este convertit în float
else if (un element este unsigned long)
urmãtorul este convertit în unsigned long
else if (un element este long int ºi
urmãtorul este unsigned int){
if( long int poate reprezenta toate
valorile unui unsigned int)
unsigned int este convertit în long int
else amândouã sunt convertite
în unsigned long int
}
else if (un element este unsigned int)
urmãtorul este convertit în unsigned int

Dupã aplicarea acestor reguli de conversie, fiecare pereche de elemente este de aceslaºi tip, iar rezultatul fiecãrei operaþii este de acelaºi tip cu cel al ambelor elemente.

2.5.1Conversia pointerilor

Urmãtoarele conversii standard se aplicã pointerilor:

O expresie constantã evaluatã la zero este convertitã într-un pointer, numit pointer nul. Un pointer nul nu este neapãrat reprezentat ca un întreg cu valoarea 0.
Un pointer la orice valoare neconstantã ºi nevolatilã poate fi convertit în void*.
Un pointer la funcþie poate fi convertit în void*, cu condiþia ca un astfel de pointer sã aibã capacitate de reprezentare suficient de mare.
Numele unui tablou poate fi convertit în pointer la debutantul element al tabloului.

Conversiile între pointerii claselor de bazã ºi derivate, precum ºi conversia între referinþe la clase de bazã ºi derivate sunt prezentate în secþiunea 5.
Alte conversii se obþin prin:

Iniþializarea obiectelor de tipuri definite de utilizator (clase);
Supraîncãrcarea operatorului de conversie.

Acestea sunt descrise în secþiunile urmãtoare.

Conversiile între tipurile de date din C++ se supun principiului tipizãrii propriu modelului obiect. Se poate observa faptul cã C++ este un limbaj hibrid din punct de vedere al tipizãrii: sunt verificate conversiile între tipurile de date ºi sunt rejectate acele conversii considerate neadmisibile. Pe de altã parte, limbajul admite forþarea conversiilor (prin operatorul de conversie cast), situaþie în care este inhibatã verificarea de tip a operanzilor. Dar aceastã forþare se face pe rãspunderea programatorului, care are probabilitatea sã le prevadã numai pe acelea care le considerã corecte.


Fiºierele componente ale unui program C++

Orice program C++ care nu este cumplit de modest este alcãtuit din mai multe unitãþi de compilare, numite convenþional fiºiere.
Din punct de vedere al limbajului C++, un fiºier reprezintã un domeniu de definiþie (domeniul fiºier), care este domeniul pentru funcþiile globale de tip static ºi inline ºi pentru variabilele globale de tip static. Un fiºier este, de asemenea, o unitate de memorare în sistemul de fiºiere ºi o unitate de compilare (un modul).
Dezvoltarea unui program într-un solitar fiºier este practic imposibil, deoarece sunt apelate funcþii din biblioteci ºi funcþii ale sistemului de operare care sunt memorate în mai multe fiºiere. Chiar ºi partea de program scrisã de utilizator este inconvenabil sã fie toatã cuprinsã într-un singur fiºier, datoritã dificultãþii de organizare ºi de evidenþiere a diferitelor pãrþi al programului. Mai mult, dacã programul este curins într-o singurã unitate de compilare, indiferent ce modificare trebuie urmatã de recompilarea întregului fiºier.
În organizare pe mai multe fiºiere a unui program, este necesar ca programatorul sã prevadã declaraþii care sã permitã analiza de cãtre compilator a fiecãrei unitãþi de compilare luatã izolat dar, în acelaºi timp o utilizare unitarã a numelor ºi a tipurilor definite. Orice sistem de programare permite o astfel de organizare ºi legare a unitãþilor compilate separat, în principal prin programul de linkare (linker).

1.1.5Linkarea modulelor

Dacã nu este specificat altfel, un nume care nu este local (definit într-o funcþie sau clasã) trebuie sã se refere la aceeaºi entitate în oricare din unitãþile de compilare (fiºiere) ale programului, adicã trebuie sã existe o singurã funcþie, valoare, tip sau obiect nelocal cu acelaºi nume. De exemplu, se considerã fiºierele:

// fisier1.cpp
int x = 0;
void f(){/* corpul functiei*/

//fisier2.cpp
extern int x;
void f();
int g() { x = f();}

Variabila x ºi funcþia f() utilizate în funcþia g() din fiºierul fisier2.cpp sunt definite în fisier1.cpp; cuvântul-cheie extern din declaraþia variabile x în fisier2.cpp este doar o declaraþie, nu o definiþie. La fel, prototipul funcþiei f()este declarat în fiºier2.cpp, iar definiþia acesteia se gãseºte în fiºier1.cpp.
Erorile de programare care se referã la definiþii multiple (în fiºiere diferite), la lipsa definiþiilor sau la neconcordanþa între definiþii ºi declaraþii sunt detectate ºi semnalate de linker.
Numele declarate static sunt locale în fiecare dintre fiºiere. Obiectele sau funcþiile cu nume vestite numai într-un fiºier au linkare internã (internal linkage) . Obiectele sau funcþiile cu nume care sunt faimoase în mai mult de un fiºier au linkare externã (external linkage). Numele tipurilor (numele claselor ºi ale enumeraþiilor) nu au nici o linkare, dar ele trebuie sã fie definite în mod unic în program.

2.5.2Fiºiere antet

Pentru a se asigura consistenþa declaraþiilor într-un program format din mai multe fiºiere se folosesc fiºiere antet (header files) care conþin informaþii de interfaþã între unitãþile programului ºi sunt incluse în fiºierele sursã. Includerea unui fiºier (cel mai probabil fiºier antet) într-un alt fiºier sursã se specificã prin directiva:
#include “nume_fisier”
La întâlnirea acestei directive, se înlocuieºte aceastã linie sursã cu întreg fiºierul nume_fisier. Numele fiºierului adăugat se încadreazã între ghilimele dacã este în directorul curent sau între paranteze ascuþite <> dacã este în directorul standard de includeri. Fiºierele antet au extensia .h, iar celelate fiºiere sursã au una din extensiile admise de compilatorul respectiv (.C, .cpp, .cxx, .cc).
Includerea fiºierelor antet în fiºierele sursã realizează recompilarea acestora la compilarea fiecãrui fiºier sursã. Acest alură poate sã nu influienþeze prea mult eficienþa de compilare, deoarece fiºierele antet conþin în general declaraþii care nu necesitã analizã intensã în compilare. Se poate, de întocmai ca sistemul de programare sã permitã utilizarea fiºierelor antet precompilate.
Un fiºier antet poate conþine:

Directive de includere #include “antet2.h”
Macro definiþii #define BOOL int
Definiþii de tipuri struct point {double x, y;};
Definiþii de constante const double epsilon = 0.1;
Enumeraþii enum state{good, false};
Definiþii funcþii inline inline char get(char*p)
{return *p++;}
Definiþii template template<class T>
class V{/* */};
Declaraþii de date extern int x;
Declaraþii de nume class X;
Declaraþii de funcþii extern int func(char c);
Comentarii /* comentariu */

Nu se introduc în fiºierele antet:

Definiþii de funcþii normale char get(char *p)
{return *p++;}
Definiþii de date int x;
Definiþii tablouri constante const int tb[] = {/* */};

Acestea nu sunt reguli impuse cu stricteþe, ci sugestii pentru un mod rezonabil de utilizare a fiºierelor antet.
Numãrul de fiºiere antet ale unui program poate sã varieze de la unul singur (pentru programele de dimensiuni reduse) la mai multe fiºiere antet, în general câte un fiºier antet pentru fiecare fiºier sursã.

Posibilitatea de împãrþire 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 modularitãþii, propriu modelului obiect.


Exerciþii propuse

E1.1 Sã se rescrie urmãtoarea instrucþiune for printr-o instrucþiune 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 funcþiile 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 funcþie strcat() care concateneazã douã siruri. Se va folosi operatorul new pentru alocarea spaþiului necesar rezultatului.

E1.4 Sã se scrie o funcþie strrev() care inverseazã poziþia caracterelor într-un ºir dat.

E1.5 Sã se scrie o funcþie atoi() care returneazã valoare de tip întreg a unui ºir de cifre zecimale. Sã se scrie o funcþie itoa() care genereazã ºirul de caractere care reprezintã un numãr întreg dat.

E1.6 Sã se scrie declaraþiile pentru urmãtoarele 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 tipãreºte 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 funcþie care afiºeazã la consolã liniile tabloului.


Sper ca atzi retzinut ceva :whistle:
Vezi profilul utilizatorului Trimite mesaj privat

[C++]Manual C++ part. 2
 Pagina de start a forumului http://alpha-thema-online.uv.ro » C / C++ / C#
Nu puteti crea un subiect nou in acest forum
Nu puteti raspunde in subiectele acestui forum
Nu puteti modifica mesajele proprii din acest forum
Nu puteti sterge mesajele proprii din acest forum
Nu puteti vota in chestionarele din acest forum
Ora este GMT + 3 ore  
Pagina 1 din 1  

  
  
 Creaza un subiect nou  Raspunde la subiect  


Fa-ti si tu propriul forum GRATUIT, pe www.myforum.ro
    Powered by phpBB © 2001-2004 phpBB Group. phpBB Skin by Vjacheslav Trushkin.