C++

Expressiecategorie Taxonomie in C++

Expressiecategorie Taxonomie in C++

Een berekening is elk type berekening dat een goed gedefinieerd algoritme volgt. Een uitdrukking is een reeks operatoren en operanden die een berekening specificeert. Met andere woorden, een uitdrukking is een identifier of een letterlijke, of een reeks van beide, samengevoegd door operators.Bij het programmeren kan een uitdrukking resulteren in een waarde en/of ervoor zorgen dat er iets gebeurt. Als het resulteert in een waarde, is de uitdrukking een glvalue, rvalue, lvalue, xvalue of prvalue. Elk van deze categorieën is een reeks uitdrukkingen. Elke set heeft een definitie en bepaalde situaties waarin de betekenis de overhand heeft, waardoor deze zich onderscheidt van een andere set. Elke set wordt een waardecategorie genoemd.

Opmerking: Een waarde of letterlijke is nog steeds een uitdrukking, dus deze termen classificeren uitdrukkingen en niet echt waarden.

glvalue en rvalue zijn de twee subsets van de grote set-expressie. glvalue bestaat in nog twee subsets: lvalue en xvalue. rvalue, de andere subset voor expressie, bestaat ook in twee andere subsets: xvalue en prvalue. Dus xvalue is een subset van zowel glvalue als rvalue: dat wil zeggen, xvalue is het snijpunt van zowel glvalue als rvalue. Het volgende taxonomiediagram, ontleend aan de C++-specificatie, illustreert de relatie van alle sets:

prvalue, xvalue en lvalue zijn de primaire categoriewaarden. glvalue is de vereniging van lwaarden en xwaarden, terwijl rwaarden de vereniging zijn van xwaarden en prwaarden.

Je hebt basiskennis van C++ nodig om dit artikel te begrijpen; je hebt ook kennis nodig van Scope in C++.

Artikel Inhoud

Basis

Om de taxonomie van de uitdrukkingscategorie echt te begrijpen, moet u zich eerst de volgende basisfuncties herinneren of kennen: locatie en object, opslag en bron, initialisatie, identifier en referentie, lvalue- en rvalu-referenties, aanwijzer, gratis opslag en hergebruik van een hulpbron.

Locatie en object

Denk aan de volgende verklaring:

int ident;

Dit is een verklaring die een locatie in het geheugen identificeert. Een locatie is een bepaalde reeks opeenvolgende bytes in het geheugen. Een locatie kan bestaan ​​uit één byte, twee bytes, vier bytes, vierenzestig bytes, enz. De locatie voor een geheel getal voor een 32-bits machine is vier bytes. Ook kan de locatie worden geïdentificeerd door een identifier.

In de bovenstaande verklaring heeft de locatie geen inhoud. Het betekent dat het geen waarde heeft, omdat de inhoud de waarde is. Een identifier identificeert dus een locatie (kleine doorlopende ruimte). Wanneer de locatie een bepaalde inhoud krijgt, identificeert de identifier vervolgens zowel de locatie als de inhoud; dat wil zeggen, de identifier identificeert vervolgens zowel de locatie als de waarde.

Denk aan de volgende uitspraken:

int ident1 = 5;
int ident2 = 100;

Elk van deze verklaringen is een verklaring en een definitie. De eerste identifier heeft de waarde (inhoud) 5 en de tweede identifier heeft de waarde 100. In een 32-bits machine is elk van deze locaties vier bytes lang. De eerste identifier identificeert zowel een locatie als een waarde. De tweede identifier identificeert ook beide.

Een object is een genoemd opslaggebied in het geheugen. Een object is dus ofwel een locatie zonder waarde of een locatie met een waarde.

Objectopslag en bronnen

De locatie voor een object wordt ook wel de opslag of bron van het object genoemd.

Initialisatie

Overweeg het volgende codesegment:

int ident;
identiteit = 8;

De eerste regel verklaart een identifier. Deze declaratie geeft een locatie (opslag of bron) voor een integer object, en identificeert het met de naam, ident. De volgende regel plaatst de waarde 8 (in bits) op de locatie die wordt geïdentificeerd door ident. Het plaatsen van deze waarde is initialisatie.

De volgende instructie definieert een vector met inhoud, 1, 2, 3, 4, 5, geïdentificeerd door vtr:

std::vector vtr1, 2, 3, 4, 5;

Hier wordt de initialisatie met 1, 2, 3, 4, 5 gedaan in dezelfde verklaring van de definitie (declaratie). De toewijzingsoperator wordt niet gebruikt. De volgende instructie definieert een array met inhoud 1, 2, 3, 4, 5:

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

Deze keer is er een toewijzingsoperator gebruikt voor de initialisatie.

Identificatie en referentie

Overweeg het volgende codesegment:

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

De uitvoer is:

4 4 4

ident is een identifier, terwijl ref1 en ref2 referenties zijn; ze verwijzen naar dezelfde locatie. Een verwijzing is een synoniem voor een identifier. Conventioneel zijn ref1 en ref2 verschillende namen van één object, terwijl ident de identifier is van hetzelfde object. Ident kan echter nog steeds de naam van het object worden genoemd, wat betekent dat ident, ref1 en ref2 dezelfde locatie noemen.

Het belangrijkste verschil tussen een identifier en een referentie is dat, indien doorgegeven als argument aan een functie, indien doorgegeven door identifier, er een kopie wordt gemaakt voor de identifier in de functie, terwijl indien doorgegeven via referentie, dezelfde locatie wordt gebruikt binnen de functie. Dus het passeren van een identificatie eindigt met twee locaties, terwijl het passeren van een referentie eindigt met dezelfde locatie same.

lvalue-referentie en rvalu-referentie:

De normale manier om een ​​referentie aan te maken is als volgt:

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

De opslag (resource) wordt eerst gelokaliseerd en geïdentificeerd (met een naam zoals ident), en vervolgens wordt een verwijzing gemaakt (met een naam zoals een ref). Bij het doorgeven als argument aan een functie wordt een kopie van de identifier gemaakt in de functie, terwijl in het geval van een referentie de originele locatie wordt gebruikt (naar verwezen) in de functie.

Tegenwoordig is het mogelijk om gewoon een referentie te hebben zonder deze te identificeren. Dit betekent dat het mogelijk is om eerst een referentie aan te maken zonder een identifier voor de locatie te hebben. Dit maakt gebruik van &&, zoals weergegeven in de volgende verklaring:

int&& ref = 4;

Hier is geen voorafgaande identificatie. Om toegang te krijgen tot de waarde van het object, gebruik je gewoon ref zoals je de ident hierboven zou gebruiken.

Met de && declaratie is er geen mogelijkheid om een ​​argument door te geven aan een functie door identifier. De enige keuze is om door te verwijzen naar referentie. In dit geval wordt er maar één locatie gebruikt binnen de functie en niet de tweede gekopieerde locatie zoals bij een identifier.

Een referentiedeclaratie met & heet lvalue reference. Een referentiedeclaratie met && wordt rvalue reference genoemd, wat ook een prvalue reference is (zie hieronder).

Wijzer

Beschouw de volgende code:

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

De uitvoer is: 5.

Hier is ptdInt een identifier zoals de ident hierboven. Er zijn hier twee objecten (locaties) in plaats van één: het puntige object, ptdInt geïdentificeerd door ptdInt, en het aanwijzerobject, ptrInt geïdentificeerd door ptrInt. &ptdInt retourneert het adres van het puntige object en plaatst het als de waarde in het pointer ptrInt-object. Om de waarde van het puntige object te retourneren (verkrijgen), gebruikt u de identifier voor het aanwijzerobject, zoals in "*ptrInt".

Opmerking: ptdInt is een identifier en geen referentie, terwijl de naam, ref, eerder genoemd, een referentie is.

De tweede en derde regel in de bovenstaande code kunnen worden teruggebracht tot één regel, wat leidt tot de volgende code:

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

Opmerking: Wanneer een aanwijzer wordt verhoogd, wijst deze naar de volgende locatie, wat geen optelling is van de waarde 1. Wanneer een aanwijzer wordt verlaagd, wijst deze naar de vorige locatie, wat geen aftrekking is van de waarde 1.

Gratis winkel

Een besturingssysteem wijst geheugen toe aan elk programma dat wordt uitgevoerd. Een geheugen dat aan geen enkel programma is toegewezen, staat bekend als de vrije winkel. De expressie die een locatie retourneert voor een geheel getal uit de gratis opslag is:

nieuw int

Dit retourneert een locatie voor een geheel getal dat niet is geïdentificeerd. De volgende code illustreert hoe u de aanwijzer gebruikt met de gratis winkel:

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

De uitvoer is: 12.

Gebruik de delete-expressie als volgt om het object te vernietigen:

verwijder ptrInt;

Het argument voor de delete-expressie is een pointer. De volgende code illustreert het gebruik ervan:

int *ptrInt = nieuwe int;
*ptrInt = 12;
verwijder ptrInt;
cout<< *ptrInt <<'\n';

De uitvoer is: 0, en niet zoiets als null of undefined. delete vervangt de waarde voor de locatie door de standaardwaarde van het specifieke type van de locatie, waarna de locatie opnieuw kan worden gebruikt. De standaardwaarde voor een int-locatie is 0.

Een bron hergebruiken

In de taxonomie van de uitdrukkingscategorie is het hergebruiken van een resource hetzelfde als het hergebruiken van een locatie of opslag voor een object. De volgende code illustreert hoe een locatie uit de gratis winkel opnieuw kan worden gebruikt:

int *ptrInt = nieuwe int;
*ptrInt = 12;
cout<< *ptrInt <<'\n';
verwijder ptrInt;
cout<< *ptrInt <<'\n';
*ptrInt = 24;
cout<< *ptrInt <<'\n';

De uitvoer is:

12
0
24

Een waarde van 12 wordt eerst toegewezen aan de niet-geïdentificeerde locatie. Dan wordt de inhoud van de locatie verwijderd (in theorie wordt het object verwijderd). De waarde van 24 wordt opnieuw toegewezen aan dezelfde locatie.

Het volgende programma laat zien hoe een integer-referentie die door een functie wordt geretourneerd, opnieuw wordt gebruikt:

#include
namespace std; gebruiken;
int&fn()

int i = 5;
int&j = ik;
retour j;

int hoofd()

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

De uitvoer is:

5
17

Een object zoals i, gedeclareerd in een lokaal bereik (functiebereik), houdt op te bestaan ​​aan het einde van het lokale bereik. De functie fn() hierboven geeft echter de referentie van i . terug. Via deze geretourneerde referentie hergebruikt de naam, myInt in de main()-functie, de locatie geïdentificeerd door i voor de waarde 17.

waardevalu

Een lwaarde is een uitdrukking waarvan de evaluatie de identiteit van een object, bitveld of functie bepaalt. De identiteit is een officiële identiteit zoals ident hierboven, of een lvalue-referentienaam, een aanwijzer of de naam van een functie. Overweeg de volgende code die werkt:

int mijnInt = 512;
int& mijnRef = mijnInt;
int* ptr = &myInt;
int fn()

++pnt; --ptr;
retourneer mijnInt;

Hier is myInt een waarde; myRef is een lvalue-referentie-expressie; *ptr is een lvalue-expressie omdat het resultaat identificeerbaar is met ptr; ++ptr of -ptr is een lvalue-expressie omdat het resultaat identificeerbaar is met de nieuwe staat (adres) van ptr, en fn is een lvalue (expressie).

Overweeg het volgende codesegment:

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

In de tweede verklaring heeft de locatie voor 'a' 2 en is herkenbaar aan 'a', net als een l-waarde. De locatie voor b heeft 8 en is herkenbaar aan b, en dat geldt ook voor een lwaarde. De locatie voor c heeft de som en is herkenbaar aan c, en dat geldt ook voor een lwaarde. In de tweede instructie zijn de uitdrukkingen of waarden van 16 en 64 rwaarden (zie hieronder).

Overweeg het volgende codesegment:

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

De uitvoer is 'v';

seq is een array. De locatie voor 'v' of een vergelijkbare waarde in de array wordt geïdentificeerd door seq[i], waarbij i een index is. Dus de uitdrukking, seq[i], is een lwaarde-uitdrukking. seq, de identifier voor de hele array, is ook een lvalue.

prwaarde

Een prvalue is een uitdrukking waarvan de evaluatie een object of een bitveld initialiseert of de waarde van de operand van een operator berekent, zoals gespecificeerd door de context waarin deze verschijnt.

In de verklaring,

int mijnInt = 256;

256 is een prvalue (prvalue-expressie) die het object initialiseert dat wordt geïdentificeerd door myInt. Er wordt niet naar dit object verwezen.

In de verklaring,

int&& ref = 4;

4 is een prvalue (prvalue-expressie) die het object initialiseert waarnaar wordt verwezen door ref. Dit object is niet officieel geïdentificeerd. ref is een voorbeeld van een rvalue-referentie-expressie of prvalue-referentie-expressie; het is een naam, maar geen officiële identificatie.

Overweeg het volgende codesegment:

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

6 is een pr-waarde die het door ident geïdentificeerde object initialiseert; het object wordt ook verwezen door ref. Hier is de ref een lvalue-referentie en geen prvalu-referentie.

Overweeg het volgende codesegment:

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

15 en 63 zijn elk een constante die zichzelf berekent en een operand (in bits) produceert voor de opteloperator. Dus 15 of 63 is een prvalue-expressie.

Elke letterlijke waarde, behalve de letterlijke tekenreeks, is een prwaarde (i.e., een prwaarde-expressie). Dus een letterlijke zoals 58 of 58.53, of waar of onwaar, is een prwaarde. Een letterlijke kan worden gebruikt om een ​​object te initialiseren of zou zichzelf (in een andere vorm in bits) berekenen als de waarde van een operand voor een operator. In de bovenstaande code initialiseert de letterlijke 2 het object, a. Het berekent zichzelf ook als een operand voor de toewijzingsoperator.

Waarom is een letterlijke tekenreeks geen pr-waarde?? Beschouw de volgende code:

char str[] = "liefde niet haten";
cout << str <<'\n';
cout << str[5] <<'\n';

De uitvoer is:

liefde niet haten
nee

str identificeert de hele string. Dus de uitdrukking, str, en niet wat het identificeert, is een lwaarde. Elk teken in de tekenreeks kan worden geïdentificeerd door str[i], waarbij i een index is. De uitdrukking, str[5], en niet het teken dat het identificeert, is een lwaarde. De letterlijke tekenreeks is een lwaarde en geen prwaardevalu.

In de volgende instructie initialiseert een array letterlijk het object, arr:

ptrInt++ of ptrInt-- 

Hier is ptrInt een pointer naar een integer-locatie. De hele uitdrukking, en niet de uiteindelijke waarde van de locatie waarnaar deze verwijst, is een prwaarde (uitdrukking). Dit komt omdat de uitdrukking, ptrInt++ of ptrInt-, de oorspronkelijke eerste waarde van zijn locatie identificeert en niet de tweede uiteindelijke waarde van dezelfde locatie. Aan de andere kant is -ptrInt of -ptrInt een lvalue omdat het de enige waarde van de interesse in de locatie identificeert. Een andere manier om ernaar te kijken is dat de oorspronkelijke waarde de tweede eindwaarde berekent.

In de tweede verklaring van de volgende code kan a of b nog steeds als een prwaarde worden beschouwd:

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

Dus a of b in de tweede instructie is een lwaarde omdat het een object identificeert. Het is ook een pr-waarde omdat het berekent naar het gehele getal van een operand voor de opteloperator.

(nieuwe int), en niet de locatie die het vaststelt, is een pr-waarde. In de volgende instructie wordt het retouradres van de locatie toegewezen aan een pointer-object:

int *ptrInt = nieuwe int

Hier is *ptrInt een lvalue, terwijl (new int) een prvalue is. Onthoud dat een lwaarde of een prwaarde een uitdrukking is. (new int) identificeert geen enkel object. Het adres retourneren betekent niet dat het object moet worden geïdentificeerd met een naam (zoals ident, hierboven). In *ptrInt is de naam, ptrInt, wat het object werkelijk identificeert, dus *ptrInt is een lwaarde. Aan de andere kant is (nieuwe int) een prwaarde, omdat het een nieuwe locatie berekent naar een adres van operandwaarde voor de toewijzingsoperator =.

xwaarde

Tegenwoordig staat lvalu voor Location Value; prvalue staat voor "pure" rvalue (zie hieronder waar rvalue voor staat). Tegenwoordig staat xvalue voor "eXpiring" lvalue.

De definitie van xvalue, geciteerd uit de C++-specificatie, is als volgt:

"Een x-waarde is een gl-waarde die een object of bitveld aangeeft waarvan de bronnen opnieuw kunnen worden gebruikt (meestal omdat het aan het einde van zijn levensduur is). [Voorbeeld: bepaalde soorten expressies met rvalue-referenties leveren x-waarden op, zoals een aanroep van een functie waarvan het retourtype een rvalue-referentie is of een cast naar een rvalue-referentietype-end voorbeeld]”

Wat dit betekent is dat zowel lvalue als prvalue kunnen verlopen. De volgende code (van bovenaf gekopieerd) laat zien hoe de opslag (resource) van de lvalue, *ptrInt wordt hergebruikt nadat deze is verwijderd.

int *ptrInt = nieuwe int;
*ptrInt = 12;
cout<< *ptrInt <<'\n';
verwijder ptrInt;
cout<< *ptrInt <<'\n';
*ptrInt = 24;
cout<< *ptrInt <<'\n';

De uitvoer is:

12
0
24

Het volgende programma (van bovenaf gekopieerd) laat zien hoe de opslag van een integer-referentie, wat een lvalue-referentie is die wordt geretourneerd door een functie, opnieuw wordt gebruikt in de main()-functie:

#include
namespace std; gebruiken;
int&fn()

int i = 5;
int&j = ik;
retour j;

int hoofd()

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

De uitvoer is:

5
17

Wanneer een object zoals i in de functie fn() buiten bereik gaat, wordt het natuurlijk vernietigd. In dit geval is de opslag van i nog steeds hergebruikt in de main()-functie.

De bovenstaande twee codevoorbeelden illustreren het hergebruik van de opslag van lvalues. Het is mogelijk om opslaghergebruik van prvalues ​​(rvalues) te hebben (zie later).

Het volgende citaat met betrekking tot xvalue komt uit de C++-specificatie:

“Over het algemeen is het effect van deze regel dat benoemde rvalue-referenties worden behandeld als lvalues ​​en niet-benoemde rvalue-referenties naar objecten worden behandeld als xvalues. rwaarde-verwijzingen naar functies worden behandeld als lwaarden, ongeacht of ze een naam hebben of niet." (zie later).

Dus een x-waarde is een l-waarde of een pr-waarde waarvan de bronnen (opslag) opnieuw kunnen worden gebruikt. xvalues ​​is de intersectieset van lvalues ​​en prvalues.

Er is meer aan xvalue dan wat in dit artikel is behandeld. xvalue verdient echter een heel artikel op zich, en daarom worden de extra specificaties voor xvalue in dit artikel niet behandeld.

Expressiecategorie Taxonomieset

Nog een citaat uit de C++-specificatie:

Opmerking: Historisch gezien werden lvalues ​​en rvalues ​​zo genoemd omdat ze aan de linker- en rechterkant van een opdracht konden voorkomen (hoewel dit over het algemeen niet meer zo is); gl-waarden zijn "gegeneraliseerde" l-waarden, pr-waarden zijn "pure" r-waarden en x-waarden zijn "eXpiring" l-waarden. Ondanks hun namen classificeren deze termen uitdrukkingen, geen waarden. - eindnoot”

Dus glvalues ​​is de uniereeks van lwaarden en xwaarden en rwaarden zijn de uniereeks van xwaarden en prwaarden. xvalues ​​is de intersectieset van lvalues ​​en prvalues.

Vanaf nu wordt de taxonomie van de uitdrukkingscategorie als volgt beter geïllustreerd met een Venn-diagram:

Conclusie

Een lwaarde is een uitdrukking waarvan de evaluatie de identiteit van een object, bitveld of functie bepaalt.

Een prvalue is een uitdrukking waarvan de evaluatie een object of een bitveld initialiseert of de waarde van de operand van een operator berekent, zoals gespecificeerd door de context waarin deze verschijnt.

Een xvalue is een lvalue of een prvalue, met als extra eigenschap dat de resources (opslag) ervan opnieuw kunnen worden gebruikt.

De C++-specificatie illustreert expressiecategorietaxonomie met een boomdiagram, wat aangeeft dat er enige hiërarchie in de taxonomie is. Vanaf nu is er geen hiërarchie in de taxonomie, dus een Venn-diagram wordt door sommige auteurs gebruikt, omdat het de taxonomie beter illustreert dan het boomdiagram.

Middelste muisknop werkt niet in Windows 10
De middelste muis knop helpt u door lange webpagina's en schermen met veel gegevens te bladeren. Als dat stopt, zul je uiteindelijk het toetsenbord ge...
Hoe de linker- en rechtermuisknop op Windows 10 pc te veranderen
Het is nogal een norm dat alle computermuisapparaten ergonomisch zijn ontworpen voor rechtshandige gebruikers. Maar er zijn muisapparaten beschikbaar ...
Emuleer muisklikken door te zweven met Clickless Mouse in Windows 10
Het gebruik van een muis of toetsenbord in de verkeerde houding of overmatig gebruik kan leiden tot veel gezondheidsproblemen, waaronder spanning, car...