C ++

Kifejezési kategória taxonómiája C ++ nyelven

Kifejezési kategória taxonómiája C ++ nyelven

A számítás bármilyen típusú számítás, amely jól definiált algoritmust követ. A kifejezés operátorok és operandusok sorozata, amely meghatározza a számítást. Más szavakkal, egy kifejezés azonosító vagy literál, vagy mindkettő sorozata, amelyet operátorok kötnek össze.A programozás során egy kifejezés eredményezhet értéket és / vagy okozhat valamilyen eseményt. Amikor értéket eredményez, a kifejezés egy glvalue, rvalue, lvalue, xvalue vagy prvalue. Ezen kategóriák mindegyike kifejezések halmaza. Minden halmaznak van meghatározása és sajátos helyzetei, ahol jelentése érvényesül, megkülönböztetve egy másik halmaztól. Minden halmazt értékkategóriának hívunk.

jegyzet: Az érték vagy a szó szerinti szó továbbra is kifejezés, tehát ezek a kifejezések osztályozzák a kifejezéseket, és nem igazán értékeket.

A glvalue és az rvalue a két halmaz a nagy halmaz kifejezésből. A glvalue két további részhalmazban létezik: lvalue és xvalue. Az rvalue, a kifejezés másik részhalmaza, két további részhalmazban is létezik: xvalue és prvalue. Tehát az xvalue mind a glvalue, mind az rvalue részhalmaza: vagyis az xvalue mind a glvalue, mind az rvalue metszéspontja. A következő taxonómiai diagram, amely a C ++ specifikációból származik, szemlélteti az összes halmaz kapcsolatát:

az prvalue, az xvalue és az lvalue az elsődleges kategóriaértékek. A glvalue az értékek és az xvalu, míg az rvalues ​​az xértékek és az prvalues ​​egyesülése.

A cikk megértéséhez alapismeretekre van szüksége C ++ nyelven; szükség van a C hatókörének ismeretére is++.

A cikk tartalma

Alapok

Ahhoz, hogy megérthesse a kifejezéskategória taxonómiáját, először fel kell idéznie vagy ismernie kell a következő alapvető jellemzőket: hely és objektum, tárhely és erőforrás, inicializálás, azonosító és hivatkozás, lvalue és rvalue hivatkozások, mutató, free store és egy forrás.

Hely és objektum

Vegye figyelembe a következő nyilatkozatot:

int ident;

Ez egy olyan nyilatkozat, amely azonosítja a memóriában lévő helyet. A hely az egymást követő bájtok meghatározott halmaza a memóriában. A hely állhat egy bájtból, két bájtból, négy bájtból, hatvannégy bájtból stb. Egy 32 bites gép egész számának helye négy bájt. Ezenkívül a helyet azonosítóval lehet azonosítani.

A fenti nyilatkozatban a helynek nincs tartalma. Ez azt jelenti, hogy nincs értéke, mivel a tartalom az érték. Tehát egy azonosító azonosítja a helyet (kis folytonos tér). Amikor a hely adott tartalmat kap, akkor az azonosító azonosítja mind a helyet, mind a tartalmat; vagyis az azonosító ezután azonosítja a helyet és az értéket is.

Vegye figyelembe a következő állításokat:

int azonos = 5;
int azonos = 100;

Ezen állítások mindegyike deklaráció és meghatározás. Az első azonosító értéke (tartalma) 5, a második azonosító értéke 100. Egy 32 bites gépben ezek a helyek mindegyike négy bájt hosszú. Az első azonosító mind a helyet, mind az értéket azonosítja. A második azonosító mindkettőt azonosítja.

Az objektum a memória elnevezett tárterülete. Tehát egy objektum vagy érték nélküli hely, vagy értékkel rendelkező hely.

Objektumtárolás és erőforrás

Az objektum helyét az objektum tárolójának vagy erőforrásának is nevezik.

Inicializálás

Vegye figyelembe a következő kódszegmenst:

int ident;
azonos = 8;

Az első sor azonosítót deklarál. Ez a nyilatkozat megad egy helyet (tárhelyet vagy erőforrást) egy egész objektum számára, azonosítva a névvel, azonosítóval. A következő sor a 8 értéket (bitben) az ident által azonosított helyre helyezi. Ennek az értéknek az elhelyezése az inicializálás.

A következő utasítás meghatároz egy vtr-t, amelynek tartalma 1, 2, 3, 4, 5, amelyet vtr azonosít:

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

Itt az 1, 2, 3, 4, 5 karakterrel történő inicializálás a definíció (deklaráció) ugyanazon utasításában történik. A hozzárendelés operátort nem használják. A következő utasítás egy tömböt határoz meg, amelynek tartalma 1, 2, 3, 4, 5:

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

Ezúttal egy hozzárendelési operátort használtak az inicializáláshoz.

Azonosító és hivatkozás

Vegye figyelembe a következő kódszegmenst:

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

A kimenet:

4 4 4

ident azonosító, míg a ref1 és ref2 referencia; ugyanarra a helyre hivatkoznak. A hivatkozás az azonosító szinonimája. Hagyományosan a ref1 és a ref2 egy objektum különböző neve, míg az ident ugyanazon objektum azonosítója. Az ident azonban továbbra is nevezhető az objektum nevének, ami azt jelenti, hogy ident, ref1 és ref2 megnevezi ugyanazt a helyet.

A fő különbség az azonosító és a hivatkozás között az, hogy ha argumentumként átadják egy függvénynek, ha azonosítóval továbbítja, akkor másolatot készítenek a függvény azonosítójáról, míg ha hivatkozással adják át, ugyanazt a helyet használják funkció. Tehát az azonosítóval való áthaladás két helyre vezet, míg a hivatkozás útján ugyanaz az egy helyre kerül.

lvalue Reference és rvalue Reference

A referencia létrehozásának szokásos módja a következő:

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

A tárolót (erőforrást) először megtalálja és azonosítja (névvel, például azonosítóval), majd hivatkozást (névvel, például hivatkozással) készít. Ha argumentumként átad egy függvénynek, akkor az azonosító másolata készül a függvényben, míg referencia esetén az eredeti helyet fogja használni (hivatkozva) a függvényben.

Ma már lehetséges, hogy csak referencia van, anélkül, hogy azonosítanánk. Ez azt jelenti, hogy először referenciát lehet létrehozni anélkül, hogy azonosítanánk a helyet. Ez a && kifejezést használja, amint azt a következő állítás mutatja:

int && ref = 4;

Itt nincs megelőző azonosítás. Az objektum értékének eléréséhez egyszerűen használja a ref parancsot, ahogy a fenti azonosítót használná.

A && deklarációval nem lehet argumentumot átadni egy függvénynek azonosítón keresztül. Az egyetlen választás az, hogy referenciával adjuk át. Ebben az esetben csak egy helyet használunk a függvényen belül, és nem a második másolt helyet, mint egy azonosítóval.

A & jelet tartalmazó referencia deklarációt lvalue referenciának hívják. A && nevű referencia deklarációt rvalue referenciának hívják, amely egyben prvalue referencia is (lásd alább).

Mutató

Vegye figyelembe a következő kódot:

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

A kimenet az 5.

Itt a ptdInt olyan azonosító, mint a fenti. Két objektum (hely) van itt egy helyett: a hegyes objektum, a ptdInt által azonosított ptdInt és a ptrInt által azonosított ptrInt mutató objektum. A & ptdInt visszaadja a hegyes objektum címét, és értékként adja a mutató ptrInt objektumba. A hegyes objektum értékének visszaadásához (megszerzéséhez) használja a mutatóobjektum azonosítóját, a “* ptrInt” részben leírtak szerint.

jegyzet: a ptdInt azonosító és nem hivatkozás, míg a korábban említett ref hivatkozás referencia.

A fenti kód második és harmadik sora egy sorra csökkenthető, ami a következő kódhoz vezet:

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

jegyzet: Ha a mutató növekszik, akkor a következő helyre mutat, amely nem az 1 érték hozzáadása. Amikor egy mutatót csökkentenek, az az előző helyre mutat, ami nem az 1. érték kivonása.

Ingyenes áruház

Az operációs rendszer minden futó programhoz memóriát rendel. Olyan memóriát, amelyet egyetlen programhoz sem rendelnek hozzá, ingyenes tárolónak nevezzük. Az a kifejezés, amely egy egész szám helyét adja vissza az ingyenes boltból:

új int

Ez egy nem azonosított egész szám helyét adja vissza. A következő kód bemutatja, hogyan kell használni a mutatót az ingyenes tárolóval:

int * ptrInt = új int;
* ptrInt = 12;
cout<< *ptrInt  <<'\n';

A kimenet az 12.

Az objektum megsemmisítéséhez használja a törlés kifejezést az alábbiak szerint:

ptrInt törlése;

A törlés kifejezés argumentuma mutató. A következő kód szemlélteti használatát:

int * ptrInt = új int;
* ptrInt = 12;
ptrInt törlése;
cout<< *ptrInt <<'\n';

A kimenet az 0, és semmihez semminek vagy definiálatlannak. A delete a hely értékét lecseréli a hely adott típusának alapértelmezett értékére, majd lehetővé teszi a hely újrafelhasználását. Az int hely alapértelmezett értéke 0.

Erőforrás újrafelhasználása

A kifejezéskategória taxonómiájában az erőforrás újrafelhasználása megegyezik egy hely vagy tároló újrafelhasználásával egy objektum számára. A következő kód bemutatja, hogy az ingyenes boltból származó hely hogyan használható fel újra:

int * ptrInt = új int;
* ptrInt = 12;
cout<< *ptrInt <<'\n';
ptrInt törlése;
cout<< *ptrInt <<'\n';
* ptrInt = 24;
cout<< *ptrInt <<'\n';

A kimenet:

12
0
24

Először 12-es értéket rendelünk az azonosítatlan helyre. Ezután a hely tartalma törlődik (elméletileg az objektum törlődik). A 24 értéket ugyanahhoz a helyhez rendeljük újra.

A következő program bemutatja, hogy egy függvény által adott egész hivatkozást hogyan használják fel újra:

#include
névtér használata std;
int & fn ()

int i = 5;
int & j = i;
visszatér j;

int main ()

int & myInt = fn ();
cout<< myInt <<'\n';
myInt = 17;
cout<< myInt <<'\n';
visszatér 0;

A kimenet:

5
17

Az olyan objektumok, mint az i, helyi hatókörben (funkció hatókörben) deklarálva, a helyi hatókör végén megszűnnek. A fenti fn () függvény azonban visszaadja az i hivatkozását. Ezen a visszaküldött hivatkozáson keresztül a myInt név a main () függvényben újra felhasználja az i által azonosított helyet a 17 értéknél.

érték

Az lvalue olyan kifejezés, amelynek értékelése meghatározza egy objektum, bitmező vagy függvény azonosságát. Az identitás egy hivatalos identitás, például a fenti azonosító, vagy egy értékérték hivatkozási név, mutató vagy egy függvény neve. Vegye figyelembe a következő működő kódot:

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

++ptr; --ptr;
return myInt;

Itt a myInt egy érték; a myRef egy érték referencia kifejezés; * a ptr egy értékérték, mert az eredménye azonosítható a ptr-vel; A ++ ptr vagy -ptr egy lvalue kifejezés, mert eredménye azonosítható a ptr új állapotával (címével), az fn pedig lvalue (kifejezés).

Vegye figyelembe a következő kódszegmenst:

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

A második utasításban az „a” helyének 2-e van, és az „a” -val azonosítható, és így egy érték is. A b helyének 8-a van, és b alapján azonosítható, így egy érték is. A c helyének meg lesz az összege, és c alapján azonosítható, így egy érték is. A második állításban a 16 és 64 kifejezések vagy értékek értékek (lásd alább).

Vegye figyelembe a következő kódszegmenst:

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

A kimenet 'v";

seq egy tömb. A 'v' vagy bármely hasonló érték helyét a tömbben az [i] szekvencia azonosítja, ahol i index. Tehát a seq [i] kifejezés értékértékű kifejezés. A seq, amely az egész tömb azonosítója, szintén érték.

prvalue

A prvalue olyan kifejezés, amelynek kiértékelése inicializál egy objektumot vagy egy bitmezőt, vagy kiszámítja az operátor operandusának értékét, amint azt a kontextus meghatározza, amelyben megjelenik.

A nyilatkozatban,

int myInt = 256;

A 256 egy prvalue (prvalue kifejezés), amely inicializálja a myInt által azonosított objektumot. Erre az objektumra nincs hivatkozás.

A nyilatkozatban,

int && ref = 4;

A 4. egy prvalue (prvalue kifejezés), amely inicializálja a hivatkozással hivatkozott objektumot. Ez az objektum nincs hivatalosan azonosítva. a ref egy példa egy rvalue referencia kifejezésre vagy prvalue referencia kifejezésre; ez egy név, de nem hivatalos azonosító.

Vegye figyelembe a következő kódszegmenst:

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

A 6. egy olyan érték, amely inicializálja az azonosítással azonosított objektumot; az objektumra hivatkozik a ref. Itt a ref egy lvalue referencia, és nem prvalue referencia.

Vegye figyelembe a következő kódszegmenst:

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

A 15. és a 63. szám egy állandó, amely kiszámítja önmagát, és operátumot (bitben) állít elő az összeadás operátor számára. Tehát a 15 vagy a 63 egy prvalue kifejezés.

Bármely literál, kivéve a literál karakterláncot, prvalue (azaz.e., prvalue kifejezés). Tehát egy szó szerinti szó, például 58 vagy 58.53, vagy igaz vagy hamis, prvalue. A literál használható egy objektum inicializálására, vagy kiszámítaná önmagát (más formában, bitben) operandus értékeként egy operátor számára. A fenti kódban a literál 2 inicializálja az objektumot, a. Szintén operandusnak számít a hozzárendelési operátor számára.

Miért nem egy karakterlánc egy prvalue?? Vegye figyelembe a következő kódot:

char str [] = "a szeretet ne gyűlölje";
cout << str <<'\n';
cout << str[5] <<'\n';

A kimenet:

szeretni, nem utálni
n

Az str az egész karakterláncot azonosítja. Tehát az str kifejezés és nem az, amit azonosít, az érték. A karaktersorozat minden karakterét str [i] -vel lehet azonosítani, ahol i index. Az str [5] kifejezés, és nem az általa azonosított karakter, értéke érték. A literál karakterlánc egy érték, és nem egy érték.

A következő utasításban egy tömb szó szerint inicializálja az arr objektumot:

ptrInt ++ vagy ptrInt-- 

Itt a ptrInt egy egész helyre mutató mutató. A teljes kifejezés, és nem a hely végső értéke, amelyre mutat, prvalue (kifejezés). Ez azért van, mert a ptrInt ++ vagy ptrInt- kifejezés a helyének eredeti első értékét, nem pedig ugyanazon hely második végső értékét azonosítja. Másrészt a -ptrInt vagy -ptrInt egy érték, mert azonosítja a hely iránti érdeklődés egyetlen értékét. A nézés másik módja az, hogy az eredeti érték kiszámítja a második végső értéket.

A következő kód második utasításában az a vagy b továbbra is prvalue-nak tekinthető:

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

Tehát, a második utasításban szereplő a vagy b értéke érték, mert azonosít egy objektumot. Ez szintén prvalue, mivel kiszámítja az összeadás operátor operandusának egész számát.

(új int), és nem az a hely, amelyet létrehoz, prvalue. A következő utasításban a hely visszatérési címét egy mutatóobjektumhoz rendeltük:

int * ptrInt = új int

Itt a * ptrInt értéke lvalue, míg (new int) prvalue. Ne feledje, hogy az lvalue vagy a prvalue kifejezés. (új int) nem azonosít egyetlen objektumot sem. A cím visszaadása nem jelenti az objektum azonosítását egy névvel (például a fenti azonosítóval). A * ptrInt fájlban a ptrInt név azonosítja az objektumot, tehát a * ptrInt egy érték. Másrészről az (új int) egy prvalue, mivel egy új helyet kiszámít egy operandusértékű címre a hozzárendelési operátor számára =.

xvalue

Ma az lvalue a Helyértéket jelenti; a prvalue a „tiszta” értéket jelenti (lásd alább, mit jelent az érték). Ma az xvalue az „eXpiring” lvalue kifejezést jelenti.

Az xvalue meghatározása a C ++ specifikációból idézve a következő:

„Az xvalue olyan érték, amely olyan objektumot vagy bitmezőt jelöl, amelynek erőforrásai újrafelhasználhatók (általában azért, mert élettartama végéhez közeledik). [Példa: Bizonyos típusú kifejezések, amelyek rvalue referenciákat tartalmaznak, xértékeket eredményeznek, például egy olyan függvény hívása, amelynek visszatérési típusa rvalue referencia, vagy rvalue referencia típus végére leadott példa] ”

Ez azt jelenti, hogy az lvalue és az prvalue is lejárhat. A következő (felülről másolt) kód megmutatja, hogy az lvalue, * ptrInt tárhelye (erőforrása) hogyan kerül felhasználásra törlése után.

int * ptrInt = új int;
* ptrInt = 12;
cout<< *ptrInt <<'\n';
ptrInt törlése;
cout<< *ptrInt <<'\n';
* ptrInt = 24;
cout<< *ptrInt <<'\n';

A kimenet:

12
0
24

A következő (felülről másolt) program megmutatja, hogy egy egész hivatkozás tárolása, amely egy függvény által visszaadott lvalue referencia, újra felhasználásra kerül a main () függvényben:

#include
névtér használata std;
int & fn ()

int i = 5;
int & j = i;
visszatér j;

int main ()

int & myInt = fn ();
cout<< myInt <<'\n';
myInt = 17;
cout<< myInt <<'\n';
visszatér 0;

A kimenet:

5
17

Amikor egy objektum, például az i az fn () függvényben, kívül esik a hatókörön, természetesen megsemmisül. Ebben az esetben az i tárolását továbbra is a main () függvényben használták fel újra.

A fenti két kódminta szemlélteti az értékek tárolásának újrafelhasználását. Lehetséges a prvalues ​​(rvalues) újbóli felhasználása a tárban (lásd később).

Az xvalue-ra vonatkozó következő idézet a C ++ specifikációból származik:

„Általában ennek a szabálynak az a következménye, hogy a megnevezett rvalue hivatkozásokat értékként kezeljük, az objektumokra vonatkozó meg nem nevezett rvalue hivatkozásokat pedig xvalues ​​értékként kezeljük. A függvényekre vonatkozó rvalue hivatkozásokat értékként kezeljük, akár megnevezve, akár nem." (Viszlát).

Tehát az xvalue olyan lvalue vagy prvalue, amelynek erőforrásait (tárolóját) újra fel lehet használni. Az xvalues ​​az lvalues ​​és prvalues ​​metszéspontja.

Az xvalue-nál több van, mint amivel ebben a cikkben foglalkozunk. Az xvalue azonban megérdemel egy egész cikket, ezért az xvalue extra specifikációival ez a cikk nem foglalkozik.

Kifejezés kategória taxonómia készlet

Egy másik idézet a C ++ specifikációból:

jegyzet: Történelmileg az értékeket és az értékeket úgy hívták, hogy megjelölhettek egy megbízás bal és jobb oldalán (bár ez általában már nem igaz); Az értékek „általánosított” értékek, az értékek „tiszta” értékek, az x értékek pedig az „eXpiring” értékek. Nevük ellenére ezek a kifejezések a kifejezéseket osztályozzák, nem pedig az értékeket. - végjegyzet ”

Tehát, a glvalues ​​az lvalues, az xvalu és az rvalues ​​az xvalues ​​és prvalues ​​egyesítési halmaza. Az xvalues ​​az lvalues ​​és prvalues ​​metszéspontja.

Mostantól a kategória-rendszertan kifejezést jobban szemlélteti egy Venn-diagram az alábbiak szerint:

Következtetés

Az lvalue olyan kifejezés, amelynek értékelése meghatározza egy objektum, bitmező vagy függvény azonosságát.

A prvalue olyan kifejezés, amelynek kiértékelése inicializál egy objektumot vagy egy bitmezőt, vagy kiszámítja az operátor operandusának értékét, amint azt a kontextus meghatározza, amelyben megjelenik.

Az xvalue egy lvalue vagy egy prvalue, azzal a további tulajdonsággal, amelyet erőforrásai (tárolója) újra felhasználhatnak.

A C ++ specifikáció egy fa diagram segítségével szemlélteti a kifejezéskategória taxonómiáját, jelezve, hogy a taxonómiában van némi hierarchia. A taxonómiában jelenleg nincs hierarchia, ezért egyes szerzők egy Venn-diagramot használnak, mivel ez jobban szemlélteti a taxonómiát, mint a fa diagram.

Az AutoKey használata a Linux játékok automatizálásához
Az AutoKey egy asztali automatizáló segédprogram Linux és X11 rendszerekhez, Python 3, GTK és Qt programozással. A parancsfájlok és a MACRO funkcióina...
Az FPS-számláló megjelenítése a Linux-játékokban
A Linux játék komoly lendületet kapott, amikor a Valve 2012-ben bejelentette a Linux támogatását a Steam kliensnek és játékaiknak. Azóta sok AAA és in...
Sid Meier Civilization VI letöltése és lejátszása Linuxon
Bevezetés a játékba A Civilization 6 egy modern felvétel a Age of Empires játékok sorozatában bevezetett klasszikus koncepcióra. Az ötlet meglehetősen...