|
Domenii
Exista trei feluri de domenii: local, fisier si clasa. Local: In general, un nume declarat intr-un bloc(&9.2) este local la acel bloc si poate fi folosit in el numai dupa punctul
de declaratie si in blocurile incluse in el. Cu toate acestea, etichetele (&9.12) pot fi utilizate oriunde in functia in care ele sint declarate. Numele argumentelor formale pentru o functie se trateaza ca si cind ar fi fost declarate in blocul cel mai extern al acelei functii. Fisier: Un nume declarat in afara oricarui bloc (&9.2) sau clasa (&8.5) poate fi utilizat in fisierul in care a fost declarat dupa punctul in care a fost declarat. Clasa: Numele unui membru al clasei este local la clasa lui si
poate fi utilizat numai intr-o functie membru al acelei clase (&8.5.2), dupa un operator aplicat la un obiect din clasa lui (&7.1) sau dupa operatorul -> aplicat la un pointer spre un obiect din clasa lui (&7.1). Membri clasei statice (&8.5.1) si functiile membru pot fi referite de asemenea acolo unde numele clasei lor este in domeniu utilizind operatorul :: (&7.1). O clasa declarata intr-o clasa (&8.5.15) nu se considera un membru si numele ei l apartine la domeniul care o include.
Un nume poate fi ascuns printr-o declaratie explicita a aceluiasi nume intr-un bloc sau clasa. Un nume intr-un bloc sau clasa poate fi ascuns numai printr-un nume declarat intr-un bloc sau clasa inclusa. Un nume nelocal ascuns poate insa sa fie utilizat cind domeniul lui se specifica folosind operatorul :: (&7.1). Un nume de clasa ascuns printr-un nume non-tip poate fi insa utilizat daca este prefixat prin class, struct sau union (&8.2). Un nume enum ascuns printr-un nume non-tip poate insa sa fie utilizat daca este prefixat de enum (&8.2).
4.2 Definitii
O declaratie (&8) este o definitie daca nu este o declaratie de functie (&10) si ea contine specificatorul extern si nu are initializator sau corpul functiei sau este declaratia unui nume de clasa (&8.8).
4.3 Linkare
Un nume din domeniul fisierului care nu este declarat expli- cit ca static este comun fiecarui fisier intr-un program multifi- sier; asa este numele unei functii. Astfel de nume se spune ca sint externe. Fiecare declaratie a unui nume extern din program se refera la acelasi obiect (&5), functie (&10), tip (&8.7), clasa (&8.5), enumerare (&8.10) sau valoare de enumerare (&8.10).
Tipurile specificate in toate declaratiile unui nume extern trebuie sa fie identice. Pot exista mai multe definitii pentru un tip, o enumerare, o functie inline (&8.1) sau o constanta care nu este agregat cu conditia ca definitiile care apar in diferite fisiere sa fie identice, iar toti initializatorii sa fie expresii constante (&12). In toate celelalte cazuri, trebuie sa fie exact o definitie pentru un nume extern din program.
O implementare poate cere ca o constanta neagregat utiliza- ta unde nu este definita, sa trebuiasca sa fie declarata extern explicit si sa aiba exact o definitie in program. Aceeasi restrictie poate fi impusa si asupra functiilor inline.
4.4 Clase de memorie
Exista doua clase de memorie: clase automatice si clase statice. Obiectele automatice sint locale. Ele apar la fiecare apel al unui bloc si se elimina la iesirea din el.
Obiectele statice exista si isi mentin valorile pe timpul executiei intregului program.
Anumite obiecte nu sint asociate cu nume si viata lor se controleaza explicit utilizind operatorii new si delete; vezi &7.2 si &9.14.
4.5 Tipuri fundamentale
Obiectele declarate ca si caractere (char) sint destul de mari pentru a memora orice membru al setului de caractere implementat si daca un caracter real din acel set se memoreaza intr-o variabila caracter, valoarea ei este echivalenta cu codul intreg al acelui caracter.
Sint disponibile trei dimensiuni de tip intreg, declarate short int, int si long int. Intregii long furnizeaza memorie nu mai putina decit cei short, dar implementarea poate face ca sau intregii short sau cei long sau ambii sa fie intregi fara alte atribute (int). Intregii int au dimensiunea naturala pe masina gazda sugerata de arhitectura ei.
Fiecare enumerare (&8.10) este un set de constante denumite. Proprietatile lui enum sint identice cu cele ale lui int. Intregii fara semn asculta de legile aritmeticii modulo 2^nunde n este numarul de biti din reprezentare.
Numerele flotante in simpla precizie (float) si in dubla precizie (double) pot fi sinonime in anumite implementari.
Deoarece obiectele de tipurile anterioare pot fi interpretate in mod util ca numere, ele vor fi referite ca tipuri aritmetice. Tipurile char, int de toate dimensiunile si enum vor fi numite tip integral. Tipurile float si double vor fi numite tip floating.
Tipul void specifica o multime vida de valori. Valoarea (inexistenta) a unui obiect void nu poate fi utilizata si nu se pot aplica conversii explicite sau implicite. Deoarece o expresie void noteaza o valoare inexistenta, o astfel de expresie poate fi utilizata numai ca o expresie instructiune (&9.1) sau ca operandul sting al unei expresii cu virgula (&7.15). O expresie poate fi convertita explicit spre tipul void (&7.2).
4.6 Tipuri derivate
Exista un numar conceptual infinit de tipuri derivate construite din tipurile fundamentale:
tablouri de obiecte de un tip dat;
functii care au argumente de tipuri date si returneaza obiecte de un tip dat;
pointeri spre obiecte de un tip dat;
referinte la obiecte de un tip dat;
constante care sint valori de un tip dat;
clase ce contin o secventa de obiecte de tipuri diferite, un set de functii pentru manipularea acestor obiecte si un set de restrictii asupra accesului la aceste obiecte si
functii;
structuri care sint clase fara restrictii la acces;
reuniuni care sint structuri capabile sa contina obiecte de tipuri diferite in momente diferite.
In general aceste metode de a construi obiecte pot fi aplicate recursiv.
Un obiect de tip void* (pointer spre void) poate fi utilizat pentru a pointa spre obiecte de tip necunoscut.
5 Obiecte si Lvalori
Un obiect este o regiune de memorie; o lvaloare este o expresie care se refera la un obiect. Un exemplu evident de expresie lvaloare este numele unui obiect. Exista operatori care produc lvalori: de exemplu, daca E este o expresie de tip poin- ter, atunci *E este o expresie lvaloare care se refera la obiectul spre care pointeaza E. Numele lvaloare vine de la expresia de atribuire E1 = E2 in care operatorul sting trebuie sa fie o lvaloare. Discutia de mai jos despre operatori indica despre fiecare daca el asteapta un operand lvaloare si daca el produce o lvaloare.
6 Conversii
Un numar de operatori pot, depinzind de operanzii lor, sa provoace conversia valorii unui operand de un tip la altul. Aceasta sectiune explica rezultatul asteptat de la o astfel de conversie. Paragraful &6.6 rezuma conversiile cerute de cei mai obisnuiti operatori; ea va fi suplimentata la nevoie printr-o discutie despre fiecare operator. Paragraful &8.5.6 descrie conversiile definite de utilizator.
6.1 Caractere si Intregi
Un caracter sau un intreg scurt poate fi utilizat oriunde se poate utiliza un intreg. Conversia unui intreg scurt spre unul mai lung implica totdeauna extensie de semn; intregii sint cantitati cu semn. Daca extensia de semn apare sau nu pentru caractere este dependent de masina; vezi &2.6. Tipul explicit unsigned char forteaza ca valorile sa fie de la zero la cea mai mare valoare permisa de masina.
Pe masinile care trateaza caracterele ca fiind cu semn, caracterele ASCII sint toate pozitive. Cu toate acestea, o constanta caracter specificata cu backslash sufera o extensie de semn si poate apare negativa; de exemplu, '377' are valoarea -1.
Cind un intreg lung se converteste spre un intreg mai scurt sau spre char, se trunchiaza la stinga; bitii in plus sint pierduti.
6.2 Flotante in simpla si dubla precizie
Aritmetica in flotanta simpla precizie poate fi utilizata pentru expresii de tip float. Conversiile intre numere flotante in simpla precizie si dubla precizie sint din punct de vedere matematic corecte in masura in care permite hardware-ul.
6.3 Flotante si Intregi
Conversiile valorilor flotante spre tipul intreg tind sa fie dependente de masina; in particular directia trunchierii numerelor negative variaza de la masina la masina. Rezultatul este imprevizibil daca valoarea nu incape in spatiul prevazut.
Conversiile valorilor intregi spre flotante se rezolva bine. Se pot pierde cifre daca destinatia nu are suficienti biti.
6.4. Pointeri si Intregi
O expresie de tip intreg poate fi adunata sau scazuta dintr-un pointer;
intr-un astfel de caz primul se converteste asa cum se specifica in discutia despre operatorul de adunare. Doi pointeri spre obiecte de acelasi tip pot fi scazuti; in acest caz rezultatul se converteste spre int sau long in functie de masina; vezi &7.4.
6.5 Intregi fara semn
Ori de cite ori se combina un intreg fara semn si un intreg de tip int, ultimul se converteste spre unsigned si rezultatul este unsigned. Valoarea este cel mai mic intreg fara semn congruent cu intregul cu semn (modulo 2^dimensiunea cuvintului). In reprezentarea prin complement fata de 2, aceasta conversie este conceptuala si in realitate nu exista o schimbare in structura bitilor.
Cind un intreg fara semn se converteste spre long, valoarea rezultatului este numeric aceeasi cu cea a intregului fara semn. Astfel conversia are ca rezultat completarea cu zerouri nesemnificative la stinga.
6.6 Conversii aritmetice
Un numar mare de operatori conduc la conversii si produc rezultate de un tip similar cu tipurile descrise mai sus. Aceste conversii vor fi numite 'conversii aritmetice uzuale'.
orice operanzi de tip char, unsigned char sau short se convertesc spre int.
daca unul din operanzi este double, atunci si celalalt se converteste spre double si acesta este tipul rezultatului. altfel daca unul din operanzi este unsigned long atunci si celalalt se converteste spre unsigned long si acesta este tipul rezultatului.
altfel, daca unul dintre operanzi este long, celalalt este convertit spre long si acesta este tipul rezultatului.
altfel, daca unul din operanzi este unsigned, celalalt se converteste spre unsigned si acesta este tipul rezultatu lui.
altfel, ambii operanzi trebuie sa fie int si acesta este tipul rezultatului.
6.7 Conversii de pointeri
Conversiile urmatoare pot fi facute ori de cite ori se atribuie, se initializeaza sau se compara pointeri. constanta 0 poate fi convertita spre un pointer si se garanteaza ca aceasta valoare va produce un pointer dis tinct de orice pointer spre orice obiect.
un pointer spre orice tip poate fi convertit spre un void*.
un pointer spre o clasa poate fi convertit spre un pointer spre o clasa de baza publica a acelei clase; vezi &8.5.3. un nume al unui vector poate fi convertit spre un pointer spre primul lui element. un identificator care se declara ca 'functie ce returneaza ' cind se utilizeaza altundeva decit ca nume intr-un apel de functiei, se converteste in pointer spre 'functia ce returneaza '.
6.8 Conversii de referinte
Conversia urmatoare poate fi facuta ori de cite ori se initializeaza referintele.
o referinta la o clasa poate fi convertita spre o referin ta spre o clasa de baza publica a acelei clase; vezi &8.6.3.
7 Expresii
Precedenta operatorilor unei expresii este aceeasi cu ordinea subsectiunilor majore ale acestei subsectiuni, intii fiind precedenta cea mai inalta. Astfel, de exemplu, expresiile care se refera la operanzii lui + (&7.4) sint acele expresii care sint definite in &7.1-&7.4. Operatorii din fiecare subsectiune au aceeasi precedenta. Asociativitatea stinga sau dreapta este specificata in fiecare subsectiune pentru operatorii discutati in ea. Precedenta si asociativitatea tuturor operatorilor unei expresii este rezumata in gramatica din &14.
Altfel ordinea evaluarii expresiilor este nedefinita. In particular compilatorul considera ca el este liber sa calculeze subexpresiile in ordinea in care el considera ca acest lucru este cel mai eficient, chiar daca subexpresiile implica efecte secundare. Ordinea in care au loc efectele secundare nu este specificata. Expresiile care implica un operator comutativ si unul asociativ (*, +, &, |, ^) pot fi rearanjate arbitrar, chiar si in prezenta parantezelor; pentru a forta o ordine particulara a evaluarii trebuie sa se utilizeze un temporar explicit.
Tratarea depasirilor si verificarea impartirii intr-o evaluare de expresie este dependenta de masina. Majoritatea implementarilor existente ale lui C++ ignora depasirile de intregi, tratarea impartirii la zero si toate exceptiile flotante difera de la masina la masina si de obicei se ajusteaza printr-o functie de biblioteca.
In plus fata de intelesul standard descris in &7.2-&7.15 operatorii pot fi supraincarcati, adica li se dau sensuri cind se aplica la tipuri definite de utilizator; vezi &7.16.
7.1 Expresii primare
Expresiile primare implica . -> :: indexare si apel de functie grupindu-se de la stinga la dreapta.
expression_list:
expression
expression_list, expression
id:
identifier
operator_function_name
typedef_name :: identifier
typedef_name :: operator_function_name
primary_expression:
id
:: identifier
constant
string
this
(expression)
primary_expression[expression] primary_expression(expression_list opt)
primary_expression.id
primary_expression->id
Un identificator este o expresie primara, cu conditia ca el sa aiba o declaratie potrivita (&8). Un operator_function_name este un identificator cu un inteles special; vezi &7.16 si &8.5.11.
Operatorul :: urmat de un identificator din domeniul fisierului este acelasi lucru cu identificatorul. El permite unui obiect sa fie referentiat chiar daca identificatorul lui a fost ascuns (&4.1).
Un typedef_name (&8.8) urmat de :: si de un identificator este o expresie primara. Typedef_name trebuie sa noteze o clasa (&8.5) iar identificatorul trebuie sa noteze un membru al acelei clase. Tipul lui se specifica printr-o declaratie de identificator. Type_name poate fi ascuns printr-un nume non_type; in acest caz typedef_name poate fi inca gasit si utilizat.
O constanta este o expresie primara. Tipul ei poate fi int, long, float sau double in functie de forma ei.
Un sir este o expresie primara. Tipul lui este 'tablou de caractere'. El este de obicei convertit imediat spre un pointer spre primul lui caracter (&6.7).
Cuvintul cheie this este o variabila locala din corpul unei functii membru (vezi &8.5); este un pointer spre obiectul pentru care functia a fost apelata.
O expresie in paranteze este o expresie primara a carui tip si valoare sint identice cu a expresiei fara paranteze. Prezenta parantezelor nu afecteaza faptul ca expresia este o lvaloare. O expresie primara urmata de o expresie in paranteze patrate este o expresie primara. Sensul intuitiv este acela al unui indice. De obicei, expresia primara are tipul 'pointer spre ', expresia indice este int, iar tipul rezultatului este ''. Expresia E1[E2] este identica (prin definitie) cu *((E1) + (E2)). Toate cheile necesare intelegerii acestei notatii sint continute in aceasta sectiune impreuna cu discutiile din &7.1, &7.2 si &7.4 despre identificatori, * si respectiv +; &8.4.2 rezuma mai jos implicatiile. Apelul unei functii este o expresie primara urmata de paranteze care contin o lista de expresii separate prin virgula, care este posibil sa fie si vida, iar expresiile formeaza argumentele reale ale functiei. Expresia primara trebuie sa fie de tipul 'functie care returneaza ' sau 'pointer spre o functie care returneaza ' iar rezultatul apelului functiei este de tipul ''.
Fiecare argument formal se initializeaza cu argumentul actual al lui (&8.6). Se fac conversii standard (&6.6-&6.8) si conversii definite de utilizator (&8.5.6). O functie poate schimba valorile argumentelor ei formale, dar aceste schimbari nu pot afecta valorile argumentelor actuale exceptind cazul in care argumentele formale sint de tip referinta (&8.4). O functie poate fi declarata ca sa accepte mai putine sau mai multe
argumente decit sint specificate in declaratia de functie (&8.4). Orice argument real de tip float pentru care nu exista un argument formal se converteste inaintea apelului spre double; cel de tip char sau short se converteste spre int si ca de obicei, numele de tablouri se convertesc spre pointeri. Ordinea de evaluare a argumentelor este nedefinita prin limbaj; sa luam nota de faptul ca compilatoarele difera intre ele.
Apelurile recursive sint permise la orice functie.
O expresie primara urmata de un punct si de un identificator (sau un identificator calificat printr-un typedef_name utilizind operatorul ::) este o expresie. Prima expresie trebuie sa fie un obiect al unei clase, iar identificatorul trebuie sa numeasca un membru al acelei clase. Valoarea este membrul numit al obiectului si el este o lvaloare daca prima expresie este o lvaloare. Sa observam ca 'obiectele unei clase' pot fi structuri (&8.5.12) sau reuniuni (&8.5.13).
O expresie primara urmata de o sageata (->) si de un identificator (sau un identificator calificat printr-un typedef_name utilizind operatorul ::) este o expresie. Prima expresie trebuie sa fie un pointer la un obiect al unei clase iar identificatorul trebuie sa numeasca un membru al acelei clase. Rezultatul este o lvaloare care referentiaza membrul numit al clasei spre care pointeaza expresia pointer. Astfel expresia E1->MOS este acelasi lucru cu (*E1).MOS. Clasele se discuta in &8.5.
Daca o expresie are tipul 'referinta la ' (vezi &8.4 si &8.6.3) valoarea expresiei este obiectul notat prin referinta. O referinta poate fi gindita ca un nume al unui obiect (&8.6.3).
7.2 Operatori unari
Expresiile cu operatori unari se grupeaza de la dreapta la stinga.
unary_expression:
unary_operator expression
expression++
expression--
sizeof expression
sizeof(type_name)
(type_name) expression
simple_type_name (expression_list)
new type_name initializer_opt
new (type_name)
delete expression
delete [expression] expression
unary_operator: unul dintre
* & + - ! ~ ++ --
Operatorul unar * inseamna indirectare: expresia trebuie sa fie un pointer, iar rezultatul este o lvaloare care se refera la un obiect spre care pointeaza expresia. Daca tipul expresiei este 'pointer spre ' tipul rezultatului este ' '.
Rezultatul operatorului unar & este un pointer spre obiectul referit de operand. Operandul trebuie sa fie o lvaloare. Daca ti- pul expresiei este '', atunci tipul rezultatului este 'pointer spre '.
Rezultatul operatorului unar + este valoarea operandului sau valoarea rezultata in urma conversiilor aritmetice obisnuite. Operandul trebuie sa fie de tip aritmetic.
Rezultatul operatorului unar - este negativarea operandului sau.
Operandul trebuie sa fie de tip aritmetic. Se fac conversii aritmetice obisnuite. Negativarea unei cantitati fara semn se calculeaza scazind valoarea ei din 2^n unde n este numarul de biti dintr-un int.
Rezultatul operatorului de negatie logica ! este 1 daca valoarea operandului sau este 0, si este 0 daca valoarea operandului sau este diferita de zero. Tipul operandului este int. Se aplica la orice tip aritmetic sau la pointeri.
Operatorul ~ produce complementul fata de 1 al operandului sau. Se fac conversii aritmetice uzuale. Tipul operandului trebuie sa fie intreg.
7.2.1 Incrementare si Decrementare
Operandul prefixului ++ este incrementat. Operandul trebuie sa fie o lvaloare. Expresia ++x este echivalenta cu x += 1. Vezi discutiile despre adunare (&7.4) si operatorii de asignare (&7.14) pentru informatii despre conversii.
Operandul prefixat prin -- se decrementeaza in mod analog ca si in cazul operatorului prefix ++.
Valoarea obtinuta prin aplicarea unui operator ++ postfix este valoarea operandului. Operandul trebuie sa fie o lvaloare. Dupa ce este luat in evidenta rezultatul; obiectul este incrementat in aceeasi maniera ca si operatorul prefix ++. Tipul rezultatului este acelasi ca si tipul operandului. Valoarea obtinuta prin aplicarea unui operator -- postfix este valoarea operandului. Operandul trebuie sa fie o lvaloare. Dupa ce rezultatul este luat in evidenta, obiectul este decremen- tat in aceeasi maniera ca si pentru operatorul prefix --. Tipul rezultatului este acelasi cu al operandului.
7.2.2 Sizeof
Operatorul sizeof produce dimensiunea in octeti a operandului sau. (Un octet este nedefinit prin limbaj cu exceptia terme- nilor valorii lui sizeof. Cu toate acestea, in toate implementarile existente un octet este spatiul cerut pentru a pastra un caracter.) Cind se aplica la un tablou, rezultatul este numarul total de octeti din tablou. Dimensiunea se determina din declaratiile obiectelor dintr-o expresie. Expresia este semantic o constanta fara semn si poate fi utilizata oriunde se cere o constanta.
Operatorul sizeof se poate aplica de asemenea la un nume de tip in paranteze. In acest caz el produce dimensiunea in octeti a unui obiect de tip indicat.
7.2.3 Conversia explicita de tip
Un simple_type_name inclus optional in paranteze (&8.2) urmat de o expresie in paranteze (sau o lista de expresii daca ti- pul este o clasa cu un constructor declarat in mod corespunzator &8.5.5) face ca valoarea expresiei sa fie convertita spre tipul denumit. Pentru a exprima conversia spre un tip care nu are un nume simplu, trebuie inclus in paranteze numele tipului (&8.7). Daca numele tipului este in paranteze, expresia nu este necesar sa fie in paranteze. Aceasta constructie se numeste cast.
Un pointer poate fi convertit explicit spre orice tip de intregi care sint destul de mari pentru a le pastra. Ori de cite ori se cere un int sau long, acest lucru este dependent de masina. Functia de transformare este de asemenea dependenta de masina, dar se intentioneaza ca aceasta sa nu fie o surpriza pentru cei care cunosc structura de adresare a masinii. Detalii despre anumite masini particulare se dau in &2.6.
Un obiect de tip intreg se poate converti in mod explicit spre un pointer. Transformarea totdeauna face ca un intreg convertit dintr-un pointer sa revina inapoi la acelasi pointer, dar printre altele ea este dependenta de masina.
Un pointer spre un tip poate fi convertit explicit spre un pointer spre un alt tip. Pointerul rezultat poate provoca exceptii de adresare la utilizare daca pointerul care se converteste nu se refera la un obiect aliniat in memorie in mod cores- punzator. Se garanteaza ca un pointer spre un obiect de o dimensiune data poate fi convertit spre un pointer spre un obiect cu o dimensiune mai mica si din nou inapoi fara a face schimbari. Diferite masini pot diferi in numarul de biti in pointeri si in cerintele de aliniere pentru obiecte. Agregatele se aliniaza la limita cea mai stricta ceruta de oricare din componentele lor.
Un obiect poate fi convertit spre o clasa de obiecte numai daca a fost declarat un constructor potrivit sau un operator de conversie (&8.5.6). Un obiect poate fi convertit explicit spre o referinta de tip X& daca un pointer spre acel obiect poate fi convertit explicit spre un X*.
7.2.4 Memoria libera
Operatorul new creaza un obiect de tipul type_name (vezi &8.7) la care se aplica el. Durata de viata a unui obiect creat prin new nu este restrinsa la domeniul in care se creaza. Operatorul new returneaza un pointer la obiectul pe care il creaza. Cind acel obiect este un tablou se returneaza un pointer la primul sau element. De exemplu, atit new int, cit si new int[10] returneaza un int*. Se poate furniza un initializator pentru anumite obiecte de clasa (&8.6.2). Pentru a obtine memorie operatorul new (&7.2) va apela functia:
void* operator new(long);
Argumentul specifica numarul de octeti cerut. Memoria va fi neinitializata. Daca operatorul new() nu poate gasi cantitatea de memorie ceruta, atunci el va returna valoarea zero.
Operatorul delete va distruge un obiect creat prin operatorul new. Rezultatul este void. Operandul lui delete trebuie sa fie un pointer returnat de new. Efectul aplicarii lui delete la un pointer care nu este obtinut prin operatorul new este nedefinit. Cu toate acestea, stergind un pointer cu valoarea zero este inofensiv. Pentru a elibera memorie operatorul delete va apela functia:
void operator delete(void*);
In forma:
delete [expression] expression
cea de a doua expresie pointeaza spre un vector iar prima expresie da numarul de elemente al acelui vector. Specificarea numarului de elemente este redondant exceptind cazul cind se sterg vectori de anumite clase (vezi &8.5.8).
7.3 Operatori multiplicatori
Operatorii multiplicatori *, / si % se grupeaza de la stinga la dreapta. Se fac conversii aritmetice obisnuite.
multiplicative_expression
expression * expression
expression / expression
expression % expression
Operatorul binar * indica inmultire. Operatorul * este asociativ si expresia cu diferite inmultiri la acelasi nivel poate fi rearanjata de compilator.
Operatorul / indica impartire. Cind intregii pozitivi se impart trunchierea este zero; dar forma trunchierii este depen- denta de masina daca operandul este negativ. Pe masinile indicate in acest manual, restul are acelasi semn cu deimpartitul. Este totdeauna adevarat ca (a / b) * b + a % b este egal cu a (daca b nu este zero).
Operatorul binar % produce restul impartirii primei expresii prin cea de a doua. Se fac conversii aritmetice uzuale. Operanzii trebuie sa nu fie flotanti.
7.4 Operatori aditivi
Operatorii aditivi + si - se grupeaza de la stinga la dreapta. Se fac conversii aritmetice obisnuite. Exista unele posibilitati de tip suplimentare pentru fiecare operator.
aditive_expression:
expression + expression
expression - expression
Rezultatul operatorului + este suma operanzilor. Un pointer spre un obiect dintr-un tablou poate fi adunat cu o valoare de tip intreg. Ultimul este in toate cazurile convertit spre un deplasament inmultindu-l prin lungimea obiectului spre care pointeaza acel pointer. Rezultatul este un pointer de acelasi tip ca si pointerul original si care pointeaza spre un alt obiect din acelasi tablou, potrivit deplasamentului fata de obiectul original. Astfel daca P este un pointer spre un obiect dintr-un ta- blou, expresia P + 1 este un pointer spre obiectul urmator din tablou.
Nu sint admise alte combinatii de tip pentru pointeri.
Operatorul + este asociativ si expresiile cu diferite adunari la acelasi nivel pot fi rearanjate de compilator.Rezultatul operatorului - este diferenta dintre operanzi. Se fac conversii aritmetice obisnuite. In plus, o valoare de tip intreg poate fi scazuta dintr-un pointer si apoi se aplica aceleasi conversii ca si pentru adunare.
Daca doi pointeri spre obiecte de acelasi tip se scad, rezultatul sete convertit (impartind la lungimea obiectului) la un intreg ce reprezinta numarul de obiecte care separa obiectele pointate. Depinzind de masina, intregul rezultat poate fi de tip int sau long (&2.6). Aceasta conversie va da in general un rezultat neasteptat daca pointerii nu pointeaza spre obiecte din acelasi tablou, intrucit pointerii, chiar spre obiecte de acelasi tip, nu difera in mod necesar printr-un multiplu de lungimea obiectului.
7.5 Operatori de deplasare
Operatorii de deplasare << si >> se grupeaza de la stinga la dreapta. Ambii realizeaza conversii aritmetice obisnuite asupra operanzilor lor, fiecare din ei trebuind sa fie de tip intreg. Apoi operandul drept se converteste spre int. Tipul rezultatului este cel al operandului sting.
Rezultatul este nedefinit daca operandul drept este negativ sau este mai mare sau egal decit lungimea obiectelor in biti.
shift_expression:
expression << expression
expression >> expression
Valoarea lui E1 << E2 este E1 (interpretat ca o configuratie de biti) deplasat la stinga cu E2 biti; bitii liberi se completeaza cu zero. Deplasarea la dreapta este garantata a fi o deplasare logica (se completeaza cu 0) daca E1 este fara semn; altfel ea este aritmetica (se copiaza bitul de semn).
7.6 Operatori relationali
Operatorii de relatie se grupeaza de la stinga la dreapta, dar acest fapt nu este foarte util; a < b < c nu inseamna ceea ce s-ar parea.
relational_expression:
expression < expression
expression > expression
expression <= expression
expression >= expression
Operatorii < (mai mic decit), > (mai mare decit), <= (mai mic sau egal cu) si >= (mai mare sau egal cu) produc zero daca relatia specificata este falsa si unu daca este adevarata. Tipul rezultatului este int. Se fac conversii aritmetice obisnuite. Doi pointeri pot fi comparati; rezultatul depinde de locatiile relative din spatiul de adrese al obiectelor pointate. Comparatia de pointeri este portabila numai cind pointerii pointeaza spre obiecte din acelasi tablou.
7.7 Operatori de egalitate
equality_expression:
expression == expression
expression != expression
Operatorii == (egal) si != (diferit) sint analogi cu operatorii de relatie exceptind precedenta mai mica a lor. (Astfel a<b == c<d este 1 daca a<b si c<d au aceeasi valoare de adevar). Un pointer poate fi comparat cu zero.
7.8 Operatorul SI pe biti
and_expression:
expression & expression
Operatorul & este asociativ si expresiile care implica & pot fi rearanjate. Se executa conversii aritmetice obisnuite; rezultatul este functia si pe biti a operanzilor. Operatorii se aplica numai la operanzi intregi.
7.9 Operatorul SAU EXCLUSIV pe biti
exclusive_or_expression:
expression ^ expression
Operatorul ^ este asociativ si expresiile care implica ^ pot fi rearanjate. Se fac conversii aritmetice obisnuite; rezultatul este functia sau exclusiv pe biti al operanzilor. Operatorii se aplica numai la operanzi intregi.
7.10 Operatorul SAU INCLUSIV pe biti
inclusive_or_expression:
expression | expression
Operatorul | este asociativ si expresiile care implica | pot fi rearanjate. Se fac conversii aritmetice obisnuite; rezultatul este functia sau inclusiv pe biti a operanzilor. Operatorii se aplica numai la operanzi intregi.
7.11 Operatorul logic SI
logical_and_expression:
expression && expression
Operatorul && se grupeaza de la stinga la dreapta. El returneaza 1 daca ambii operanzi ai lui sint diferiti de zero, si zero in celelalte cazuri. Spre deosebire de &, && garanteaza evaluarea de la stinga la dreapta; mai mult decit atit, cel de al doilea operand nu se evalueaza daca primul operand este zero.Operanzii nu este necesar sa aiba acelasi tip, dar fiecare trebuie sa aiba unul din tipurile fundamentale sau sa fie un poin- ter. Rezultatul este totdeauna int.
7.12 Operatorul logic SAU
logical_or_expression:
expression || expression
Operatorul || se grupeaza de la stinga la dreapta. El retur- neaza 1 daca oricare din operanzi este diferit de zero, si zero altfel. Spre deosebire de |, || garanteaza o evaluare de la stinga la dreapta; mai mult decit atit, operandul al doilea nu este evaluat daca valoarea primului operand este diferita de 0.
Nu este necesar ca operanzii sa aiba acelasi tip, dar fiecare trebuie sa aiba unul din tipurile fundamentale sau sa fie un pointer. Rezultatul este intotdeauna int.
7.13 Operator conditional
conditional_expression:
expression ? expression : expression
Expresiile conditionale se grupeaza de la dreapta la stinga. Prima expresie se evalueaza si daca nu este zero, rezultatul este valoarea celei de a doua expresii, altfel este a celei de a treia expresii. Daca este posibil, se fac conversii aritmetice pentru a aduce expresiile a doua si a treia la un tip comun. Daca este posibil se fac conversii de pointeri pentru a aduce expresiile a doua si a treia la un tip comun. Rezultatul are tipul comun:
numai una din expresiile doi sau trei se evalueaza.
7.14 Operatori de asignare
Exista mai multi operatori de asignare si toti se grupeaza de la dreapta la stinga. Toti cer o lvaloare ca operand sting si tipul unei expresii de asignare este acela al operandului sting; aceasta lvaloare trebuie sa nu se refere la o constanta (nume de tablou, nume de functie sau const). Valoarea este valoarea memorata in operandul sting dupa ce asignarea a avut loc.
assigment_expresion;
expression assigment_operator expression
assigment_operator: unul dintre
= += -= *= /= %= >>= <<= &= ^= |=
In atribuire simpla cu =, valoarea expresiei o inlocuieste pe cea a obiectului referit prin operandul din partea stinga. Daca ambii operanzi au tipul aritmetic, operandul drept se converteste spre tipul operandului sting preparat pentru asignare. Daca argumentul sting are tipul pointer operandul drept trebuie sa fie de acelasi tip sau de un tip care poate fi convertit spre el (vezi &6.7). Ambii operanzi pot fi obiecte de aceeasi clasa. Obiectele de anumite clase nu pot fi atribuite (vezi &8.5.3).
Asignarea la un obiect de tip 'referinta la ' atribuie la obiectul notat prin referinta.
Comportamentul unei expresii de forma E1 op= E2 poate fi dedus luind echivalentul ei de forma E1 = E1 op (E2); totusi E1 se evalueaza numai o data. In += si -=, operatorul sting poate fi un pointer, caz in care operandul drept (de tip intreg) se converteste asa cum s-a explicat in &7.4; toti operanzii drepti si toti operanzii stingi non_pointer trebuie sa aiba tipul aritmetic.
7.15 Operatorul virgula
comma_expression:
expression, expression
O pereche de expresii separate printr-o virgula se evalueaza de la stinga la dreapta si valoarea expresiei din stinga este eliminata. Tipul si valoarea rezultatului este tipul si valoarea operandului din dreapta. Acest operator se grupeaza de la stinga la dreapta. In contextele in care virgulei i se da un inteles special, de exemplu in listele parametrilor reali ale functiilor (&7.1) si in listele de initializatori (&8.6), operatorul virgula asa cum a fost descris in aceasta sectiune poate sa apara numai in paranteze; de exemplu:
f(a, (t=3, t+2), c)
are trei argumente, cel de al doilea are valoarea 5.
7.16 Operatori de supraincarcare
Majoritatea operatorilor pot fi supraincarcati, adica declarati sa accepte obiectele unei clase ca operanzi (vezi &8.5.11). Nu este posibil sa se schimbe precedenta operatorilor si nici sa se schimbe sensul operatorilor aplicati la obiecte non_clasa. In telesul predefinit al operatorilor = si & (unar) aplicati la obiectele unei clase se poate schimba.
Identitatile dintre operatori aplicate la tipurile de baza (de exemplu a++ <=> a += 1) nu este necesar sa se indeplineasca pentru operatorii aplicati la tipurile clasa. Unii operatori, de exemplu cel de asignare, cere ca un operand sa fie o lvaloare cind se aplica la tipuri de baza; aceasta nu se cere cind operatorii sint declarati pentru tipuri clasa.
7.16.1 Operatori unari
Un operator unar, daca este prefix sau postfix, poate fi definit printr-o functie membru (vezi &8.5.4) fara apartenente sau o functie prieten (vezi &8.5.10) care are un argument dar nu ambele. Astfel, pentru operatorul unar @, atit x@, cit si @x pot fi interpretati fie ca x.operator@(), fie ca operator@(x). Cind operatorii ++ si -- sint supraincarcati, nu este posibil sa se faca distinctie intre aplicatia prefix si cea postfix.
7.16.2 Operatori binari
Un operator binar poate fi definit sau printr-o functie membru care are un argument sau printr-o functie prieten care are doi parametri, dar nu ambele. Astfel, pentru un operator binar @, x@y poate fi interpretat sau x.operator@(y) sau operator@(x, y).
7.16.3 Operatori speciali
Apelul de functie
primary_expression (expression_list_opt)
si indexarea
primary_expression[expression]
se considera operatori binari. Numele functiilor care se definesc sint operator() si operator[]. Astfel, un apel x(arg) este interpretat ca x.operator()(arg) pentru un obiect de clasa x. O expresie de forma x[y] se interpreteaza ca x.operator[](y).
8 Declaratii
Declaratiile se utilizeaza pentru a specifica interpretarea data fiecarui identificator; ele nu neaparat rezerva memorie asociata cu identificatorul. Declaratiile au forma:
declaration:
decl_specifiers_opt declarator_list_opt;
name_declaration
asm_declaration
Declaratorii din lista declaratiilor contin identificatorii de declarat. Numai in definitiile functiilor externe (&10) sau declaratiile de functii externe se pot omite decl_specifier. Numai cind, declarind o clasa (&8.5) sau o enumerare (&8.10), adica, atunci cind decl_specifier este un specificator de clasa (class_specifier), sau de enumerare (enum_specifier) se poate ca declarator_list sa fie vida. Name_declarations se descriu in &8.8;
declaratiile asm se descriu in &8.11.
decl_specifier:
sc_specifier
type_specifier
fct_specifier
friend
typedef
decl_specifiers:
decl_specifier decl_specifiers_opt
Lista trebuie sa fie autoconsistenta in modul descris mai jos.
8.1 Specificatori de clasa de memorie
Specificatorii de clasa de memorie sint:
sc_specifier:
auto
static
extern
register
Declaratiile care utilizeaza specificatorii auto, static si register servesc de asemenea ca definitii prin faptul ca ele implica rezervarea unei cantitati de memorie de o marime potrivita. Daca o declaratie extern nu este o definitie (&4.2) trebuie sa existe undeva o definitie pentru identificatorii dati. O declaratie register este cel mai bine sa fie gindita ca o declaratie auto impreuna cu sugestia ca, compilatorul stie faptul ca variabilele respective vor fi utilizate din greu. Aceasta informatie poate fi ignorata.
Operatorul de adresa & nu poate fi aplicat la ele. Specificatorii auto sau register pot fi utilizati numai pentru nume de obiecte declarate intr-un bloc si pentru argumente formale. Se poate sa nu existe functii statice intr-un bloc si nici argumente statice formale. Cel putin un sc_specifier poate fi dat intr-o declaratie. Daca sc_specifier este absent dintr-o declaratie, clasa de memorie se considera automatic intr-o functie si static in afara.
Exceptie: functiile nu sint niciodata automatice. Specificatorii static si extern pot fi folositi numai pentru nume de obiecte sau functii.
Anumiti specificatori pot fi folositi numai in declaratii de functii:
fct_specifiers:
overload
inline
virtual
Specificatorul overload permite ca un nume sa fie utilizat pentru a nota diferite functii: vezi &8.9. Specificatorul inline este numai o informatie pentru compi- lator; el nu afecteaza intelesul programului si poate fi ignorat. Se indica faptul ca substitutia inline a corpului functiei este de preferat implementarii obisnuite al apelului de functie. O functie (vezi &8.5.2 si &8.5.10) definita intr-o declaratie a unei clase este implicit o functie inline. Specificatorul virtual poate fi utilizat numai in decla- ratiile membrilor unei clase; vezi &8.5.4.
Specificatorul friend poate fi folosit sa se suprapuna peste numele care ascund regulile pentru membri unei clase si poate fi utilizat numai intr-o declaratie de clasa; vezi &8.5.10.
Specificatorul typedef se foloseste pentru a introduce un nume pentru un tip; vezi &8.8.
8.2 Specificatori de tip
Specificatorii de tip sint:
type_specifier:
simple_type_name
class_specifier
enum_specifier
elaborated_type_specifier
const
Cuvintul const poate fi adaugat la orice specificator de tip legal. Altfel, intr-o declaratie se poate da cel mult un specifi- cator de tip. Un obiect de tip const nu este o lvaloare. Daca specificatorul de tip lipseste dintr-o declaratie, el se ia int.
simple_type_name:
typedef_name
char
short
int
long
unsigned
float
double
void
Cuvintele long, short si unsigned pot fi gindite ca adjective. Ele pot fi aplicate la int; unsigned de asemenea se poate aplica la char, short si long.
Specificatorii de clasa si enumerare se discuta in &8.5 si respectiv &8.10.
elaborate_type_specifier:
key type_def_name
key identifier
key:
class
struct
union
enum
Un specificator de tip elaborat poate fi utilizat pentru a face referire la numele unei clase sau la numele unei enumerari unde numele poate fi ascuns printr-un nume local. De exemplu:
class x ;
void f(int)
Daca numele de clasa sau de enumerare nu a fost in prealabil declarat, specificatorul de tip elaborat actioneaza ca o decla- ratie de nume; vezi &8.8.
8.3 Declaratori
declarator_list:
init_declarator
init_declarator, declarator_list
init_declarator:
declarator initializer_opt
Initializatorii se discuta in &8.6. Specificatorii din declaratie indica tipul si clasa de memorie al obiectelor la care se refera declaratorii. Declaratorii au sintaxa:
declarator:
dname
(declarator)
*const_opt declarator
&const_opt declarator
declarator(argument_declaration_list) declarator[constant_expression_opt]
dname:
simple_dname
typedef_name :: simple_dname
simple_dname:
identifier
typedef_name
~typedef_name
operator_function_name
conversion_function_name
Gruparea este aceeasi ca in expresii.
8.4 Intelesul (sensul) declaratorilor
Fiecare declarator se considera a fi o asertiune care, cind apare intr-o expresie o constructie asemanatoare cu declaratorul, produce un obiect de tipul si clasa de memorie indicata. Fiecare declarator contine exact un dname; el specifica identificatorul care este declarat. Exceptind declaratiile unor functii speciale (vezi &8.5.2), un dname va fi un identificator simplu. Daca apare ca un declarator un identificator 'neimpodobit', atunci el are tipul indicat de specificatorul care se afla in capul declaratiei. Un declarator in paranteze este identic cu un declarator 'neimpodobit', dar legaturile declaratorilor complecsi pot fi alterate prin paranteze; vezi exemplele de mai jos.
Sa ne imaginam o declaratie:
T D1
unde T este un specificator de tip (int, etc.) si D1 este un declarator. Sa presupunem ca aceasta declaratie face ca identifi- catorul sa aiba tipul ' T', unde '' este vid daca D1 este chiar un identificator (cum ar fi tipul lui x in 'int x' exact int).
Daca D1 are forma:
*D
tipul identificatorului pe care il contine este ' pointer la T'. Daca D1 are forma:
*const D
tipul identificatorului pe care il contine este ' pointer constant la T' adica, acelasi tip ca si *D, dar identificatorul continut nu este o lvaloare. Daca D1 are forma:
&D
sau
&const D
tipul identificatorului pe care il contine este ' referinta la T'. Intrucit o referinta nu poate fi prin definitie o lvaloare, utilizarea lui lvalue este redondanta. Nu este posibil sa avem o referinta spre void (void&). Daca D1 are forma:
D(argument_declaration_list)
atunci identificatorul pe care il contine are tipul ' functie care are argumente de tip argument_declaration_list si returneaza T'.
argument_declaration_list:
arg_declaration_list_opt _opt argument_declaration_list:
arg_declaration_list, argument_declaration
argument_declaration
argument_declaration:
decl_specifiers declarator
decl_specifiers declarator = expression
decl_specifiers abstract_declarator
decl_specifiers abstract_declarator = expression
Daca argument_declaration_list se termina cu trei puncte, atunci numarul argumentelor se stie numai ca este egal sau mai mare decit numarul de argumente specificat; daca este vid, fun- ctia nu are argumente.Toate declaratiile pentru o functie trebuie sa corespunda exact atit in privinta tipului valorii returnate, cit si in numarul si tipul argumentelor. Argumentul argument_declaration_list se utilizeaza pentru a verifica si converti argumentele actuale in apeluri si pentru a verifica asignarile pointerilor spre functii. Daca este specificata o expresie intr-o declaratie de argument, atunci aceasta expresie se utilizeaza ca argument implicit. Argumentele implicite vor fi utilizate in apeluri acolo unde ultimele argumente sint omise. Un argument implicit nu poate fi redefinit printr-o declaratie ulterioara. Cu toate acestea, o declaratie poate adauga argumente implicite care nu s-au dat in declaratiile precedente.
Un identificator poate fi furnizat optional ca un argument nume; daca este prezent intr-o declaratie de functie, el nu poate fi utilizat intrucit el iese imediat afara din domeniul sau; daca este prezent intr-o definitie de functie (&10) el numeste un argument formal. Daca D1 are forma:
D[constant_expression]
sau
D[]
atunci identificatorul pe care il contine are tipul ' tablou de T'. In primul caz expresia constanta este o expresie a carei valoare se determina la compilare si a carei tip este int (expresiile constante se definesc in &12). Cind diferite specificari de tablouri sint adiacente, se creaza un tablou multidimensional; expresiile constante care specifica limitele tablourilor se pot omite numai pentru primul membru al secventei. Aceasta omisiune este utila cind tabloul este extern si definitia reala, care aloca memorie, se da in alta parte. Prima expresie constanta poate fi omisa de asemenea cind declaratorul este urmat de initializare. In acest caz dimensiunea se calculeaza din numarul elementelor initiale furnizate.
Un tablou poate fi construit din unul din tipurile de baza, dintr-un pointer, dintr-o structura sau reuniune sau dintr-un alt tablou (pentru a genera un tablou multi_dimensional).
Nu toate posibilitatile admise prin sintaxa de mai sus sint permise. Restrictiile sint: functiile nu pot returna tablouri sau functii, desi ele pot returna pointeri spre astfel de lucruri; nu exista tablouri de functii, desi pot fi tablouri de pointeri spre functii.
8.4.1 Exemple
Declaratia:
int i, *pi, f(), *fpi(), (*pif)();
declara un intreg i, un pointer pi spre un intreg, o functie f care returneaza un intreg, o functie fpi care returneaza un pointer spre un intreg si un pointer pif spre o functie care returneaza un intreg. Este util mai ales sa se compare ultimii doi. Sensul lui *fpi() este *(fpi()) si aceeasi constructie intr-o expresie cere apelul functiei fpi si apoi utilizind indirectarea prin pointer rezulta producerea unui intreg. La declaratorul (*pif)(), parantezele sint necesare pentru a indica faptul ca indirectarea printr-un pointer la o functie produce o functie, care apoi este apelata. Functiile f si fpi se declara fara argumente, iar pif pointeaza spre o functie care nu are argumente.
Declaratiile:
const a=10, *pc=&a, *const cpc=pc; int b, *const cp=&b;
declara: a - o constanta intreaga; pc - un pointer spre o constanta intreaga;
cpc - un pointer constant spre o constanta intreaga; b - un intreg; cp - pointer constant spre un intreg. Valoarea lui pc poate fi schimbata si la fel si obiectul spre care pointeaza cp. Exemple de operatii ilegale sint:
a=1;
a++;
*pc=2;
cp=&a;
cpc++;
Exemple de operatii legale:
b=a;
*cp=a;
pc++;
pc=cpc;
Declaratia:
fseek(FILE*, long, int);
declara o functie care poate fi apelata cu zero, unu sau doi parametri de tip int. Ea poate fi apelata in oricare din modurile:
point(1, 2);
point(1);
point();
Declaratia:
printf(char* );
declara o functie care poate fi apelata cu un numar variabil de argumente si tipuri. De exemplu:
printf('hello world');
printf('a=%d b=%d', a, b);
Cu toate acestea, trebuie ca totdeauna char* sa fie primul sau parametru. Declaratia:
float fa[17], *afp[17];
declara un tablou de numere flotante si un tablou de pointeri spre numere flotante.
In final:
static int x3d[3][5][7];
declara un tablou de intregi tridimensional de ordinul 3x5x7. x3d este un tablou de 3 elemente; fiecare element este un tablou de 5 elemente; fiecare din acestea fiind la rindul lui un tablou de sapte intregi. Oricare din expresiile x3d, x3d[i], x3d[i][j], x3d[i][j][k] pot apare intr-o expresie.
8.4.2 Tablouri, Pointeri si Indici
Ori de cite ori intr-o expresie apare un identificator de tip tablou, el este convertit intr-un pointer spre primul element al tabloului. Din cauza acestei conversii, tablourile nu sint lvalori. Exceptind cazul in care operatorul de indexare [] a fost declarat pentru o clasa (&7.16.3), el se interpreteaza in asa fel incit E1[E2] este identic cu *((E1)+(E2)). Din cauza regulilor de conversie care se aplica la + daca E1 este un tablou si E2 un intreg, E1[E2] se refera la al E2-lea membru al lui E1. De aceea, in ciuda aparentei asimetrice, indexarea este o operatie comutativa. O regula consistenta se aplica in cazul tablourilor multidi- mensionale. Daca E este un tablou ndimensional de ordinul ixjxxk, atunci E care apare intr-o expresie este convertit spre un pointer spre un tablou (n-1)dimensional de ordinul jxxk. Daca operatorul * se aplica explicit sau implicit ca rezultat al indexarii, rezultatul este tabloul (n-1)dimensional, care este convertit imediat intr-un pointer.
De exemplu, consideram:
int x[3][5];
Aici x este un tablou de 3x5 intregi. Cind x apare intr-o ex- presie, el se converteste spre un pointer spre (primul din cele trei) elementul care este un tablou de ordinul 5. In expresia x[i], care este echivalenta cu *(x+i), x este convertit intii spre un pointer asa cum s-a descris mai sus; apoi x+i este convertit spre tipul lui x, care implica multiplicarea lui i prin lungimea obiectului spre care pointeaza pointerul, si anume obiecte de 5 intregi. Rezultatele se aduna si se aplica indirectarea pentru a produce un tablou de cinci intregi care la rindul lui este convertit spre un pointer spre primul dintre intregi. Daca exista un alt indice se aplica din nou aceeasi regula; de data aceasta rezulta un intreg. Din toate acestea rezulta ca tablourile din C++ sint pastrate pe linie (ultimul indice variaza mai repede) si ca primul indice din declaratie ajuta sa se determine cantitatea de memorie consumata de un tablou dar el nu joaca alt rol in calculele de indici.
8.5 Declaratii de clasa
O clasa este un tip. Numele ei devine un typedef_name (vezi &8.8) care poate fi utilizat chiar in specificarea clasei. Obiectele unei clase constau dintr-o secventa de membri.
class_specifier:
class_head
class_head
class_head:
aqqr identifier_opt
aqqr identifier: public_opt typedef_name
aqqr:
class
struct
union
Obiectele de clasa pot fi asignate, pasate ca argumente la functii si returnate de functii (exceptind obiectele unor clase derivate; vezi &8.5.3).
Alti operatori plauzibili, cum ar fi egalitatea, pot fi definiti de utilizator; vezi &8.5.11.
O structura este o clasa cu toti membri publici; vezi &8.5.9. O reuniune este o structura care contine numai un membru la un moment dat; vezi &8.5.13. O lista de membri (member_list) poate declara ca membri date, functii, clase, enumerari, cimpuri (&8.5.14) si prieteni (&8.5.10). O lista de membri poate de asemenea contine declaratii pentru a specifica vizibilitatea numelor membrilor; vezi &8.5.9.
member_list:
member_declaration member_list_opt member_declaration:
decl_specifiers_opt member_declarator;;
function_definition; _opt
member_declarator:
declarator
identifier_opt: constant_expresion
Membri care sint obiecte de clasa trebuie sa fie obiecte ale claselor declarate in prealabil. In particular, o clasa cl poate sa nu contina un obiect de clasa cl, dar ea poate contine un pointer spre un obiect de clasa cl.
Un exemplu simplu de declaratie a unei structuri este:
struct tnod;
care contine un tablou de 20 de caractere, un intreg si doi pointeri spre structuri similare. Odata ce aceasta declaratie a fost data, declaratia:
tnode s, *sp;
declara pe s ca fiind un tnode si sp un pointer spre un tnode. Cu aceste declaratii:
sp->count
se refera la cimpul count al structurii spre care pointeaza sp;
s.left
se refera la pointerul spre subarborele sting al lui s; iar
s.right->tword[0]
se refera la primul caracter al membrului tnod al subarborelui drept al lui s.
8.5.1 Membri statici
Un membru care este data a unei clase poate fi static; functiile care sint membri nu pot fi. Membri pot sa nu fie auto, register sau extern. Exista numai o singura copie a unui membru static comuna pentru toate obiectele unei clase dintr-un program. Un membru static mem al unei clase cl poate fi referit prin cl::mem, adica fara a se face referire la un obiect. El exista chiar daca nu s-a creat nici un obiect al clasei cl. Nu se poate specifica nici un initializator pentru un membru static si nu poate fi o clasa cu un constructor.
8.5.2 Functii membru
O functie declarata ca membru (fara specificatorul friend (&8.5.10)) se numeste functie membru si se apeleaza utilizind sintaxa membrului unei clase (&7.1). De exemplu:
struct tnod;
tnode n1, n2;
n1.set('asdf', &n2, 0);
n2.set('ghjk',0, 0);
Definitia unei functii membru se considera ca este in dome- niul clasei sale. Aceasta inseamna ca poate utiliza direct numele clasei sale. Daca definitia unei functii membru este lexic in afara declaratiei de clasa, numele functiei membru trebuie sa fie calificat prin numele clasei folosind operatorul ::. Definitiile functiilor se discuta in &10. De exemplu:
void tnode::set(char* w, tnode* l, tnode* r)
Notatia tnode::set() specifica faptul ca set() este un mem- bru al clasei tnode si este in domeniul de vizibilitate al clasei tnode. Numele membrilor tword, count, left si right se refera la obiectul pentru care a fost apelata functia. Astfel, in apelul n1.set('abc', 0, 0) tword se refera la n1.tword, iar in apelul n2.set('def', 0, 0) el se refera la n2.tword. Functiile strlen, error si strcpy se presupun ca sint declarate in alta parte; vezi &10. Intr-o functie membru, cuvintul cheie this este un pointer spre obiectul pentru care a fost apelata functia.
O functie membru poate fi definita (&10) in declaratia de clasa, caz in care ea este inline (&8.1). Astfel:
struct x
int b;
};
este echivalenta cu:
struct x;
inline x::f()
Este legal sa se aplice adresa operatorului la o functie membru. Cu toate acestea, tipul pointerului rezultat spre functie este nedefinit, asa ca orice utilizare a ei este dependenta de implementare.
8.5.3 Clase derivate
In constructia:
aqqr identifier : public_opt typedef_name
typedef_name trebuie sa noteze o clasa in prealabil declarata, care se numeste clasa de baza pentru clasa ce se declara. Pentru sensul de public vezi &8.5.9. Membri clasei de baza pot fi refe- riti ca si cum ei ar fi membri clasei derivate, exceptind cazul in care numele membrilor bazei au fost redefiniti in clasa derivata; in acest caz operatorul :: (&7.1) poate fi utilizat pentru a ne referi la membri ascunsi. O clasa derivata poate fi ea insasi folosita ca o clasa de baza. Nu este posibila derivarea dintr-o reuniune (&8.5.13). Un pointer spre o clasa derivata poate fi convertit implicit intr-un pointer spre o clasa de baza publica (&6.7).
Asignarea nu este definita implicit (vezi &7.14 si &8.5) pentru obiectele unei clase derivate dintr-o clasa pentru care operatorul = a fost definit (&8.5.11). De exemplu:
class base;
class derived : public base;
derived d;
d.a=1;
d.base::b=2;
d.b=3;
d.c=4;
base* bp=&d;
asigneaza cei patru membri ai lui d iar bp devine un pointer spre d.
8.5.4 Functii virtuale
Daca clasa de baza base contine o functie virtuala (&8.1) vf si o clasa derivata contine de asemenea o functie vf, atunci ambele functii trebuie sa aiba acelasi tip, iar un apel al lui vf pentru un obiect al clasei derivate implica derived::vf. De exemplu:
struct base;
class derived : public base;
derived d;
base* bp=&d;
bp->vf();
bp->f();
Apelurile invoca derived::vf si base::f,respectiv pentru obiectul clasei derivate numit d. Adica, interpretarea apelului unei functii virtuale depinde de tipul obiectului pentru care ea este apelata, in timp ce interpretarea apelului unei functii membru nevirtuale depinde numai de tipul pointerului care desemneaza acel obiect.
O functie virtuala nu poate fi un prieten (&8.5.10). O functie f dintr-o clasa derivata dintr-o clasa care are o functie virtuala f este ea insasi considerata virtuala. O functie virtu- ala care a fost definita intr-o clasa de baza nu este necesar sa fie definita intr-o clasa derivata. In acest caz, functia defi- nita pentru clasa de baza este utilizata in toate apelurile.
8.5.5 Constructori
O functie membru cu acelasi nume ca si clasa ei se numeste constructor. El se utilizeaza pentru a construi valori de tipul clasei lui. Daca o clasa are un constructor, fiecare obiect al acelei clase trebuie sa fie initializat inainte de a face orice utilizare a obiectului (vezi &8.6). Un constructor nu poate fi declaratt virtual sau prieten.
Daca o clasa are o clasa de baza sau obiecte membru cu constructori, constructorii lor se apeleaza inaintea constructo- rului clasei derivate. Deci, mai intii se apeleaza constructorul pentru clasa de baza. Vezi &10 pentru un exemplu de felul in care pot fi specificate argumentele pentru constructori si &8.5.8 pentru a vedea cum se pot utiliza constructorii pentru gestionarea memoriei libere.
Un obiect al unei clase cu un constructor nu poate fi membru al unei reuniuni.
Nu se poate specifica un tip de valoare returnata de un constructor si nici nu se poate folosi o instructiune return in corpul unui constructor.Un constructor poate fi utilizat explicit ca sa creeze obiecte noi de tipul lui, utilizind sintaxa:
typedef_name(argument_list_opt);
De exemplu:
complex zz = complex(1, 2.3);
cprint(complex(7.8, 1.2));
Obiectele create in acest fel sint fara nume (exceptind cazul in care constructorul a fost utilizat ca initializator; ca in cazul lui zz de mai sus), cu viata limitata in domeniul in care au fost ele create.
8.5.6 Conversii
Un constructor avind un argument specifica o conversie de la tipul argumentului lui, la tipul clasei. Astfel de conversii se utilizeaza implicit in plus fata de conversiile standard (&6.6- &7). O asignare la un obiect apartinind clasei X este legala daca tipul T al valorii asignate este X sau daca a fost declarata o conversie de tip de la T la X. Constructorii se utilizeaza similar pentru conversia initializatorilor (&8.6), al argumentelor functiei (&7.1) si al valorilor returnate de functie (&9.10). De exemplu:
class X;
f(X arg)
Cind un constructor pentru clasa X nu accepta tipul asignat, nu se incearca sa se gaseasca alti constructori care sa converteasca valoarea asignata intr-un tip acceptabil de un constructor pentru clasa respectiva. De exemplu:
class X;
class Y;
Y a = 1; //este ilegal; nu se incearca Y(X(1))
O functie membru a clasei X cu un nume de forma:
conversion_function_name : operator type
specifica o conversie de la tipul X la tipul type. Tipul type poate sa nu contina declaratorii [] 'vector de' sau () 'functie ce returneaza'. Se va utiliza implicit ca si constructorii de mai sus (numai daca este unic &8.9) sau poate fi apelat explicit utilizind notatia cast. De exemplu:
class X;
X a;
int i=int(a);
i=(int)a;
i=a;
In toate cele trei cazuri valoarea asignata va fi convertita spre X::operator int(). Conversiile definite de utilizator pot fi utilizate numai in asignari si initializari. De exemplu:
X a, b;
//
int i = (a) ? 1+a : 0;
int j = (a && b) ? a+b : i;
8.5.7 Destructori
O functie membru a clasei cl numita ~cl se numeste destructor; el nu are argumente si nici nu se poate specifica o valoare de revenire pentru el; se utilizeaza pentru a distruge valorile de tip cl imediat inainte de a distruge obiectul care le contine. Un destructor nu poate fi apelat explicit. Destructorul pentru o clasa de baza se executa dupa destructorul pentru clasa lui derivata. Destructorii pentru obiectele membru se executa dupa destructorul pentru obiectul pentru care ele sint membre. Vezi &8.5.8 pentru o explicatie despre felul in care destructorii pot fi utilizati pentru a gestiona memoria libera. Un obiect al unei clase cu un destructor nu poate fi un membru al unei reuniuni.
8.5.8 Memorie libera
Cind se creaza un obiect de clasa folosind operatorul new constructorul va utiliza (implicit) operatorul new pentru a obtine memoria ceruta (&7.1). Asignind memorie la pointerul this inainte de orice folosire a unei functii membru, constructorul poate sa implementeze obiectul. Prin atribuirea lui 0 la this, un destructor poate elimina operatia de alocare standard pentru obiectele clasei sale. De exemplu:
class cl
~cl()
};
La intrarea intr-un constructor, this este diferit de zero daca alocarea a avut deja loc (asa este cazul pentru auto, static si obiectele membre) si zero altfel.
Apeluri la constructori pentru o clasa de baza si pentru obiectele membru se vor face dupa o asignare la this. Daca constructorul unei clase de baza asigneaza la this, noua valoare va fi folosita de asemenea de catre constructorul claselor derivate (daca exista vreuna).
Numarul elementelor trebuie sa fie specificat cind se sterge un vector de obiecte al unei clase cu un destructor. De exemplu:
class x;
X.p = new X[size];
delete[size].p;
8.5.9 Vizibilitatea numelor membri
Membri unei clase declarate cu cuvintul cheie class sint privati, adica, numele lor pot fi utilizate numai de functiile membru (&8.5.2) si functiile prietene (&8.5.10) exceptind cazul cind ele apar dupa eticheta 'public'; in acest caz ele sint publice. Un membru public poate fi utilizat in orice functie. O structura struct este o clasa cu toti membri publici (&8.5.12). Daca o clasa derivata se declara struct sau daca cuvintul cheie public precede numele clasei de baza in declaratia clasei derivate, atunci membri publici ai clasei de baza sint publici pentru clasa derivata; altfel ei sint privati. Un membru public mem pentru o clasa de baza privata base poate fi declarat ca sa fie public pentru o clasa derivata printr-o declaratie de forma:
typedef_name::identifier;
unde typedef_name noteaza clasa de baza si identifier este numele membrului clasei de baza. O astfel de declaratie trebuie sa apara in partea publica a clasei derivate.
Consideram:
class base;
class derived : base;
int ef(derived&);
Functia externa ef poate folosi numai numele c, e si df. Functia df fiind un membru al lui derived poate folosi b, c, bf, d, e si df, dar nu si pe a. Fiind un membru al lui base, functia bf poate utiliza membri a, b, c si bf.
8.5.10 Prieteni
Un prieten al unei clase este o functie nemembru care poate utiliza numele membrilor privati dintr-o clasa. Un prieten nu este in domeniul unei clase si nu se apeleaza utilizind sintaxa de selectie de membru (exceptind cazul in care el este un membru al unei alte clase). Exemplul urmator ilustreaza diferenta dintre membri si prieteni:
class private;
void friend_set(private* p, int i) void private::member_set(int i); private obj; friend_set(&obj, 10); obj.member_set(10);
Cind o declaratie friend se refera la un nume sau la un operator supraincarcat numai functia specificata prin tipurile argument devine un prieten. Un membru al unei clase cl1 poate fi prietenul clasei cl2. De exemplu:
class cl2;
Toate functiile unei clase cl1 pot fi facute prietene ale clasei cl2 printr-o singura declaratie:
class cl2;
O functie friend definita (&10) intr-o declaratie de clasa este inline.
8.5.11 Functii operator
Cei mai multi operatori pot fi supraincarcati astfel incit sa aiba ca operanzi obiecte de clasa.
operator_function_name:
operator operator
operator: unul din
newdelete
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>=<<=== !=
<= >= && || ++ -- () []
Ultimii doi operatori sint pentru apelul functiilor si pentru indexare. O functie operator (exceptind operatorii new si delete; vezi &7.2) trebuie sau sa fie o functie membru sau sa aiba cel putin un argument de clasa. Vezi de asemenea &7.16.
8.5.12 Structuri
O structura este o clasa cu toti membri publici. Adica:
struct s;
este echivalent cu:
class s;
O structura poate avea functii membru (inclusiv constructori si destructori). Baza unei structuri derivate este publica.Adica:
struct s : b ;
este echivalent cu:
class s : public b;
8.5.13 Reuniuni
O reuniune poate fi gindita ca o structura a carei obiecte membru incep la deplasamentul 0 si a carei dimensiune este suficienta pentru a contine oricare din obiectele membru ale ei. Cel mult unul din obiectele membru pot fi pastrate intr-o reuniune in orice moment. O reuniune poate avea functii membru (inclusiv constructori si destructori). Nu este posibil sa se deriveze o clasa dintr-o reuniune. Un obiect al unei clase cu un constructor sau un destructor poate fi un membru al unei reuniuni.
O reuniune de forma:
union;
este numita reuniune anonima; ea defineste un obiect nedenumit. Numele mebrilor unei reuniuni anonime trebuie sa fie distincte de alte nume din domeniul unde este declarata reuniunea; ei pot fi utilizati direct in acel domeniu fara a utiliza sintaxa de acces uzuala la un membru (&8.5). De exemplu:
union;
a=1;
//
p='asdf';
Aici a si p se utilizeaza ca variabile obisnuite (nemembru), dar intrucit ele sint membri ai unei reuniuni ele trebuie sa aiba aceeasi adresa.
8.5.14 Cimpuri de biti
Un membru_declarator de forma:
identifier_opt : constant_expression
specifica un cimp; lungimea lui este separata de numele cimpului prin doua puncte. Cimpurile se impacheteaza in intregi masina; ele nu se pot pastra pe mai multe cuvinte. Un cimp care nu poate fi pastrat in spatiul ramas al unui intreg se pune in cuvintul urmator. Nici un cimp nu poate fi mai mare decit un cuvint. Cimpurile sint asignate de la dreapta spre stinga pe unele masini si de la stinga spre dreapta pe altele; vezi &2.6.
Un cimp nedenumit este util pentru cadraje, pentru a se conforma cu conditiile impuse din afara. Ca un caz special, un cimp nedenumit cu dimensiunea 0 specifica alinierea cimpului urmator la o limita de cuvint. Implementarile nu sint supuse unor restrictii, altele decit sa accepte cimpuri intregi. Totusi, chiar cimpurile int pot fi considerate ca unsigned. Din aceste motive, cimpurile trebuie sa fie declarate ca unsigned. Operatorul adresa & nu poate fi aplicat la ele, asa ca nu exista pointeri spre cimpuri. Cimpurile nu pot fi membri ai unei reuniuni.
8.5.15 Clase imbricate
O clasa poate fi declarata intr-o alta clasa. Aceasta, totusi este numai o notatie convenabila intrucit clasa interioara apartine domeniului care o include. De exemplu:
int x;
class enclose;
int g(inner*);
inner a;
void inner::f(int i);//asignare la ::x
int enclose::g(inner* p) // eroare
8.6 Initializare
Un declarator poate specifica o valoare initiala pentru identificatorul care se declara:
initializer:
= expression
(expression_list)
initializer_list:
expression
initializer_list, initializer_list
Toate expresiile dintr-un initializator pentru o variabila statica trebuie sa fie expresii constante (care este descrisa in &12) sau expresii care se reduc la adresa unei variabile in prealabil declarate, posibil modificate cu o expresie constanta. Variabilele automatice sau registru pot fi initializate prin expresii arbitrare care implica constante, variabile si functii in prealabil declarate.
Variabilele statice si externe care nu sint initializate se garanteaza ca au valoarea initiala diferita de zero; variabilele automatice si registru care nu sint initializate au o valoare initiala imprevizibila.
Cind un initializator se aplica la un scalar (un pointer sau un obiect al tipului aritmetic), el consta dintr-o singura expresie, poate in acolade. Valoarea initiala a obiectului este egala cu a expresiei; se fac aceleasi conversii ca si la asignare.
Sa observam ca intrucit () nu sint un initializator, X a(); nu este declaratia unui obiect al clasei X, ci declaratia unei functii care nu are argumente si returneaza o valoare de tip X.
8.6.1 Liste initializatoare
Cind variabila declarata este un agregat (o clasa sau un tablou) initializatorul poate consta dintr-o lista de initializa- tori separati prin virgula si inclusa in acolade pentru membri agregatului, scrisi in ordinea crescatoare a indicilor sau a ordinii membrilor. Daca tabloul contine subagregate, aceasta regula se aplica recursiv la membri agregatului. Daca sint mai putini initializatori in lista decit membri ai agregatului atunci agregatul se completeaza cu zerouri.
Acoladele pot fi utilizate dupa cum urmeaza. Daca initializatorul incepe cu o acolada stinga, atunci lista de initializa- tori care urmeaza (separati prin virgula) initializeaza membri agregatului; este eroare daca sint mai multi initializatori decit membri. Daca, totusi, initializatorul nu incepe cu o acolada stinga, atunci se iau atitea elemente din lista cite sint necesare pentru a initializa membri agregatului; membri ramasi sint lasati sa initializeze membrul urmator al agregatului din care face parte agregatul curent.
De exemplu:
int x[] = ;
declara si initializeaza pe x ca si tablou de o dimensiune, care are trei membri, intrucit nu s-a specificat nici o dimensiune si sint trei initializatori.
float y[4][3] = {, , ,};
este o initializare complet inclusa in acolade: 1, 3 si 5 initializeaza prima linie a tabloului y[0] si anume y[0][0], y[0][1] si y[0][2]. La fel urmatoarele doua linii initializeaza y[1] si y[2]. Initializatorul se termina mai devreme si de aceea y[3] se initializeaza cu zero. Exact acelasi efect s-ar fi obtinut prin:
float y[4][3] = ;
Initializatorul pentru y incepe cu o acolada stinga, dar cel pentru y[0] nu mai incepe si de aceea se initializeaza trei ele- mente din lista. La fel urmatoarele trei elemente se iau succesiv pentru y[1] si y[2]. De asemenea:
float y[4][3] = , , , };
initializeaza prima coloana a lui y (privit ca un tablou bidimensional) si lasa restul 0.
8.6.2 Obiecte de clasa
Un obiect cu membri privati nu poate fi initializat folosind o lista initializatoare; nici un obiect al unei reuniuni. Un obiect al unei clase cu un constructor trebuie sa fie initializat. Daca o clasa are un constructor care nu are argumente, acel constructor se foloseste pentru obiecte care nu sint explicit initializate. O lista de argumente pentru constructor se poate adauga la numele dintr-o declaratie sau la tipul dintr-o expresie din new. Initializarile urmatoare produc toate aceeasi valoare (&8.4):
struct complex
};
complex zz1(1, 0);
complex zz2(1);
complex* zp1 = new complex(1, 0); complex* zp2 = new complex(1);
Obiectele de clasa pot fi de asemenea initializate prin utilizarea
explicita a operatorului =. De exemplu:
complex zz3 = complex(1, 0);
complex zz4 = complex(1);
complex zz5 = 1;
complex zz6 = zz3;
Daca exista un constructor care are ca referinta un obiect al clasei lui proprii, atunci el va fi apelat cind se initiali- zeaza un obiect al unei clase cu un alt obiect al acelei clase, dar nu cind un obiect este initializat cu un constructor. Un obiect poate fi un membru al unui agregat numai: (1) daca obiectele clasei nu au un constructor sau (2) unul din constructorii ei nu are argumente sau (3) daca agregatul este o clasa cu un constructor care specifica o lista de initializare membru (vezi &10). In cazul (2) constructorul respectiv se apeleaza cind se creaza agregatul. Daca agregatul este o clasa (dar nu daca este un vector) argumentele implicite se pot folosii pentru apelul constructorului. Daca un membru al unui agregat are un destructor atunci acel destructor se apeleaza cind agregatul este distrus. Constructorii pentru obiecte statice nelocale se apeleaza in ordinea in care ei apar in fisier; destructorii se apeleaza in ordine inversa. Nu este definit apelul unui constructor si al unui destructor pentru un obiect static local daca nu este ape- lata functia in care este definit obiectul. Daca se apeleaza con- structorul pentru un obiect static local, atunci el este apelat dupa constructorii pentru obiectele globale care il preced lexical. Daca este apelat destructorul pentru un obiect static local, atunci el este apelat inaintea destructorilor pentru obiecte globale care il preced lexical.
8.6.3 Referinte
Cind o variabila se declara ca este T&, adica 'referinta la tipul T', ea trebuie sa fie initializata printr-un obiect de tip T sau printr-un obiect care poate fi convertit spre tipul T. Referinta devine un alt nume pentru obiect. De exemplu:
int i;
int &r=i;
r=1;// valoarea lui i devine 1
int* p=&r;// p pointeaza spre i
Valoarea unei referinte nu poate fi schimbata dupa initializare. Sa observam ca initializarea este tratata diferit fata de asignare. Daca initializatorul pentru o referinta la tipul T nu este o lvaloare se creaza un obiect de tip T si acesta este initializat cu initializatorul. Referinta devine un nume pentru acel obiect. Domeniul de existenta al unui obiect creat in acest fel este domeniul in care el este creat. De exemplu:
double& rr=1;
este legal si rr va pointa spre double care contine valoarea 1.0. Sa observam ca o referinta la o clasa B poate fi initializata printr-un obiect al clasei D cu conditia ca B sa fie o clasa de baza publica a lui D (in acest caz D este un B). Referintele sint utile mai ales ca parametri formali. De exemplu:
struct B; struct D : B; int f(B&);
D a; f(a);
8.6.4 Tablouri de caractere
Un tablou de tip char poate fi initializat printr-un sir: caractere succesive din sir initializeaza membri tabloului. De exemplu:
char msg[] = 'Syntax error on line %sn';
arata un tablou de caractere ai carui membri se initializeaza cu un sir. Sa observam ca sizeof[msg] == 25.
8.7 Nume de tip
Uneori (pentru a specifica explicit conversiile de tip si ca un argument al lui sizeof sau new) se cere sa se furnizeze numele unui tip de data. Aceasta se realizeaza utilizind un type_name, care in esenta este o declaratie pentru un obiect de acel tip care omite numele obiectului.
type_name:
type_specifier abstract_declarator abstract_declarator:
empty
*abstract_declarator
abstract_declarator(argument_declaration_list)
abstract_declarator[constant_expression_opt]
(abstract_declarator)
Este posibil sa se identifice unic locatia din abstract_declarator unde ar apare identificatorul daca constructor ar fi un declarator intr-o declaratie. Tipul denumit este apoi acelasi cu tipul identificatorului ipotetic. De exemplu:
int
int*
int* [3]
int (*)[3]
int* ()
int (*)()
numesc respectiv tipurile 'intreg', 'pointer spre intreg', 'tablou de trei pointeri spre intreg', 'pointer spre un tablou de trei intregi', 'functie care returneaza un pointer spre intreg' si 'pointer spre o functie care returneaza un intreg'.
8.8 Typedef
Declaratiile care contin specificatorul (decl_specifier) typedef definesc identificatori care pot fi utilizati mai tirziu ca si cum ei ar fi fost cuvinte cheie de tipuri care numesc tipuri fundamentale sau derivate.
typedef_name: identifier
In domeniul unei declaratii care implica typedef, fiecare identificator care apare ca parte a oricarui declarator devine sintactic echivalent cu cuvintul cheie care numeste tipul asociat cu identificatorul in modul descris in &8.4. Specificatorul typedef poate sa nu fie utilizat pentru un membru al clasei. Numele unei clase sau al unei enumerari este de asemenea un nume typedef. De exemplu, dupa:
typedef int MILES, *KLICKSP;
struct complex;
constructiile:
MILES distance;
extern KLICKSP metricp;
complex z, *zp;
sint toate declaratii legale; tipul lui distance este int, cel al lui metricp este 'pointer spre int'. Typedef nu introduce tipuri noi, ci numai sinonime pentru tipurile care ar putea fi specificate in alt mod. Astfel in exemplul de mai sus distance este considerata sa aiba exact ace- lasi tip ca multe alte obiecte int. O declaratie de clasa introduce un tip nou. De exemplu:
struct X;
struct Y;
X a1;
Y a2;
int a3;
declara trei variabile de trei tipuri diferite.
O declaratie de forma:
name_declaration:
aqqr identifier; enum identifier;
specifica faptul ca un identificator este numele unei anumite clase sau o enumerare (posibil nedefinite inca). Astfel de declaratii admit declaratia claselor care se refera una la alta.
class vector; class matrix;
class vector;
8.9 Nume de functii supraincarcate
Cind se specifica declaratii de functii diferite pentru un singur nume, se spune ca numele acela este supraincarcat. Cind se utilizeaza acel nume, selectia se face prin compararea tipurilor argumentelor actuale (efective) cu tipurile argumentelor formale. Gasirea functiei care este apelata se realizeaza in trei pasi separati:
1. Cauta o corespondenta exacta si o utilizeaza daca ea a fost gasita;
2. Cauta o corespondenta utilizind conversiile standard (&6.6-8) si utilizeaza una din ele.
3. Cauta o corespondenta folosind conversii definite de utilizator (&8.5.6).
Daca se gaseste un set unic de conversii, se foloseste acesta. Zero, un caracter (char) sau short se considera o cores- pondenta exacta pentru un argument de tip double. Numai conversiile urmatoare vor fi aplicate pentru un argument la o functie supraincarcata: int spre long, int spre double, conversiile de pointer si referinta (&6.7-&8). Pentru a supraincarca numele unei alte functii decit a unui membru sau functie operator trebuie ca declaratia overload sa preceada orice declaratie a functiei (vezi &8.1). De exemplu:
overload abs;
double abs(double);
int abs(int);
abs(1); //apeleaza abs(int);
abs(1.0); //apeleaza abs(double);
De exemplu :
class X;
class Y;
class Z;
overload int f(X), f(Y);
overload int g(X), g(Z);
f(1);//ilegal: f(X(1)) sau f(Y(1))
g(1);//g(X(1))
g('asdf'); //g(Z('asdf'))
Operatorul adresa & poate fi aplicat numai la un nume supra- incarcat intr-o asignare sau o initializare, unde tipul asteptat determina care functie ia adresa. De exemplu :
int operator=(matrix&, matrix&);
int operator=(vector&, vector&);
int(*pfm)(matrix&, matrix&) = &operator=;
int(*pfv)(vector&, vector&) = &operator=;
int(*pfx)( /**/ ) = &operator=; //eroare
8.10 Declaratii de enumerare
Enumerarile sint tipuri int cu constante denumite.
enum_specifier:
enum identifier_opt enum_list:
enumerator
enum_list, enumerator
enumerator:
identifier
identifier = constant_expression
Identificatorii dintr-o lista de enumerare (enum_list) sint declarati ca si constante si pot apare oriunde se cere o constanta. Daca nu apar enumeratori cu =, atunci valorile constantelor corespunzatoare incep la 0 si se maresc cu 1 in timp ce declaratia se citeste de la stinga spre dreapta. Un enumerator cu = da identificatorului asociat valoarea indicata; identificatorii urmatori continua marirea cu 1 de la valoarea asignata.
Numele enumeratorilor trebuie sa fie distincte pentru variabilele ordinare. Numele enumeratorilor cu constante diferite trebuie de asemenea sa fie distincte.
Valorile enumeratorilor nu este necesar sa fie distincte.
Rolul identificatorului enum_specifier este analog cu cel al numelui de clasa; el numeste o enumerare particulara. De exemplu:
enum color; color col=read; color* cp=&col; if(*cp == blue)
//
face ca, color sa fie un tip ce descrie diferite culori si apoi declara col ca un obiect de acel tip si cp ca un pointer spre un obiect de acel tip. Valorile posibile sint din setul .
8.11 Declaratia asm
O declaratie asm are forma:
asm;
Sensul unei declaratii asm nu este definit. De obicei se foloseste pentru a transfera informatie prin compilator la un asamblor.
9 Instructiuni
Instructiunile se executa in secventa, daca nu se indica altfel.
9.1 Instructiunea expresie
Majoritatea instructiunilor sint instructiuni expresie, care au forma:
expression;
De obicei instructiunile expresie sint atribuiri sau apeluri de functie.
9.2 Instructiunea compusa (blocul)
Diferite instructiuni pot fi utilizate unde se asteapta una singura furnizind o instructiune compusa (de asemenea, aceasta se numeste 'bloc'):
compound_statement:
statement_list:
statement
statement statement_list
Sa observam ca o declaratie este un exemplu de o instructiune (&9.14).
9.3 Instructiune conditionala
Cele doua forme ale instructiunii conditionale sint:
if(expression) statement
if(expression) statement else statement Expresia trebuie sa fie de tip aritmetic sau pointer sau un tip de clasa pentru care este definita o conversie spre tipul pointer sau aritmetic (vezi &8.5.6). Expresia este evaluata si daca nu este zero, se executa prima subinstructiune. Daca se utilizeaza else se executa cea de a doua subinstructiune daca expresia este zero. De obicei ambiguitatea 'else' se rezolva conectind un else cu ultimul if intilnit.
9.4 Instructiunea WHILE
Instructiunea while are forma:
while(expression) statement
Subinstructiunea se executa repetat atita timp cit valoarea expresiei ramine diferita de zero. Testul are loc inaintea fiecarei executii a instructiunii. Expresia se trateaza ca si in instructiunea conditionala (&9.3).
9.5 Instructiunea DO
Instructiunea do are forma:
do statement while(expression);
Subinstructiunea se executa repetat pina cind valoarea devine zero. Testul are loc dupa fiecare executie a instructiunii. Expresia este tratata ca intr-o instructiune conditionala (9.3).
9.6 Instructiunea FOR
Instructiunea for are formatul:
for(statement_1; expression_1 opt; expression_2 opt)
statement_2;
Aceasta instructiune este echivalenta cu:
statement_1
while(expression_1)
cu exceptia faptului cind in statement_2 se executa o instructiune continue, caz in care se executa expression_2 inainte de a se executa expression_1. Prima instructiune specifica initiali- zarea pentru ciclu; prima expresie exprima un test care se face inaintea oricarei iteratii si se iese din ciclu cind expresia devine zero; cea de a doua expresie adesea exprima o incrementare care se face dupa fiecare iteratie.
Oricare din expresii sau chiar si ambele pot fi vide. Lipsa expresiei expression_1 face while echivalent cu while(1). Sa observam ca daca statement_1 este o declaratie, domeniul numelui declarat se extinde pina la sfirsitul blocului care include instructiunea for.
9.7 Instructiunea SWITCH
Instructiunea switch transfera controlul unei instructiuni dintr-un set de instructiuni, in functie de valoarea unei expresii. Are forma:
switch(expression) statement
Tipul expresiei expression trebuie sa fie aritmetic sau pointer. Orice instructiune din statement poate avea o etichetata case sau mai multe, dupa cum urmeaza:
case constant_expression :
unde expresia constanta trebuie sa fie de acelasi tip cu expresia din switch; de obicei se fac conversii aritmetice. Nu se poate sa existe doua constante identice de tip case in acelasi switch. Expresiile constante se definesc in &12.
Este posibil sa existe o eticheta de forma:
default:
Cind se executa instructiunea switch, se evalueaza expresia ei si se compara cu fiecare constanta case. Daca una din constantele case este egala cu valoarea expresiei atunci controlul este transferat instructiunii etichetate cu constanta respectiva case. Daca nu exista nici o constanta case care sa aiba aceeasi valoare cu cea a expresiei si daca exista eticheta default, atunci controlul este transferat instructiunii etichetate cu default. Daca nu exista nici o constanta case care sa aiba aceeasi valoare cu expresia si nu exista eticheta default, atunci nici una din instructiunile din switch nu se executa; case si default nu alte- reaza controlul, care continua nestinjenit de la o eticheta la alta. Pentru a iesi din switch vezi break (&9.8). De obicei instructiunea care este subiectul unui switch este compusa. Pot apare declaratii in capul acestei instructiuni, dar initializarile variabilelor registru si automatice sint inefective.
9.8 Instructiunea BREAK
Instructiunea:
break;
are ca actiune terminarea celei mai interioare instructiuni while, do, for sau switch; controlul este transferat la instructiunea urmatoare.
9.9 Instructiunea CONTINUE
Instructiunea:
continue;
transfera controlul la partea de continuare a ciclului celui mai interior care o contine; adica la sfirsitul ciclului. Mai exact, in fiecare din instructiunile:
while()
for()
dowhile();
O instructiune continue este echivalenta cu goto contin (dupa contin este o instructiune vida, &9.13).
9.10 Instructiunea RETURN
O functie revine la functia care a apelat-o prin intermediul instructiunii return, care are una din formele:
return;
return expresie;
Prima forma poate fi utilizata numai in functii care nu returneaza o valoare, adica o functie care returneaza o valoare de tip void. Cea de a doua forma poate fi utilizata numai in functii care returneaza o valoare; valoarea expresiei este returnata la functia care a facut apelul. Daca este necesar, expresia se converteste ca si intr-o initializare spre tipul functiei in care ea apare. Atingerea sfirsitului unei functii este echivalenta cu o instructiune return fara valoare returnata.
9.11 Instructiunea GOTO
Controlul poate fi transferat neconditionat prin instructiunea:
goto identifier;
unde identifier trebuie sa fie o eticheta (&9.12) localizata in functia curenta. Nu este posibil sa se transfere controlul peste o declaratie cu initializator, exceptind transferarea controlului peste un bloc interior fara a intra in el.
9.12 Instructiunea etichetata
Orice instructiune poate fi precedata de o eticheta de forma:
identifier:
care serveste sa declare pe identifier ca eticheta. Singura utilizare a unei etichete este de a fi utilizata intr-o instructiune goto. Domeniul unei etichete este functia curenta, excluzind orice subbloc in care acelasi identificator este redeclarat (vezi &4.1).
9.13 Instructiunea NULL
Instructiunea null are forma:
;
O instructiune null este utila pentru a introduce o eticheta inainte de } a unei instructiuni compuse sau sa furnizeze un corp nul pentru o instructiune ciclica cum ar fi while.
9.14 Instructiunea declarativa
O instructiune declarativa se utilizeaza pentru a introduce un identificator nou intr-un bloc; ea are forma:
declaration_statement: declaration
Daca un identificator introdus printr-o declaratie a fost in prealabil declarat intr-un bloc exterior, declaratia externa este ascunsa pe domeniul blocului, dupa care el isi reia existenta. Orice initializari ale variabilelor auto si register se fac ori de cite ori se executa instructiunile declarative ale lor. Este posibil sa se intre intr-un bloc dar nu asa incit sa nu se execute initializarile; vezi &9.11.
Initializarile variabilelor cu clasa de memorie static (&4.4) se fac numai o data si anume cind incepe executia programului.
10 Definitii de functii
Un program consta dintr-un sir de declaratii. Codul pentru orice functie poate fi dat numai in afara oricarui bloc sau intr-o declaratie de clasa. Definitiile de functii au forma:
function_definition:
decl_specifiers_opt fct_declarator base_initializer_opt fct_body
decl_specifiers register, auto, typedef pot sa nu fie utilizati, iar friend si virtual pot fi utilizate numai intr-o definitie de clasa (&8.5). Un declarator de functie este un declarator pentru o 'functie care returneaza ' (&8.4). Argumentele formale sint domeniul celui mai extern bloc al corpului functiei. Declaratorii de functie au forma:
fct_declarator:
declarator(argument_declaration_list) Daca un argument este specificat register, argumentul actual corespunzator va fi copiat, daca este posibil, intr-un registru din afara setului functiei. Daca o expresie constanta este specificata ca un initializator pentru un argument aceasta valoare se utilizeaza ca o valoare implicita a argumentului.
Corpul functiei are forma:
fct_body:
compound_statement
Exemplu complet de definitie de functie.
int max(int a, int b, int c)
Aici int este specificatorul de tip; max(int a,int b, intc) este fct_declarator; este corpul functiei.
Intrucit in contextul unei expresii un nume de tablou (in particular ca argument efectiv) se ia drept pointer la primul element al tabloului, declaratia de argument formal 'array of' se ajusteaza pentru a fi citit ca 'pointer la '.
Initializatorii pentru o clasa de baza si pentru membri pot fi specificati in definitia constructorului. Aceasta este cel mai util pentru obiectele de clasa, constante si referinte unde semanticile de initializare si asignare difera. Un initializator al bazei are forma:
base_initializer:
:member_initializer_list member_initializer_list:
member_initializer
member_initializer, member_initializer_list
member_initializer:
identifier_opt(argument_list_opt)
Daca identifier este prezent intr-un member_initializer argumentul lista se utilizeaza pentru clasa de baza. De exemplu:
struct base;
struct derived : base;
derived::derived(int a) : (a+1), b(a+2), c(a+3)
derived d(10);
Intii, se apeleaza constructorul clasei de baza base::base() pentru obiectul d cu argumentul 11; apoi constructorul pentru membrul b cu argumentul 12 si constructorul pentru membrul c cu argumentul 13; apoi se executa corpul derived::derived() (vezi &8.5.5). Ordinea in care se apeleaza constructorii pentru membri este nespecificata. Daca clasa de baza are un constructor care poate fi apelat fara argumente, nu se furnizeaza nici o lista de argumente. Daca membrul unei clase are un constructor care poate fi apelat fara argumente, atunci nu este necesar sa se furnizeze nici un argument pentru acel membru.
11 Linii de control ale compilatorului
Compilatorul contine un preprocesor capabil de macrosubstitutie, compilare conditionala si incluziune de fisiere denumite. Liniile care incep cu # comunica cu acest preprocesor. Aceste linii au sintaxa independenta de restul limbajului; ele pot apare oriunde si au efecte independente de domeniu si sint valabile pina la sfirsitul fisierului cu programul sursa. Sa observam ca definitiile const si inline sint o alta alternativa fata de multe utilizari ale lui #define.
11.1 Substitutia de siruri
O linie de control a compilatorului de forma:
#define identifier token_string
face ca preprocesorul sa inlocuiasca intrarile ulterioare ale lui identifier cu sirul token_string dat. Punctul si virgula din token_string sau de la sfirsitul lui sint parte a sirului de substitutie. O linie de forma:
#define identifier(identifier, , identifier) token_string
unde nu exista spatiu intre identificator si '(', este macrodefinitie cu argumente. Intrarile ulterioare ale primului identificator urmat de '(' si de siruri delimitate prin virgula si terminate prin ')' se inlocuiesc prin token_string din definitie. Fiecare aparitie a unui identificator mentionat in lista argumentelor formale a definitiei se inlocuieste prin sirul corespunzator de la apel. Argumentele efective din apel sint sirurile separate prin virgule; virgulele din sirurile incluse intre ghilimele nu separa argumente. Numarul argumentelor formale si reale trebuie sa fie acelasi. Sirurile si constantele caracter din token_string sint analizate pentru descoperirea argumentelor formale. O definitie lunga poate fi continuata pe o alta linie utilizind la sfirsitul liniei de continuat. O linie de forma:
#undef identifier
face ca definitia preprocesor a lui identifier sa fie anulata.
11.2 Incluziune de fisiere
O linie de control a compilatorului de forma:
#include 'filename'
face ca linia respectiva sa fie inlocuita prin continutul fisierului filename. Fisierul denumit este cautat intii in directorul fisierului sursa, apoi intr-o secventa de locuri specificate standard. O linie de control de forma:
#include <filename>
cauta filename numai in locurile specificate standard si nu si in directorul fisierului sursa (cum se specifica locurile standard nu este parte a limbajului).
Directivele #include pot fi imbricate.
11.3 Compilarea conditionata
O linie de control a compilatorului de forma:
#if expression
verifica daca expresia este diferita de zero. Expresia trebuie sa fie o expresie constanta (&12). In plus fata de operatiile obisnuite din C++ se poate utiliza un identificator unar. Acesta cind se aplica la un identificator, atunci valoarea lui este diferita de zero daca identificatorul respectiv a fost definit utilizind #define si nu s-a utilizat ulterior pentru el #undef; altfel valoarea lui este zero.
O linie de control de forma:
#ifdef identifier
verifica daca identifier este definit curent in preprocesor, adica daca el a fost obiectul unei linii de control #define.
O linie de control de forma:
#ifndef identifier
verifica daca identifier este nedefinit curent in preprocesor. Toate cele trei forme sint urmate de un numar arbitrar de linii, care pot contine si o linie de control:
#else
si apoi de linia de control:
#endif
Daca conditia verificata este adevarata, atunci liniile dintre #else si #endif sint ignorate. Daca conditia verificata este falsa, atunci toate liniile dintre cea de test si #else sau, in lipsa lui #else, #endif sint ignorate. Aceste constructii pot fi imbricate.
11.4 Linie de control
In beneficiul altor preprocesoare care genereaza programe C++, o linie de forma:
#linie constant 'filename'
face ca, compilatorul sa considere ca numarul de linie al liniei sursa urmatoare se da printr-o constanta, iar fisierul de intrare curent este denumit prin identificator. Daca identificatorul lipseste, numele fisierului nu se schimba. Aceasta se face pentru a elimina erorile.
12 Expresii constante
In diferite locuri C++ cere expresii care se evalueaza ca o constanta: cum ar fi limitele unui tablou (&8.4), expresiile case (&9.7) argumentele implicite ale functiilor (&8.4) si initializatorii (&8.6). In ultimul caz expresia poate implica numai constante intregi, constante caracter, constante enumerative, valori const care nu sint agregate initializate cu expresii constante si expresii sizeof care pot fi legate prin operatorii binari:
+ - * / % & | ^ <<
>>==!=< > <=>=&&||
sau cei unari:
+ - ~ !
sau prin operatorul ternar:
?:
Parantezele pot fi utilizate pentru grupari, dar nu pentru apeluri de functii.
In alte cazuri expresiile constante pot de asemenea sa contina operatorul unar & aplicat la obiecte statice sau externe, sau la tablouri statice sau externe indexate cu o expresie constanta. Unarul & poate fi aplicat implicit prin aparitia unui tablou fara indici sau a unei functii. Regula de baza este ca initializatorii trebuie evaluati sau ca o constanta sau ca adresa a unui obiect declarat in prealabil static sau extern + sau - o constanta. O posibilitate mai mica este atinsa pentru o expresie constanta dupa #if; nume declarate const, expresii sizeof si constante enumerative nu sint admise.
13 Consideratii de portabilitate
Anumite parti ale lui C++ sint inerent dependente de masina. Urmarind lista necazurilor potentiale, bulinele nu inseamna ca vor apare toate 'necazurile', dar se sublinieaza unele dintre principalele necazuri. Caracteristicile curate hardware cum este marimea unui cuvint, proprietatile aritmeticii flotante si impartirea intreaga in practica au dovedit ca acestea nu constituie prea mult o problema. Alte aspecte ale
hardware-ului sint reflectate in diferite implementari. Unele dintre acestea, in particular extensia de semn (care converteste un caracter negativ intr-un intreg negativ) si ordinea in care octetii sint plasati in cuvint este o pacoste care trebuie privita cu multa grija. Majoritatea necazurilor celorlalte reprezinta doar probleme minore.
Numarul variabilelor registru care pot fi in realitate plasate in registrii variaza de la masina la masina, asa cum este de fapt si cu setul tipurilor valide. Nu mai putin, toate compilatoarele fac lucruri proprii masinii pentru care el a fost construit; declaratiile de variabile registru incorecte sau in exces sint ignorate.
Ordinea evaluarii argumentelor functiilor nu este specificata de catre limbaj. Aceasta ordine este de la dreapta la stinga pentru unele masini si de la stinga la dreapta pentru altele.
De cind constantele caracter sint obiecte reale ale tipului int, sint permise si constantele caracter multi-caracter. Implementarea specifica este foarte dependenta de masina deoarece ca- racterele sint asignate de la stinga la dreapta pentru unele masini si de la dreapta la stinga pentru altele.
14 Sumar de sintaxa
Acest sumar al sintaxei C++ se intentioneaza sa fie un ajutor pentru intelegerea limbajului. Ceea ce se prezinta nu sint instructiuni exacte ale limbajului.
14.1 Expresii
expression:
term
expression binary_operator expression
expression ? expression : expression
expression_list
expression_list:
expression
expression_list, expression
term:
primary_expression
unary_operator term
term++
term--
sizeof expression
sizeof (type_name)
(type_name) expression
simple_type_name (expression_list)
new type_name initializer_opt
new (type_name)
delete expression
delete [expression] expression
special_operator:
() []
free_store_operator: one of
new delete abstract_declarator:
empty
*abstract_declarator
abstract_declarator (argument_declaration_list) abstract_declarator [constant_expression_opt]
simple_type_name:
typedef_name
char
short
int
long
unsigned
float
double
void
typedef_name:
identifier
14.2 Declaratii
declaration:
decl_specifiers_opt declarator_list_opt;
name_declaration
asm declaration
name_declaration:
aggr identifier; enum identifier;
aggr:
class
struct
union
asm_declaration:
asm (string); decl_specifiers:
decl_specifier decl_specifiers_opt decl_specifier:
sc_specifier
type_specifier
fct_specifier
friend
typedef
type_specifier:
simple_type_name
class_specifier
enum_specifier
elaborated_type_specifier
const
sc_specifier:
auto
extern
register
static
fct_specifier:
inline
overload
virtual
elaborated_type_specifier:
key typedef_name
key identifier
key:
class
struct
union
enum
declarator_list:
init_declarator
init_declarator, declarator_list
init_declarator:
declarator initializer_opt declarator:
dname
(declarator)
const_opt declarator
& const_opt declarator declarator (argument_declaration_list) declarator [constant_expression_opt]
dname:
simple_dname
typedef_name::simple_dname
simple_dname:
identifier
typedef_name
~typedef_name
operator_function_name
conversion_function_name
operator_function_name:
operator operator conversion_function_name:
operator type argument_declaration_list:
arg_declaration_list_opt _opt arg_declaration_list:
arg_declaration_list, argument_declaration
argument_declaration
argument_declaration:
decl_specifiers declarator
decl_specifiers declarator = expression
decl_specifiers abstract_declarator
decl_specifiers abstract_declarator = expression
class_specifiers:
class_head
class_head
class_head:
aggr identifier_opt
aggr identifier:public_opt typedef_name
member_list:
member_declaration member_list_opt member_declaration:
decl_specifiers_opt member_declarator initializer_opt
function_definition;_opt
member_declarator:
declarator
identifier_opt:constant_expression
initializer:
= expression
( expression_list )
initializer_list:
expression
initializer_list, initializer_list
enum_specifier:
enum identifier_opt enum_list:
enumerator
enum_list, enumerator
enumerator:
identifier
identifier = constant_expression
14.3 Instructiuni
compound_statement:
statement_list:
statement
statement statement_list
statement:
declaration
compound_statement
expression_opt;
if(expression) statement
if(expression) statement else statement while(expression) statement do statement while(expression);
for(statement expression_opt;expression_opt) statement
switch(expression) statement
case constant_expression : statement
default : statement
break;
continue;
return expression_opt;
goto identifier;
identifier : statement;
14.4 Definitii externe
program:
external_definition
external_definition program
external_definition
funtion_definition
declaration
function_definition
decl_specifiers_opt fct_declarator base_initializer_opt
fct_body fct_declarator:
declarator(argument_declaration_list) fct_body:
compound_statement base_initializer:
:member_initializer_list member_initializer_list:
member_initializer
member_initializer, member_initializer_list
member_initializer:
identifier_opt (argument_list_opt)
14.5 Preprocesor
#define identifier token_string
#define identifier(identifier,, identifier) token_string
#else
#endif
#if expression
#ifdef identifier
#ifndef identifier
#include 'filename'
#include <filename>
#line constant 'filename'
#undef identifier
15 Diferente fata de C
15.1 Extensii
Tipurile argumentelor unei functii pot fi specificate (&7.1) si vor fi verificate (&7.1). Vor avea loc si conversiile de tip (&7.1). Aritmetica flotanta in simpla precizie poate fi folosita pentru expresii flotante (&6.2).
Numele functiilor pot fi supraincarcate (&8.9). Operatorii pot fi supraincarcati (&7.16, &8.5.11). Functiile pot fi substituite inline (&8.1).
Obiectele data pot fi constante (&8.4).Pot fi declarate obiecte ale tipului referinta (&8.4, &8.6.3).Alocarea si dealocarea sint furnizate de operatorii new si delete (&7.2).
Clasele pot furniza incapsularea datelor (&8.5.9), garanteaza initializarea (&8.6.2), conversiile definite de utilizator (&8.5.6) si tipizarea dinamica prin folosirea functiilor virtuale (&8.5.4). Numele unei clase sau enumerari este un nume de tip (&8.5). Orice pointer poate fi asignat spre void* fara folosirea unei matrite
(&7.14).
O declaratie in interiorul unui bloc este o instructiune (&9.14).
Pot fi declarate reuniuni fara nume (&8.5.13).
15.2 Sumar de incompatibilitati
Multe constructii in C sint legale in C++, intelesul lor raminind neschimbat. Exceptiile sint urmatoarele:
Programele C care folosesc unul din noile cuvinte cheie:
class const delete friend inline
new operator overload public signed
thisvirtualvolatile
ca identificatori, nu sint corecte.
In C++ declaratia functiei f() inseamna ca f nu primeste argumente, pe cind in C aceasta inseamna ca f poate lua argumente de orice tip. In C un nume extern poate fi definit de mai multe ori, pe cind in C++ trebuie sa fie definit exact o data.
Numele de clase din C++ se afla in acelasi domeniu al numelor valabil si pentru celelalte nume, lucru ilustrat in urmatoarele constructii:
int s;
struct s ;
void f()
void g()
15.3 Anacronisme
Extensiile prezentate aici pot fi furnizate pentru a face mai usoara utilizarea programelor C ca programe C++. Notati ca fiecare dintre aceste particularitati prezinta aspecte neastepta- te. O implementare care furnizeaza aceste extensii de asemenea poate furniza utilizatorului o cale de a se asigura ca aceste lucruri nu vor apare in fisierul sursa.
Numele inca nedefinite pot fi utilizate intr-un apel ca si numele de functii. In acest caz numele trebuie implicit declarat ca o functie ce returneaza int cu tipul argumentului ().
Cuvintul cheie void poate fi folosit pentru a indica faptul ca functia nu primeste argumente;deci void este echivalent cu ().
Programe utilizind sintaxa C pentru definirea functiilor.
old_function_definition:
decl_specifiers_opt old_function_declarator
declaration_list fct_body old_function_declarator:
declarator (parameter_list) parameter_list:
identifier
identifier, identifier
de exemplu, functia:
max(a, b)
poate fi utilizata.
Daca o functie definita ca cea de mai sus nu are o declaratie anterioara, tipul argumentelor ei vor fi (), care sint neverificate. Daca, in schimb, functia este anterior declarata, tipurile argumentelor ei trebuie sa corespunda cu cele din declaratie.
Un punct poate fi folosit in locul operatorului de rezolutie de domeniu :: pentru a specifica numele din definitia functiei membru. De exemplu:
int cl.fct()
Acelasi nume poate fi declarat pentru ambele clase sau enumerari si obiectul data sau functie, in acelasi scop.