C ++

Uttryckskategoritaxonomi i C ++

Uttryckskategoritaxonomi i C ++

En beräkning är alla typer av beräkningar som följer en väldefinierad algoritm. Ett uttryck är en sekvens av operatorer och operander som specificerar en beräkning. Med andra ord är ett uttryck en identifierare eller en bokstav, eller en sekvens av båda, förenade av operatörer.Vid programmering kan ett uttryck resultera i ett värde och / eller orsaka att något händer. När det resulterar i ett värde är uttrycket en glvalue, rvalue, lvalue, xvalue eller prvalue. Var och en av dessa kategorier är en uppsättning uttryck. Varje uppsättning har en definition och särskilda situationer där dess innebörd råder, vilket skiljer den från en annan uppsättning. Varje uppsättning kallas en värdekategori.

Notera: Ett värde eller bokstavligt är fortfarande ett uttryck, så dessa termer klassificerar uttryck och inte riktigt värden.

glvalue och rvalue är de två delmängderna från det stora uppsättningsuttrycket. glvalue finns i ytterligare två underuppsättningar: lvalue och xvalue. rvalue, den andra delmängden för uttryck, finns också i ytterligare två underuppsättningar: xvalue och prvalue. Så, xvalue är en delmängd av både glvalue och rvalue: det vill säga xvalue är skärningspunkten för både glvalue och rvalue. Följande taxonomidiagram, hämtat från C ++ - specifikationen, illustrerar förhållandet mellan alla uppsättningar:

prvalue, xvalue och lvalue är de primära kategorivärdena. glvalue är föreningen av lvalues ​​och xvalues, medan rvalues ​​är unionen av xvalues ​​och prvalues.

Du behöver grundläggande kunskaper i C ++ för att förstå den här artikeln; du behöver också kunskap om omfattning i C++.

Artikelinnehåll

Grunderna

För att verkligen förstå uttryckskategoritaxonomin måste du först komma ihåg eller känna till följande grundläggande funktioner: plats och objekt, lagring och resurs, initialisering, identifierare och referens, lvalue och rvalue-referenser, pekare, gratis butik och återanvändning av en resurs.

Plats och objekt

Tänk på följande förklaring:

int ident;

Detta är en deklaration som identifierar en plats i minnet. En plats är en viss uppsättning på varandra följande byte i minnet. En plats kan bestå av en byte, två byte, fyra byte, sextiofyra byte, etc. Platsen för ett heltal för en 32-bitars maskin är fyra byte. Platsen kan också identifieras med en identifierare.

I ovanstående deklaration har platsen inget innehåll. Det betyder att det inte har något värde, eftersom innehållet är värdet. Så identifierar en identifierare en plats (litet kontinuerligt utrymme). När platsen ges ett visst innehåll identifierar identifieraren sedan både platsen och innehållet; det vill säga identifieraren identifierar sedan både platsen och värdet.

Tänk på följande påståenden:

int ident1 = 5;
int ident2 = 100;

Var och en av dessa uttalanden är en deklaration och en definition. Den första identifieraren har värdet (innehåll) 5 och den andra identifieraren har värdet 100. I en 32-bitars maskin är var och en av dessa platser fyra byte lång. Den första identifieraren identifierar både en plats och ett värde. Den andra identifieraren identifierar också båda.

Ett objekt är en namngiven lagringsregion i minnet. Så ett objekt är antingen en plats utan ett värde eller en plats med ett värde.

Objektlagring och resurs

Platsen för ett objekt kallas också objektets lagring eller resurs.

Initiering

Tänk på följande kodsegment:

int ident;
ident = 8;

Den första raden förklarar en identifierare. Denna deklaration ger en plats (lagring eller resurs) för ett heltalobjekt som identifierar det med namnet, ident. Nästa rad sätter värdet 8 (i bitar) till den plats som identifieras av ident. Att sätta detta värde är initialisering.

Följande uttalande definierar en vektor med innehåll, 1, 2, 3, 4, 5, identifierad av vtr:

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

Här görs initialiseringen med 1, 2, 3, 4, 5 i samma uttalande av definitionen (deklarationen). Tilldelningsoperatören används inte. Följande uttalande definierar en matris med innehåll 1, 2, 3, 4, 5:

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

Den här gången har en uppdragsoperatör använts för initialiseringen.

Identifierare och referens

Tänk på följande kodsegment:

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

Utgången är:

4 4 4

ident är en identifierare, medan ref1 och ref2 är referenser; de refererar till samma plats. En referens är en synonym till en identifierare. Konventionellt är ref1 och ref2 olika namn på ett objekt, medan ident är identifikatorn för samma objekt. Emellertid kan ident fortfarande kallas objektets namn, vilket betyder, ident, ref1 och ref2 namn samma plats.

Huvudskillnaden mellan en identifierare och en referens är att när den skickas som ett argument till en funktion, om den skickas av en identifierare, görs en kopia för identifieraren i funktionen, medan den skickas genom referens används samma plats fungera. Så att passera genom identifierare hamnar på två platser, medan passera genom referens hamnar på samma plats.

lvalue Reference och rvalue Reference

Det normala sättet att skapa en referens är som följer:

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

Lagringen (resurs) lokaliseras och identifieras först (med ett namn som ident), och sedan görs en referens (med ett namn som en ref). När du överför som ett argument till en funktion kommer en kopia av identifieraren att göras i funktionen, medan för en referens kommer den ursprungliga platsen att användas (hänvisas till) i funktionen.

Idag är det möjligt att bara ha en referens utan att identifiera den. Detta innebär att det är möjligt att skapa en referens först utan att ha en identifierare för platsen. Detta använder &&, som visas i följande uttalande:

int && ref = 4;

Här finns ingen föregående identifiering. För att komma åt objektets värde, använd bara ref som du skulle använda identen ovan.

Med && deklarationen finns det ingen möjlighet att skicka ett argument till en funktion med identifierare. Det enda valet är att passera genom referens. I det här fallet används bara en plats inom funktionen och inte den andra kopierade platsen som med en identifierare.

En referensdeklaration med & kallas lvalue reference. En referensdeklaration med && kallas rvalue reference, som också är en prvalue reference (se nedan).

Pekare

Tänk på följande kod:

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

Utgången är 5.

Här är ptdInt en identifierare som identen ovan. Det finns två objekt (platser) här istället för ett: det spetsiga objektet, ptdInt identifierat av ptdInt, och pekarobjektet, ptrInt identifierat av ptrInt. & ptdInt returnerar adressen till det spetsiga objektet och lägger det som värdet i pekaren ptrInt-objekt. För att returnera (erhålla) värdet på det spetsiga objektet, använd identifieraren för pekarobjektet, som i “* ptrInt”.

Notera: ptdInt är en identifierare och inte en referens, medan namnet, ref, som nämnts tidigare, är en referens.

Den andra och tredje raden i ovanstående kod kan reduceras till en rad, vilket leder till följande kod:

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

Notera: När en pekare ökas pekar den till nästa plats, vilket inte är ett tillägg av värdet 1. När en pekare minskas pekar den på föregående plats, vilket inte är en subtraktion av värdet 1.

Gratis butik

Ett operativsystem tilldelar minne för varje program som körs. Ett minne som inte tilldelas något program kallas gratisbutik. Uttrycket som returnerar en plats för ett heltal från gratisbutiken är:

ny int

Detta returnerar en plats för ett heltal som inte identifieras. Följande kod illustrerar hur man använder pekaren med gratisbutiken:

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

Utgången är 12.

För att förstöra objektet, använd raderingsuttrycket enligt följande:

ta bort ptrInt;

Argumentet för borttagningsuttrycket är en pekare. Följande kod illustrerar dess användning:

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

Utgången är 0, och inte något som null eller odefinierat. delete ersätter värdet för platsen med standardvärdet för den specifika typen av plats och tillåter sedan platsen för återanvändning. Standardvärdet för en int-plats är 0.

Återanvända en resurs

I uttryckskategoritaxonomi är återanvändning av en resurs detsamma som att återanvända en plats eller lagring för ett objekt. Följande kod illustrerar hur en plats från gratisbutiken kan återanvändas:

int * ptrInt = ny int;
* ptrInt = 12;
cout<< *ptrInt <<'\n';
ta bort ptrInt;
cout<< *ptrInt <<'\n';
* ptrInt = 24;
cout<< *ptrInt <<'\n';

Utgången är:

12
0
24

Ett värde på 12 tilldelas först den oidentifierade platsen. Sedan raderas platsens innehåll (i teorin raderas objektet). Värdet 24 tilldelas till samma plats.

Följande program visar hur en heltalsreferens som returneras av en funktion återanvänds:

#omfatta
använder namnrymd std;
int & fn ()

int i = 5;
int & j = i;
returnera j;

int main ()

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

Utgången är:

5
17

Ett objekt som i, deklarerat i ett lokalt omfång (funktionsomfång) upphör att existera i slutet av det lokala omfånget. Funktionen fn () ovan returnerar emellertid referensen till i. Genom denna returnerade referens återanvänder namnet, myInt i huvudfunktionen (), den plats som identifierats av i för värdet 17.

värde

En lvalue är ett uttryck vars utvärdering avgör identiteten för ett objekt, bitfält eller funktion. Identiteten är en officiell identitet som ident ovan, eller ett referensnamn för värden, en pekare eller namnet på en funktion. Tänk på följande kod som fungerar:

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

++ptr; --ptr;
returnera minInt;

Här är myInt en lvalue; myRef är ett referensuttryck för lvalue; * ptr är ett lvalue-uttryck eftersom resultatet kan identifieras med ptr; ++ ptr eller -ptr är ett lvalue-uttryck eftersom dess resultat kan identifieras med det nya tillståndet (adressen) för ptr, och fn är ett lvalue (expression).

Tänk på följande kodsegment:

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

I det andra uttalandet har platsen för 'a' 2 och kan identifieras med 'a', och det är också ett värde. Platsen för b har 8 och kan identifieras av b, och det är också ett värde. Platsen för c kommer att ha summan och kan identifieras av c, och det är också ett värde. I det andra uttalandet är uttrycken eller värdena 16 och 64 värden (se nedan).

Tänk på följande kodsegment:

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

Utgången är 'v';

seq är en matris. Platsen för 'v' eller något liknande värde i matrisen identifieras av seq [i], där i är ett index. Så uttrycket, seq [i], är ett värde på ett värde. seq, som är identifieraren för hela arrayen, är också en lvalue.

värdering

En prvalue är ett uttryck vars utvärdering initialiserar ett objekt eller ett bitfält eller beräknar värdet på operandens operand, som specificeras av det sammanhang där det visas.

I uttalandet,

int myInt = 256;

256 är en prvalue (prvalue expression) som initialiserar objektet som identifierats av myInt. Det refereras inte till detta objekt.

I uttalandet,

int && ref = 4;

4 är en prvalue (prvalue expression) som initialiserar objektet som refereras till av ref. Detta objekt identifieras inte officiellt. ref är ett exempel på ett rvärde referensuttryck eller förvärde referensuttryck; det är ett namn men inte en officiell identifierare.

Tänk på följande kodsegment:

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

6 är en förvärde som initialiserar objektet identifierat med ident; objektet hänvisas också till med ref. Här är ref en referens för lvalue och inte en referens för prvalue.

Tänk på följande kodsegment:

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

15 och 63 är var och en en konstant som beräknar för sig själv och producerar en operand (i bitar) för additionsoperatören. Så, 15 eller 63 är ett prvalue-uttryck.

Alla bokstavliga, utom stränglitterära, är en förvärde (i.e., ett prvalue-uttryck). Så, en bokstav som 58 eller 58.53, eller sant eller falskt, är ett värde. En bokstav kan användas för att initialisera ett objekt eller skulle beräkna till sig själv (till någon annan form i bitar) som värdet på en operand för en operatör. I ovanstående kod initialiserar bokstav 2 objektet, a. Det beräknar sig också som en operand för uppdragsoperatören.

Varför är en sträng bokstavlig inte en prvalue? Tänk på följande kod:

char str [] = "älskar inte hatar";
cout << str <<'\n';
cout << str[5] <<'\n';

Utgången är:

älskar inte hatar
n

str identifierar hela strängen. Så uttrycket, str, och inte vad det identifierar, är ett värde. Varje tecken i strängen kan identifieras med str [i], där i är ett index. Uttrycket str [5], och inte det tecken som det identifierar, är ett värde. Strängen bokstavlig är en lvalue och inte en prvalue.

I följande uttalande initialiserar en matris bokstavligen objektet, arr:

ptrInt ++ eller ptrInt-- 

Här är ptrInt en pekare till ett heltal. Hela uttrycket, och inte det slutliga värdet på den plats det pekar på, är ett värde (uttryck). Detta beror på att uttrycket, ptrInt ++ eller ptrInt-, identifierar det ursprungliga första värdet för sin plats och inte det andra slutvärdet för samma plats. Å andra sidan är -ptrInt eller -ptrInt en lvalue eftersom den identifierar det enda värdet på intresset på platsen. Ett annat sätt att titta på det är att det ursprungliga värdet beräknar det andra slutvärdet.

I det andra uttalandet av följande kod kan a eller b fortfarande betraktas som en prvalue:

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

Så, a eller b i det andra uttalandet är ett värde eftersom det identifierar ett objekt. Det är också en förvärde eftersom den beräknar till heltalet för en operand för tilläggsoperatören.

(ny int), och inte den plats som den fastställer är ett värde. I följande uttalande tilldelas platsens returadress till ett pekarobjekt:

int * ptrInt = ny int

Här är * ptrInt en lvalue, medan (new int) är en prvalue. Kom ihåg att en lvalue eller en prvalue är ett uttryck. (new int) identifierar inget objekt. Att returnera adressen betyder inte att identifiera objektet med ett namn (som ident ovan). I * ptrInt är namnet ptrInt det som verkligen identifierar objektet, så * ptrInt är en lvalue. Å andra sidan är (new int) en förvärde, eftersom den beräknar en ny plats till en adress med operandvärde för tilldelningsoperatören =.

xvärde

Idag står lvalue för Location Value; prvalue står för "ren" rvalue (se vad rvalue står för nedan). Idag står xvalue för “eXpiring” -värde.

Definitionen av xvalue, citerad från C ++ specifikationen, är som följer:

”En xvalue är ett glvalue som betecknar ett objekt eller bitfält vars resurser kan återanvändas (vanligtvis för att det är nära slutet av sin livstid). [Exempel: Vissa typer av uttryck som involverar rvalue-referenser ger x-värden, till exempel ett anrop till en funktion vars returtyp är en rvalue-referens eller en cast till ett r-value-referensexempel] "

Vad detta betyder är att både lvalue och prvalue kan upphöra. Följande kod (kopierad ovanifrån) visar hur lagring (resurs) för lvalue, * ptrInt återanvänds efter att den har raderats.

int * ptrInt = ny int;
* ptrInt = 12;
cout<< *ptrInt <<'\n';
ta bort ptrInt;
cout<< *ptrInt <<'\n';
* ptrInt = 24;
cout<< *ptrInt <<'\n';

Utgången är:

12
0
24

Följande program (kopierat ovanifrån) visar hur lagring av en helreferens, som är en referens för värdet som returneras av en funktion, återanvänds i huvudfunktionen ():

#omfatta
använder namnrymd std;
int & fn ()

int i = 5;
int & j = i;
returnera j;

int main ()

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

Utgången är:

5
17

När ett objekt som i i fn () -funktionen går utanför räckvidden förstörs det naturligtvis. I det här fallet har lagringen av i fortfarande använts i huvudfunktionen ().

Ovanstående två kodprover illustrerar återanvändningen av lagring av värden. Det är möjligt att ha en lagringsåteranvändning av värden (rvalues) (se senare).

Följande citat angående xvalue kommer från C ++ specifikationen:

”I allmänhet är effekten av denna regel att namngivna rvärdehänvisningar behandlas som lvalues ​​och unnamnade rvalue referenser till objekt behandlas som xvalues. rvärdehänvisningar till funktioner behandlas som värden oavsett om de är namngivna eller inte." (ses senare).

Så, en xvalue är en lvalue eller en prvalue vars resurser (lagring) kan återanvändas. xvalues ​​är skärningspunkten mellan lvalues ​​och prvalues.

Det finns mer att xvärdera än vad som har behandlats i den här artikeln. Men xvalue förtjänar en hel artikel på egen hand, så de extra specifikationerna för xvalue behandlas inte i den här artikeln.

Uttryckskategori Taxonomi Set

Ett annat citat från C ++ -specifikationen:

Notera: Historiskt sett var värden och värden så kallade eftersom de kunde visas på vänster och höger sida av ett uppdrag (även om det inte längre är sant). glvalues ​​är "generaliserade" värden, värden är "rena" värden och xvalues ​​är "eXpiring" -värden. Trots deras namn klassificerar dessa termer uttryck, inte värden. - slutnot ”

Så, glvalues ​​är fackuppsättningen av lvalues ​​och xvalues ​​och rvalues ​​är fackuppsättningen av xvalues ​​och prvalues. xvalues ​​är skärningspunkten mellan lvalues ​​och prvalues.

Från och med nu illustreras uttryckskategoritaxonomin bättre med ett Venn-diagram enligt följande:

Slutsats

En lvalue är ett uttryck vars utvärdering avgör identiteten för ett objekt, bitfält eller funktion.

En prvalue är ett uttryck vars utvärdering initialiserar ett objekt eller ett bitfält eller beräknar värdet på operandens operand, enligt vad som anges i det sammanhang där det visas.

En xvalue är en lvalue eller en prvalue, med den ytterligare egenskapen att dess resurser (lagring) kan återanvändas.

C ++ - specifikationen illustrerar taxonomi för uttryckskategori med ett träddiagram, vilket indikerar att det finns en viss hierarki i taxonomin. Från och med nu finns det ingen hierarki i taxonomin, så ett Venn-diagram används av vissa författare, eftersom det illustrerar taxonomin bättre än träddiagrammet.

Hur man visar OSD-överlägg i helskärms Linux-appar och -spel
Att spela helskärmsspel eller använda appar i distraktionsfritt helskärmsläge kan avskärma dig från relevant systeminformation som syns i en panel ell...
Topp 5 spelinspelningskort
Vi har alla sett och älskat streaming av spel på YouTube. PewDiePie, Jakesepticye och Markiplier är bara några av de bästa spelarna som har tjänat mil...
Hur man utvecklar ett spel på Linux
För ett decennium sedan skulle inte många Linux-användare förutsäga att deras favoritoperativsystem en dag skulle vara en populär spelplattform för ko...