Uttrykkskategori Taksonomi i C ++

Uttrykkskategori Taksonomi i C ++
En beregning er alle typer beregninger som følger en veldefinert algoritme. Et uttrykk er en sekvens av operatører og operander som spesifiserer en beregning. Med andre ord, et uttrykk er en identifikator eller en bokstavelig, eller en sekvens av begge, sammen med operatører.Ved programmering kan et uttrykk resultere i en verdi og/eller forårsake noen som skjer. Når det resulterer i en verdi, er uttrykket en glvalue, rvalue, lvalue, xvalue eller prvalue. Hver av disse kategoriene er et sett med uttrykk. Hvert sett har en definisjon og spesielle situasjoner der dens betydning råder, og skiller den fra et annet sett. Hvert sett kalles en verdikategori.

Merk: En verdi eller bokstavelig er fremdeles et uttrykk, så disse begrepene klassifiserer uttrykk og ikke verdsetter verdier.

Glvalue og Rvalue er de to undergruppene fra Big Set -uttrykket. Glvalue eksisterer i to ytterligere undergrupper: Lvalue og Xvalue. Rvalue, den andre delmengden for uttrykk, eksisterer også i to ytterligere undergrupper: xvalue og prvalue. Så xvalue er en delmengde av både glvalue og rvalue: det vil si at xvalue er skjæringspunktet mellom både glvalue og rvalue. Følgende taksonomi -diagram, hentet fra C ++ -spesifikasjonen, illustrerer forholdet mellom alle settene:

prvalue, xvalue og lvalue er de primære kategorien verdier. Glvalue er foreningen av lvalues ​​og xvalues, mens rvalues ​​er foreningen av xvalues ​​og prvalues.

Du trenger grunnleggende kunnskap i C ++ for å forstå denne artikkelen; Du trenger også kunnskap om omfang i C++.

Artikkelinnhold

  • Grunnleggende
  • Lvalue
  • Prvalue
  • xvalue
  • Ekspresjonskategori Taksonomisett
  • Konklusjon

Grunnleggende

For virkelig å forstå uttrykket taksonomi, må du huske eller kjenne følgende grunnleggende funksjoner først: beliggenhet og objekt, lagring og ressurs, initialisering, identifikator og referanse, lvalue og rvalue referanser, peker, gratis butikk og gjenbruk av en ressurs.

Plassering og objekt

Tenk på følgende erklæring:

int identitet;

Dette er en erklæring som identifiserer et sted i minnet. Et sted er et bestemt sett med påfølgende byte i minnet. Et sted kan bestå av en byte, to byte, fire byte, sekstifire byte osv. Plasseringen for et heltall for en 32bit maskin er fire byte. Plasseringen kan også identifiseres av en identifikator.

I ovennevnte erklæring har ikke stedet noe innhold. Det betyr at det ikke har noen verdi, ettersom innholdet er verdien. Så en identifikator identifiserer et sted (liten kontinuerlig plass). Når plasseringen får et bestemt innhold, identifiserer identifikatoren både plasseringen og innholdet; det vil si at identifikatoren deretter identifiserer både plasseringen og verdien.

Tenk på følgende utsagn:

int ident1 = 5;
int identitet2 = 100;

Hver av disse uttalelsene er en erklæring og en definisjon. Den første identifikatoren har verdien (innholdet) 5, og den andre identifikatoren har verdien 100. I en 32bit maskin er hvert av disse stedene fire byte lang. Den første identifikatoren identifiserer både et sted og en verdi. Den andre identifikatoren identifiserer også begge deler.

Et objekt er et navngitt lagringsregion i minnet. Så et objekt er enten et sted uten en verdi eller et sted med en verdi.

Objektlagring og ressurs

Plasseringen for et objekt kalles også lagring eller ressurs for objektet.

Initialisering

Tenk på følgende kodesegment:

int identitet;
identitet = 8;

Den første linjen erklærer en identifikator. Denne erklæringen gir et sted (lagring eller ressurs) for et heltallobjekt, og identifiserer det med navnet, identitet. Neste linje setter verdien 8 (i biter) inn i stedet identifisert av identitet. Innføring av denne verdien er initialisering.

Følgende uttalelse definerer en vektor med innhold, 1, 2, 3, 4, 5, identifisert av VTR:

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

Her gjøres initialiseringen med 1, 2, 3, 4, 5 i samme uttalelse av definisjonen (erklæring). Oppdragsoperatøren brukes ikke. Følgende uttalelse definerer en matrise med innhold 1, 2, 3, 4, 5:

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

Denne gangen har en oppdragsoperatør blitt brukt til initialiseringen.

Identifikator og referanse

Tenk på følgende kodesegment:

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

Utgangen er:

4 4 4

Ident er en identifikator, mens ref1 og ref2 er referanser; De refererer til samme sted. En referanse er et synonym til en identifikator. Konvensjonelt er Ref1 og Ref2 forskjellige navn på ett objekt, mens identifiserer er identifikatoren for samme objekt. Imidlertid kan identitet fortsatt kalles navnet på objektet, som betyr, identitet, ref1 og ref2 navn på samme sted.

Hovedforskjellen mellom en identifikator og en referanse er at når det sendes som et argument til en funksjon, hvis den sendes av identifikator, blir det gjort en kopi for identifikatoren i funksjonen, mens hvis den sendes med referanse, brukes samme sted i funksjon. Så å gå forbi identifikator ender opp med to steder, mens han passerer med referanse ender opp med det samme stedet.

Lvalue Reference og Rvalue Reference

Den normale måten å lage en referanse på er som følger:

int identitet;
identitet = 4;
int & ref = identitet;

Lagringen (ressursen) er lokalisert og identifisert først (med et navn som identitet), og deretter blir en referanse (med et navn som en ref) gjort. Når du går som et argument til en funksjon, vil en kopi av identifikatoren bli gjort i funksjonen, mens for en referanse vil den opprinnelige plasseringen bli brukt (referert til) i funksjonen.

I dag er det mulig å bare ha en referanse uten å identifisere den. Dette betyr at det er mulig å lage en referanse først uten å ha en identifikator for stedet. Dette bruker &&, som vist i følgende uttalelse:

int && ref = 4;

Her er det ingen foregående identifikasjon. For å få tilgang til verdien av objektet, bruker du bare Ref som du vil bruke identiteten ovenfor.

Med && erklæringen er det ingen mulighet for å gi et argument til en funksjon av identifikatoren. Det eneste valget er å passere ved referanse. I dette tilfellet er det bare ett sted som brukes i funksjonen og ikke det andre kopierte stedet som med en identifikator.

En referanse erklæring med og kalles lvalue referanse. En referanse erklæring med && kalles Rvalue Reference, som også er en Prvalue -referanse (se nedenfor).

Peker

Tenk på følgende kode:

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

Utgangen er 5.

Her er Ptdint en identifikator som identiteten ovenfor. Det er to objekter (steder) her i stedet for en: det spisse objektet, Ptdint identifisert av Ptdint, og Pointer -objektet, Ptrint identifisert av Ptrint. & ptdint returnerer adressen til det spisse objektet og setter den som verdien i pekeren ptrint -objektet. For å returnere (få) verdien av det spisse objektet, bruk identifikatoren for pekerobjektet, som i “*ptrint”.

Merk: Ptdint er en identifikator og ikke en referanse, mens navnet, Ref, nevnt tidligere, er en referanse.

Den andre og tredje linjen i koden ovenfor kan reduseres til en linje, noe som fører til følgende kode:

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

Merk: Når en peker øker, peker den på neste sted, som ikke er et tillegg av verdien 1. Når en peker er dekrementert, peker den på forrige sted, som ikke er en subtraksjon av verdien 1.

Gratis butikk

Et operativsystem tildeler minne for hvert program som kjører. Et minne som ikke er tildelt noe program er kjent som gratisbutikken. Uttrykket som returnerer et sted for et heltall fra gratisbutikken er:

Ny int

Dette returnerer et sted for et heltall som ikke er identifisert. Følgende kode illustrerer hvordan du bruker pekeren med gratisbutikken:

int *ptrint = ny int;
*ptrint = 12;
cout<< *ptrInt <<'\n';

Utgangen er 12.

For å ødelegge objektet, bruk slettingsuttrykket som følger:

slett ptrint;

Argumentet til slettingsuttrykket er en peker. Følgende kode illustrerer bruken:

int *ptrint = ny int;
*ptrint = 12;
slett ptrint;
cout<< *ptrInt <<'\n';

Utgangen er 0, og ikke noe som null eller udefinert. Slette erstatter verdien for plasseringen med standardverdien for den aktuelle typen plassering, og tillater deretter stedet for gjenbruk. Standardverdien for et INT -sted er 0.

Bruke en ressurs på nytt

I uttrykket taksonomi er gjenbruk av en ressurs det samme som gjenbruk av et sted eller lagring for et objekt. Følgende kode illustrerer hvordan et sted fra gratis butikk kan gjenbrukes:

int *ptrint = ny int;
*ptrint = 12;
cout<< *ptrInt <<'\n';
slett ptrint;
cout<< *ptrInt <<'\n';
*ptrint = 24;
cout<< *ptrInt <<'\n';

Utgangen er:

12
0
24

En verdi på 12 blir først tildelt det uidentifiserte stedet. Deretter blir innholdet i plasseringen slettet (i teorien blir objektet slettet). Verdien av 24 blir tildelt til samme sted.

Følgende program viser hvordan et heltallreferanse returnert av en funksjon gjenbrukes:

#inkludere
ved hjelp av navneområdet STD;
int & fn ()
int i = 5;
int & j = i;
return j;

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

Utgangen er:

5
17

Et objekt som jeg, erklært i et lokalt omfang (funksjonsomfang), slutter å eksistere på slutten av det lokale omfanget. Imidlertid returnerer funksjonen fn () ovenfor referansen til i. Gjennom denne returnerte referansen gjenbruker navnet, Myint in the Main () -funksjonen, stedet identifisert av I for verdien 17.

Lvalue

En lValue er et uttrykk hvis evaluering bestemmer identiteten til et objekt, bitfelt eller funksjon. Identiteten er en offisiell identitet som identitet ovenfor, eller et lValue referansenavn, en peker eller navnet på en funksjon. Tenk på følgende kode som fungerer:

int myint = 512;
int & myref = myint;
int* ptr = &myInt;
int fn ()
++PTR; --Ptr;
Returner Myint;

Her er Myint en lValue; Myref er et lValue -referanseuttrykk; *PTR er et lValue -uttrykk fordi resultatet er identifiserbart med PTR; ++ PTR eller -PTR er et LValue -uttrykk fordi resultatet kan identifiseres med den nye tilstanden (adressen) til PTR, og FN er en LValue (uttrykk).

Tenk på følgende kodesegment:

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

I den andre uttalelsen har stedet for 'A' 2 og kan identifiseres av 'A', og det samme er en lvalue. Plasseringen for B har 8 og kan identifiseres av B, og det samme er en lValue. Plasseringen for C vil ha summen, og kan identifiseres av C, og det samme er en lValue. I den andre uttalelsen er uttrykkene eller verdiene på 16 og 64 rvalues ​​(se nedenfor).

Tenk på følgende kodesegment:

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

Utgangen er 'v';

SEQ er en matrise. Plasseringen for 'V' eller lignende verdi i matrisen er identifisert av SEQ [i], der jeg er en indeks. Så uttrykket, seq [i], er et LValue -uttrykk. SEQ, som er identifikatoren for hele matrisen, er også en lValue.

Prvalue

En prvalue er et uttrykk hvis evaluering initialiserer et objekt eller et bitfelt eller beregner verdien av operanden til en operatør, som spesifisert i konteksten den vises.

I uttalelsen,

int myint = 256;

256 er et prvalue (prvalue -uttrykk) som initialiserer objektet identifisert av myint. Dette objektet er ikke referert til.

I uttalelsen,

int && ref = 4;

4 er et prvalue (prvalue -uttrykk) som initialiserer objektet referert av ref. Dette objektet identifiseres ikke offisielt. Ref er et eksempel på et RValue -referanseuttrykk eller Prvalue -referanseuttrykk; Det er et navn, men ikke en offisiell identifikator.

Tenk på følgende kodesegment:

int identitet;
identitet = 6;
int & ref = identitet;

6 er en prvalue som initialiserer objektet identifisert av identitet; Objektet er også referert til av ref. Her er dommeren en lValue -referanse og ikke en prValue -referanse.

Tenk på følgende kodesegment:

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

15 og 63 er hver en konstant som beregner for seg selv, og produserer en operand (i biter) for tilleggsoperatøren. Så 15 eller 63 er et prvalue -uttrykk.

Enhver bokstavelig, bortsett fra strengen bokstavelig, er en prvalue (i.e., et prvalue -uttrykk). Så en bokstavelig som 58 eller 58.53, eller sant eller usant, er en prvalue. En bokstavelig kan brukes til å initialisere et objekt eller vil beregne til seg selv (til en annen form i biter) som verdien av en operand for en operatør. I koden ovenfor initialiserer de bokstavelige 2 objektet, a. Det beregner seg også som en operand for oppdragsoperatøren.

Hvorfor er en streng bokstavelig ikke en prvalue? Tenk på følgende kode:

char str [] = "elsker ikke hat";
cout << str <<'\n';
cout << str[5] <<'\n';

Utgangen er:

Elsker ikke hat
n

STR identifiserer hele strengen. Så uttrykket, STR, og ikke hva det identifiserer seg, er en lValue. Hvert tegn i strengen kan identifiseres av Str [i], der jeg er en indeks. Uttrykket, str [5], og ikke karakteren den identifiserer, er en lvalue. Strengen bokstavelig er en lValue og ikke en prvalue.

I følgende uttalelse initialiserer en matrise bokstavelig objektet, ARR:

ptrint ++ eller ptrint--

Her er Ptrint en peker til et heltallsted. Hele uttrykket, og ikke den endelige verdien av stedet det peker på, er en prvalue (uttrykk). Dette er fordi uttrykket, ptrint ++ eller ptrint-, identifiserer den opprinnelige første verdien av plasseringen og ikke den andre endelige verdien av samme sted. På den annen side er -ptrint eller -ptrint en lValue fordi den identifiserer den eneste verdien av interessen for stedet. En annen måte å se på det er at den opprinnelige verdien beregner den andre endelige verdien.

I den andre uttalelsen av følgende kode kan a eller b fremdeles betraktes som en prvalue:

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

Så A eller B i den andre uttalelsen er en lValue fordi den identifiserer et objekt. Det er også en prvalue siden den beregner til heltallet til en operand for tilleggsoperatøren.

(ny int), og ikke stedet det oppretter er en prvalue. I følgende uttalelse tildeles retuensadressen til stedet til et pekerobjekt:

int *ptrint = ny int

Her er *ptrint en lValue, mens (ny int) er en prvalue. Husk at en lValue eller en prvalue er et uttrykk. (New Int) identifiserer ikke noe objekt. Å returnere adressen betyr ikke å identifisere objektet med et navn (for eksempel identitet, ovenfor). I *Ptrint er navnet, Ptrint, det som virkelig identifiserer objektet, så *Ptrint er en lValue. På den annen side er (New Int) en prvalue, ettersom det beregner et nytt sted til en adresse for operandverdi for tildelingsoperatøren =.

xvalue

I dag står Lvalue for stedsverdi; Prvalue står for “Pure” Rvalue (se hva Rvalue står for nedenfor). I dag står Xvalue for "utløp" Lvalue.

Definisjonen av xvalue, sitert fra C ++ -spesifikasjonen, er som følger:

“En xvalue er en glvalue som betegner et objekt eller bitfelt hvis ressurser kan gjenbrukes (vanligvis fordi det er nær slutten av livet). [Eksempel: Visse typer uttrykk som involverer Rvalue-referanser gir xValues, for eksempel en samtale til en funksjon hvis returtype er en Rvalue-referanse eller en rollebesetning til et Rvalue Reference Type-slutteksempel] ”

Hva dette betyr er at både LValue og Prvalue kan utløpe. Følgende kode (kopiert ovenfra) viser hvordan lagringen (ressursen) til LValue, *ptrint blir brukt på nytt etter at den er blitt slettet.

int *ptrint = ny int;
*ptrint = 12;
cout<< *ptrInt <<'\n';
slett ptrint;
cout<< *ptrInt <<'\n';
*ptrint = 24;
cout<< *ptrInt <<'\n';

Utgangen er:

12
0
24

Følgende program (kopiert ovenfra) viser hvordan lagring av en heltallreferanse, som er en lValue -referanse returnert av en funksjon, blir gjenbrukt i Main () -funksjonen:

#inkludere
ved hjelp av navneområdet STD;
int & fn ()
int i = 5;
int & j = i;
return j;

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

Utgangen er:

5
17

Når et objekt som jeg i FN () -funksjonen går ut av omfang, blir det naturlig ødelagt. I dette tilfellet har lagring av I fortsatt blitt gjenbrukt i hovedfunksjonen ().

Ovennevnte to kodeprøver illustrerer gjenbruk av lagring av lValues. Det er mulig å ha en lagring av re-bruk av prvalues ​​(rvalues) (se senere).

Følgende sitat angående XValue er fra C ++ -spesifikasjonen:

Generelt sett er effekten av denne regelen at navngitte Rvalue -referanser blir behandlet som lValues ​​og ikke navngitte Rvalue -referanser til objekter blir behandlet som XValues. Rvalue referanser til funksjoner blir behandlet som lValues ​​enten de er navngitt eller ikke." (ser senere).

Så en xvalue er en lValue eller en prvalue hvis ressurser (lagring) kan gjenbrukes. XValues ​​er krysset med lValues ​​og prvalues.

Det er mer til XValue enn det som har blitt adressert i denne artikkelen. Imidlertid fortjener Xvalue en hel artikkel på egen hånd, og derfor blir ikke de ekstra spesifikasjonene for Xvalue adressert i denne artikkelen.

Ekspresjonskategori Taksonomisett

Et annet sitat fra C ++ -spesifikasjonen:

Merk: Historisk sett ble lValues ​​og rvalues ​​såkalt fordi de kunne vises på venstre og høyre side av et oppdrag (selv om dette ikke lenger er generelt sant); Glvalues ​​er "generaliserte" lvalues, prvalues ​​er "rene" rvalues, og xvalues ​​"utløper" lvalues. Til tross for navnene deres, klassifiserer disse begrepene uttrykk, ikke verdier. - Sluttnotat ”

Så, glvalues ​​er unionssettet med lValues ​​og xvalues ​​og rvalues ​​er unionssettet med xvalues ​​og prvalues. XValues ​​er krysset med lValues ​​og prvalues.

Per nå illustreres uttrykkskategori -taksonomien bedre med et Venn -diagram som følger:

Konklusjon

En lValue er et uttrykk hvis evaluering bestemmer identiteten til et objekt, bitfelt eller funksjon.

En prvalue er et uttrykk hvis evaluering initialiserer et objekt eller et bitfelt eller beregner verdien av operanden til en operatør, som spesifisert i konteksten den vises.

En xvalue er en lValue eller en prvalue, med den ekstra eiendommen som ressursene (lagring) kan gjenbrukes.

C ++ -spesifikasjonen illustrerer taksonomi for ekspresjonskategori med et trediagram, noe som indikerer at det er noe hierarki i taksonomien. Per nå er det ikke noe hierarki i taksonomien, så et Venn -diagram brukes av noen forfattere, da det illustrerer taksonomien bedre enn trediagrammet.