C ++

Taxonomie categorie de expresie în C ++

Taxonomie categorie de expresie în C ++

Un calcul este orice tip de calcul care urmează un algoritm bine definit. O expresie este o secvență de operatori și operanzi care specifică un calcul. Cu alte cuvinte, o expresie este un identificator sau un literal, sau o secvență a ambelor, alăturate de operatori.În programare, o expresie poate duce la o valoare și / sau poate provoca unele întâmplări. Când rezultă o valoare, expresia este glvalue, rvalue, lvalue, xvalue sau prvalue. Fiecare dintre aceste categorii este un set de expresii. Fiecare set are o definiție și situații particulare în care predomină sensul său, diferențându-l de un alt set. Fiecare set se numește categorie de valori.

Notă: O valoare sau literal este încă o expresie, așa că acești termeni clasifică expresiile și nu valorile.

glvalue și rvalue sunt cele două subseturi din expresia setului mare. glvalue există în alte două subseturi: lvalue și xvalue. rvalue, celălalt subset pentru expresie, există și în alte două subseturi: xvalue și prvalue. Deci, xvalue este un subset al glvalue și rvalue: adică xvalue este intersecția ambelor glvalue și rvalue. Următoarea diagramă de taxonomie, preluată din specificația C ++, ilustrează relația tuturor seturilor:

prvalue, xvalue și lvalue sunt valorile categoriei principale. glvalue este uniunea valorilor și valorilor x, în timp ce valorile sunt uniunea valorilor și valorilor x.

Aveți nevoie de cunoștințe de bază în C ++ pentru a înțelege acest articol; aveți nevoie și de cunoștințe despre domeniul de aplicare în C++.

Conținutul articolului

Noțiuni de bază

Pentru a înțelege cu adevărat expresia taxonomiei categoriei, trebuie mai întâi să vă amintiți sau să cunoașteți următoarele caracteristici de bază: locație și obiect, stocare și resursă, inițializare, identificator și referință, referințe lvalue și rvalue, pointer, depozit gratuit și reutilizarea unui resursă.

Locație și obiect

Luați în considerare următoarea declarație:

int ident;

Aceasta este o declarație care identifică o locație în memorie. O locație este un set particular de octeți consecutivi în memorie. O locație poate consta din un octet, doi octeți, patru octeți, șaizeci și patru de octeți etc. Locația unui număr întreg pentru o mașină de 32 de biți este de patru octeți. De asemenea, locația poate fi identificată printr-un identificator.

În declarația de mai sus, locația nu are conținut. Înseamnă că nu are nicio valoare, deoarece conținutul este valoarea. Deci, un identificator identifică o locație (spațiu continuu mic). Când locației i se oferă un anumit conținut, identificatorul identifică apoi atât locația, cât și conținutul; adică identificatorul identifică apoi atât locația, cât și valoarea.

Luați în considerare următoarele afirmații:

int ident1 = 5;
int ident2 = 100;

Fiecare dintre aceste afirmații este o declarație și o definiție. Primul identificator are valoarea (conținutul) 5, iar al doilea identificator are valoarea 100. Într-o mașină de 32 de biți, fiecare dintre aceste locații are o lungime de patru octeți. Primul identificator identifică atât o locație, cât și o valoare. Al doilea identificator îi identifică și pe amândoi.

Un obiect este o regiune numită de stocare în memorie. Deci, un obiect este fie o locație fără valoare, fie o locație cu o valoare.

Depozitare și resurse de obiecte

Locația pentru un obiect este numită și stocarea sau resursa obiectului.

Inițializare

Luați în considerare următorul segment de cod:

int ident;
ident = 8;

Prima linie declară un identificator. Această declarație oferă o locație (stocare sau resursă) pentru un obiect întreg, identificându-l cu numele, ident. Următoarea linie pune valoarea 8 (în biți) în locația identificată prin ident. Punerea acestei valori este inițializarea.

Următoarea declarație definește un vector cu conținut, 1, 2, 3, 4, 5, identificat prin vtr:

std :: vector vtr 1, 2, 3, 4, 5;

Aici, inițializarea cu 1, 2, 3, 4, 5 se face în aceeași afirmație a definiției (declarație). Operatorul de atribuire nu este utilizat. Următoarea declarație definește o matrice cu conținut 1, 2, 3, 4, 5:

int arr [] = 1, 2, 3, 4, 5;

De data aceasta, pentru inițializare a fost utilizat un operator de atribuire.

Identificator și referință

Luați în considerare următorul segment de cod:

int ident = 4;
int & ref1 = ident;
int & ref2 = ident;
cout<< ident <<"<< ref1 <<"<< ref2 << '\n';

Ieșirea este:

4 4 4

ident este un identificator, în timp ce ref1 și ref2 sunt referințe; se referă la aceeași locație. O referință este un sinonim pentru un identificator. În mod convențional, ref1 și ref2 sunt nume diferite ale unui obiect, în timp ce ident este identificatorul aceluiași obiect. Cu toate acestea, ident poate fi numit în continuare numele obiectului, ceea ce înseamnă, ident, ref1 și ref2 denumesc aceeași locație.

Principala diferență între un identificator și o referință este că, atunci când este transmis ca argument către o funcție, dacă este transmis de identificator, se face o copie pentru identificatorul din funcție, în timp ce dacă este transmisă prin referință, aceeași locație este utilizată în funcţie. Deci, trecerea prin identificator se termină cu două locații, în timp ce trecerea prin referință se termină cu aceeași locație.

lvalue Reference și rvalue Reference

Modul normal de a crea o referință este următorul:

int ident;
ident = 4;
int & ref = ident;

Stocarea (resursa) este localizată și identificată mai întâi (cu un nume precum ident), apoi se face o referință (cu un nume precum un ref). Când treceți ca argument către o funcție, o copie a identificatorului va fi făcută în funcție, în timp ce pentru cazul unei referințe, locația originală va fi utilizată (menționată) în funcție.

Astăzi, este posibil să aveți doar o referință fără a o identifica. Aceasta înseamnă că este posibil să creați mai întâi o referință fără a avea un identificator pentru locație. Aceasta folosește &&, așa cum se arată în următoarea afirmație:

int && ref = 4;

Aici, nu există nicio identificare anterioară. Pentru a accesa valoarea obiectului, pur și simplu utilizați ref așa cum ați folosi identitatea de mai sus.

Cu declarația &&, nu există posibilitatea de a transmite un argument unei funcții prin identificator. Singura alegere este să treci prin referință. În acest caz, există o singură locație utilizată în cadrul funcției și nu a doua locație copiată ca la un identificator.

O declarație de referință cu & se numește lvalue reference. O declarație de referință cu && se numește rvalue reference, care este, de asemenea, o referință de valoare (vezi mai jos).

Pointer

Luați în considerare următorul cod:

int ptdInt = 5;
int * ptrInt;
ptrInt = &ptdInt;
cout<< *ptrInt <<'\n';

Ieșirea este 5.

Aici, ptdInt este un identificator ca identul de mai sus. Există două obiecte (locații) aici în loc de unul: obiectul ascuțit, ptdInt identificat prin ptdInt și obiectul pointer, ptrInt identificat prin ptrInt. & ptdInt returnează adresa obiectului ascuțit și o pune ca valoare în obiectul pointer ptrInt. Pentru a returna (obține) valoarea obiectului ascuțit, utilizați identificatorul pentru obiectul pointer, ca în „* ptrInt”.

Notă: ptdInt este un identificator și nu o referință, în timp ce numele, ref, menționat anterior, este o referință.

A doua și a treia linie din codul de mai sus pot fi reduse la o linie, ducând la următorul cod:

int ptdInt = 5;
int * ptrInt = &ptdInt;
cout<< *ptrInt <<'\n';

Notă: Când un indicator este incrementat, acesta indică următoarea locație, care nu este o adăugare a valorii 1. Când un indicator este decrementat, acesta indică locația anterioară, care nu este o scădere a valorii 1.

Magazin gratuit

Un sistem de operare alocă memorie pentru fiecare program care rulează. O memorie care nu este alocată niciunui program este cunoscută sub numele de magazin gratuit. Expresia care returnează o locație pentru un număr întreg din magazinul gratuit este:

nou int

Aceasta returnează o locație pentru un întreg care nu este identificat. Următorul cod ilustrează modul de utilizare a indicatorului cu magazinul gratuit:

int * ptrInt = nou int;
* ptrInt = 12;
cout<< *ptrInt  <<'\n';

Ieșirea este 12.

Pentru a distruge obiectul, utilizați expresia de ștergere după cum urmează:

ștergeți ptrInt;

Argumentul pentru expresia de ștergere este un indicator. Următorul cod ilustrează utilizarea acestuia:

int * ptrInt = nou int;
* ptrInt = 12;
ștergeți ptrInt;
cout<< *ptrInt <<'\n';

Ieșirea este 0, și nu ceva de genul nul sau nedefinit. șterge înlocuiește valoarea locației cu valoarea implicită a tipului particular de locație, apoi permite reutilizarea locației. Valoarea implicită pentru o locație int este 0.

Reutilizarea unei resurse

În taxonomia categoriilor de expresii, reutilizarea unei resurse este la fel ca reutilizarea unei locații sau stocare pentru un obiect. Următorul cod ilustrează modul în care o locație din magazinul gratuit poate fi refolosită:

int * ptrInt = nou int;
* ptrInt = 12;
cout<< *ptrInt <<'\n';
ștergeți ptrInt;
cout<< *ptrInt <<'\n';
* ptrInt = 24;
cout<< *ptrInt <<'\n';

Ieșirea este:

12
0
24

O valoare 12 este atribuită mai întâi locației neidentificate. Apoi, conținutul locației este șters (în teorie obiectul este șters). Valoarea 24 este reatribuită aceleiași locații.

Următorul program arată cum este refolosită o referință întreagă returnată de o funcție:

#include
folosind spațiul de nume std;
int & fn ()

int i = 5;
int & j = i;
retur j;

int main ()

int & myInt = fn ();
cout<< myInt <<'\n';
myInt = 17;
cout<< myInt <<'\n';
retur 0;

Ieșirea este:

5
17

Un obiect precum i, declarat într-un domeniu de aplicare local (domeniul de funcții), încetează să mai existe la sfârșitul domeniului de aplicare local. Cu toate acestea, funcția fn () de mai sus, returnează referința lui i. Prin această referință returnată, numele, myInt în funcția main (), reutilizează locația identificată de i pentru valoarea 17.

valoare

O valoare este o expresie a cărei evaluare determină identitatea unui obiect, câmp de biți sau funcție. Identitatea este o identitate oficială, cum ar fi ident de mai sus, sau un nume de referință lvalue, un pointer sau numele unei funcții. Luați în considerare următorul cod care funcționează:

int myInt = 512;
int & myRef = myInt;
int * ptr = &myInt;
int fn ()

++ptr; --ptr;
returnează myInt;

Aici, myInt este o valoare; myRef este o expresie de referință de valoare; * ptr este o expresie de valoare deoarece rezultatul său este identificabil cu ptr; ++ ptr sau -ptr este o expresie lvalue deoarece rezultatul său este identificabil cu noua stare (adresa) a ptr, iar fn este o valoare (expresie).

Luați în considerare următorul segment de cod:

int a = 2, b = 8;
int c = a + 16 + b + 64;

În a doua afirmație, locația pentru „a” are 2 și este identificabilă prin „a”, la fel și o valoare. Locația pentru b are 8 și este identificabilă prin b, la fel și o valoare. Locația pentru c va avea suma și este identificabilă prin c, la fel și o valoare. În a doua afirmație, expresiile sau valorile 16 și 64 sunt valori (vezi mai jos).

Luați în considerare următorul segment de cod:

char seq [5];
seq [0] = 'l', seq [1] = 'o', seq [2] = 'v', seq [3] = 'e', ​​seq [4] = '\ 0';
cout<< seq[2] <<'\n';

Ieșirea este „v';

seq este o matrice. Locația pentru „v” sau orice valoare similară din matrice este identificată prin secțiunea [i], unde i este un index. Deci, expresia, sec [i], este o expresie de valoare. seq, care este identificatorul pentru întreaga matrice, este, de asemenea, o valoare.

valoare

O valoare este o expresie a cărei evaluare inițializează un obiect sau un câmp de biți sau calculează valoarea operandului unui operator, așa cum este specificat de contextul în care apare.

În declarație,

int myInt = 256;

256 este un prvalue (expresie prvalue) care inițializează obiectul identificat de myInt. Acest obiect nu este menționat.

În declarație,

int && ref = 4;

4 este o valoare (expresie de valoare) care inițializează obiectul la care se face referire prin ref. Acest obiect nu este identificat oficial. ref este un exemplu de expresie de referință de valoare sau expresie de referință de valoare; este un nume, dar nu un identificator oficial.

Luați în considerare următorul segment de cod:

int ident;
ident = 6;
int & ref = ident;

6 este o valoare care inițializează obiectul identificat prin ident; obiectul este de asemenea referit prin ref. Aici, referința este o referință de valoare și nu o referință de valoare.

Luați în considerare următorul segment de cod:

int a = 2, b = 8;
int c = a + 15 + b + 63;

15 și 63 sunt fiecare o constantă care se calculează la sine, producând un operand (în biți) pentru operatorul de adunare. Deci, 15 sau 63 este o expresie de valoare.

Orice literal, cu excepția literalului șir, este o valoare (i.e., o expresie de valoare). Deci, un literal cum ar fi 58 sau 58.53, sau adevărat sau fals, este o valoare. Un literal poate fi folosit pentru a inițializa un obiect sau ar calcula pentru sine (într-o altă formă în biți) ca valoare a unui operand pentru un operator. În codul de mai sus, literalul 2 inițializează obiectul, a. De asemenea, se calculează ca un operand pentru operatorul de atribuire.

De ce un șir literal nu este o valoare? Luați în considerare următorul cod:

char str [] = "dragostea nu urăște";
cout << str <<'\n';
cout << str[5] <<'\n';

Ieșirea este:

iubesc nu urăsc
n

str identifică întregul șir. Deci, expresia, str, și nu ceea ce identifică, este o valoare. Fiecare caracter din șir poate fi identificat prin str [i], unde i este un index. Expresia, str [5], și nu caracterul pe care îl identifică, este o valoare. Șirul literal este un lvalue și nu un prvalue.

În următoarea declarație, o matrice literal inițializează obiectul, arr:

ptrInt ++ sau ptrInt-- 

Aici, ptrInt este un pointer către o locație întreagă. Întreaga expresie și nu valoarea finală a locației pe care o indică este o valoare (expresie). Acest lucru se datorează faptului că expresia, ptrInt ++ sau ptrInt-, identifică prima valoare originală a locației sale și nu a doua valoare finală a aceleiași locații. Pe de altă parte, -ptrInt sau -ptrInt este o valoare, deoarece identifică singura valoare a interesului în locație. Un alt mod de a-l privi este că valoarea inițială calculează a doua valoare finală.

În a doua afirmație a codului următor, a sau b pot fi considerate în continuare ca o valoare:

int a = 2, b = 8;
int c = a + 15 + b + 63;

Deci, a sau b în a doua afirmație este o valoare, deoarece identifică un obiect. Este, de asemenea, o valoare, deoarece calculează la numărul întreg al unui operand pentru operatorul de adunare.

(new int), și nu locația pe care o stabilește este o valoare. În următoarea declarație, adresa de returnare a locației este atribuită unui obiect pointer:

int * ptrInt = nou int

Aici, * ptrInt este o valoare, în timp ce (new int) este o valoare. Amintiți-vă, o valoare sau o valoare este o expresie. (new int) nu identifică niciun obiect. Returnarea adresei nu înseamnă identificarea obiectului cu un nume (cum ar fi ident, mai sus). În * ptrInt, numele, ptrInt, este ceea ce identifică cu adevărat obiectul, deci * ptrInt este o valoare. Pe de altă parte, (new int) este o valoare, deoarece calculează o nouă locație la o adresă a valorii operandului pentru operatorul de atribuire =.

xvalue

Astăzi, lvalue reprezintă Valoarea locației; prvalue reprezintă valoarea „pură” (vezi ce înseamnă rvalue mai jos). Astăzi, xvalue înseamnă „eXpiring” lvalue.

Definiția xvalue, citată din specificația C ++, este după cum urmează:

„Un xvalue este un glvalue care denotă un obiect sau câmp de biți ale cărui resurse pot fi refolosite (de obicei pentru că este aproape de sfârșitul vieții sale).

Ceea ce înseamnă acest lucru este că atât lvalue cât și prvalue pot expira. Următorul cod (copiat de mai sus) arată cum este reutilizată stocarea (resursa) lvalue, * ptrInt după ce a fost șters.

int * ptrInt = nou int;
* ptrInt = 12;
cout<< *ptrInt <<'\n';
ștergeți ptrInt;
cout<< *ptrInt <<'\n';
* ptrInt = 24;
cout<< *ptrInt <<'\n';

Ieșirea este:

12
0
24

Următorul program (copiat de mai sus) arată cum este reutilizată stocarea unei referințe întregi, care este o referință de valoare returnată de o funcție, în funcția main ():

#include
folosind spațiul de nume std;
int & fn ()

int i = 5;
int & j = i;
retur j;

int main ()

int & myInt = fn ();
cout<< myInt <<'\n';
myInt = 17;
cout<< myInt <<'\n';
retur 0;

Ieșirea este:

5
17

Când un obiect precum i în funcția fn () iese din sfera de aplicare, este în mod natural distrus. În acest caz, stocarea lui i a fost încă reutilizată în funcția main ().

Cele două eșantioane de cod de mai sus ilustrează reutilizarea stocării valorilor. Este posibil să aveți o reutilizare a stocării de valori (rvalues) (a se vedea mai târziu).

Următorul citat referitor la xvalue provine din specificația C ++:

„În general, efectul acestei reguli este că referințele rvalue numite sunt tratate ca valori și referințele rvalue nenumite la obiecte sunt tratate ca valori x. Referințele rvalue la funcții sunt tratate ca lvalues, fie că sunt denumite sau nu." (ne vedem mai tarziu).

Deci, un xvalue este un lvalue sau un prvalue ale cărui resurse (stocare) pot fi refolosite. xvalues ​​este setul de intersecție al valorilor și valorilor.

Xvalue are mai mult decât ceea ce a fost abordat în acest articol. Cu toate acestea, xvalue merită un articol întreg singur, astfel încât specificațiile suplimentare pentru xvalue nu sunt abordate în acest articol.

Set de taxonomie pentru categoria de expresii

O altă cotație din specificația C ++:

Notă: Din punct de vedere istoric, valorile și valorile erau așa-numite, deoarece acestea puteau apărea în partea stângă și dreaptă a unei sarcini (deși acest lucru nu mai este în general adevărat); valorile gl sunt valori „generalizate”, valorile sunt valori „pure” și valorile x sunt valori „eXpirante”. În ciuda numelor lor, acești termeni clasifică expresii, nu valori. - nota de final ”

Deci, glvalues ​​este uniunea set de lvalues ​​și xvalues ​​și rvalues ​​sunt uniunea set de xvalues ​​și prvalues. xvalues ​​este setul de intersecție al valorilor și valorilor.

Începând de acum, expresia taxonomiei categoriei este mai bine ilustrată cu o diagramă Venn după cum urmează:

Concluzie

O valoare este o expresie a cărei evaluare determină identitatea unui obiect, câmp de biți sau funcție.

O valoare este o expresie a cărei evaluare inițializează un obiect sau un câmp de biți sau calculează valoarea operandului unui operator, așa cum este specificat de contextul în care apare.

Un xvalue este un lvalue sau un prvalue, cu proprietatea suplimentară că resursele sale (stocare) pot fi refolosite.

Specificația C ++ ilustrează taxonomia categoriilor de expresie cu o diagramă arborescentă, indicând faptul că există o anumită ierarhie în taxonomie. În prezent, nu există ierarhie în taxonomie, deci o diagramă Venn este utilizată de unii autori, deoarece ilustrează taxonomia mai bine decât diagrama arborelui.

Best Gamepad Mapping Apps for Linux
If you like to play games on Linux with a gamepad instead of a typical keyboard and mouse input system, there are some useful apps for you. Many PC ga...
Instrumente utile pentru jucătorii Linux
Dacă vă place să jucați jocuri pe Linux, este posibil să fi folosit aplicații și utilitare precum Wine, Lutris și OBS Studio pentru a îmbunătăți exper...
Jocuri HD remasterizate pentru Linux care nu au avut niciodată lansare Linux mai devreme
Mulți dezvoltatori și editori de jocuri vin cu remasterizarea HD a jocurilor vechi pentru a prelungi durata de viață a francizei, vă rog fanilor să so...