C ++

Taxonomie kategorie výrazů v C ++

Taxonomie kategorie výrazů v C ++

Výpočet je jakýkoli typ výpočtu, který se řídí přesně definovaným algoritmem. Výraz je posloupnost operátorů a operandů, která určuje výpočet. Jinými slovy, výraz je identifikátor nebo literál nebo posloupnost obou, spojené operátory.V programování může výraz vyústit v hodnotu a / nebo způsobit, že se něco stane. Když je výsledkem hodnota, výraz je glvalue, rvalue, lvalue, xvalue nebo prvalue. Každá z těchto kategorií je sada výrazů. Každá sada má definici a konkrétní situace, kdy převažuje její význam, který ji odlišuje od jiné sady. Každá sada se nazývá hodnotová kategorie.

Poznámka: Hodnota nebo literál je stále výraz, takže tyto výrazy klasifikují výrazy a ne skutečné hodnoty.

glvalue a rvalue jsou dvě podmnožiny z výrazu velké množiny. glvalue existuje ve dvou dalších podmnožinách: lvalue a xvalue. rvalue, další podmnožina výrazu, existuje také ve dvou dalších podmnožinách: xvalue a prvalue. Takže xvalue je podmnožinou glvalue a rvalue: to znamená, že xvalue je průsečík glvalue a rvalue. Následující diagram taxonomie, převzatý ze specifikace C ++, ilustruje vztah všech sad:

prvalue, xvalue a lvalue jsou hodnoty primární kategorie. glvalue je spojení hodnot a hodnot x, zatímco hodnoty r jsou spojením hodnot a hodnot.

Abyste porozuměli tomuto článku, potřebujete základní znalosti v C ++; potřebujete také znalost oboru v C++.

Obsah článku

Základy

Abyste skutečně porozuměli taxonomii kategorie výrazů, musíte si nejprve připomenout nebo znát následující základní funkce: umístění a objekt, úložiště a zdroj, inicializace, identifikátor a reference, odkazy lvalue a rvalue, ukazatel, volné úložiště a opětovné použití a zdroj.

Umístění a objekt

Zvažte následující prohlášení:

int ident;

Toto je deklarace, která identifikuje umístění v paměti. Umístění je konkrétní sada po sobě jdoucích bajtů v paměti. Umístění se může skládat z jednoho bajtu, dvou bajtů, čtyř bajtů, šedesáti čtyř bajtů atd. Umístění pro celé číslo pro 32bitový stroj je čtyři bajty. Místo lze také identifikovat pomocí identifikátoru.

Ve výše uvedené deklaraci nemá umístění žádný obsah. To znamená, že nemá žádnou hodnotu, protože obsah je hodnota. Takže identifikátor identifikuje místo (malý souvislý prostor). Když je umístění přidělen konkrétní obsah, identifikátor poté identifikuje umístění i obsah; to znamená, že identifikátor poté identifikuje jak umístění, tak hodnotu.

Zvažte následující tvrzení:

int ident1 = 5;
int ident2 = 100;

Každý z těchto příkazů je deklarací a definicí. První identifikátor má hodnotu (obsah) 5 a druhý identifikátor má hodnotu 100. V 32bitovém stroji je každé z těchto umístění dlouhé čtyři bajty. První identifikátor identifikuje jak umístění, tak hodnotu. Druhý identifikátor také identifikuje obojí.

Objekt je pojmenovaná oblast úložiště v paměti. Objekt je tedy buď umístění bez hodnoty, nebo umístění s hodnotou.

Úložiště a zdroje objektů

Umístění objektu se také nazývá úložiště nebo prostředek objektu.

Inicializace

Zvažte následující segment kódu:

int ident;
ident = 8;

První řádek deklaruje identifikátor. Tato deklarace poskytuje umístění (úložiště nebo prostředek) pro celočíselný objekt a identifikuje jej jménem, ​​ident. Další řádek vloží hodnotu 8 (v bitech) do místa identifikovaného ident. Uvedení této hodnoty je inicializace.

Následující příkaz definuje vektor s obsahem 1, 2, 3, 4, 5 identifikovaný vtr:

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

Zde se inicializace pomocí 1, 2, 3, 4, 5 provádí ve stejném příkazu definice (deklarace). Operátor přiřazení se nepoužívá. Následující příkaz definuje pole s obsahem 1, 2, 3, 4, 5:

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

Tentokrát byl pro inicializaci použit operátor přiřazení.

Identifikátor a reference

Zvažte následující segment kódu:

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

Výstupem je:

4 4 4

ident je identifikátor, zatímco ref1 a ref2 jsou odkazy; odkazují na stejné místo. Odkaz je synonymem pro identifikátor. Konvenčně jsou ref1 a ref2 různé názvy jednoho objektu, zatímco ident je identifikátor stejného objektu. Ident však lze stále nazývat názvem objektu, což znamená, ident, ref1 a ref2 pojmenovat stejné umístění.

Hlavní rozdíl mezi identifikátorem a odkazem je v tom, že když je předán jako argument funkci, je-li předán identifikátorem, je vytvořena kopie identifikátoru ve funkci, zatímco pokud je předán odkazem, použije se stejné umístění v rámci funkce. Takže předávání identifikátoru končí na dvou místech, zatímco předávání odkazem končí na stejném jednom místě.

lvalue Reference a rvalue Reference

Normální způsob vytvoření reference je následující:

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

Úložiště (zdroj) je nejprve lokalizováno a identifikováno (s názvem, jako je ident), a poté je vytvořen odkaz (s názvem, jako je ref). Při předávání jako argument funkce bude ve funkci vytvořena kopie identifikátoru, zatímco v případě odkazu bude ve funkci použito (odkazováno) původní umístění ve funkci.

Dnes je možné mít pouze odkaz bez jeho identifikace. To znamená, že je možné nejprve vytvořit odkaz, aniž byste museli mít identifikátor umístění. Toto používá &&, jak je uvedeno v následujícím prohlášení:

int && ref = 4;

Zde neexistuje žádná předchozí identifikace. Pro přístup k hodnotě objektu jednoduše použijte ref, jako byste použili výše uvedený ident.

S && deklarací není možnost předat argument funkci podle identifikátoru. Jedinou možností je projít odkazem. V tomto případě je ve funkci použito pouze jedno umístění a ne druhé zkopírované umístění jako u identifikátoru.

Referenční deklarace s & se nazývá lvalue reference. Referenční deklarace s && se nazývá rvalue reference, což je také reference první hodnoty (viz níže).

Ukazatel

Zvažte následující kód:

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

Výstup je 5.

Zde je ptdInt identifikátor jako výše uvedený ident. Místo jednoho jsou zde dva objekty (umístění): špičatý objekt, ptdInt identifikovaný pomocí ptdInt a ukazatelový objekt, ptrInt identifikovaný pomocí ptrInt. & ptdInt vrátí adresu špičatého objektu a vloží ji jako hodnotu do ukazatele ptrInt objektu. Chcete-li vrátit (získat) hodnotu špičatého objektu, použijte identifikátor ukazatele, jako v „* ptrInt“.

Poznámka: ptdInt je identifikátor a nikoli odkaz, zatímco název, ref, uvedený výše, je odkaz.

Druhý a třetí řádek ve výše uvedeném kódu lze zmenšit na jeden řádek, což vede k následujícímu kódu:

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

Poznámka: Když se ukazatel zvýší, ukazuje na další umístění, které není přídavkem hodnoty 1. Když je ukazatel snížen, ukazuje na předchozí umístění, což není odečtením hodnoty 1.

Obchod zdarma

Operační systém přiděluje paměť pro každý spuštěný program. Paměť, která není přidělena žádnému programu, se nazývá volné úložiště. Výraz, který vrací umístění pro celé číslo z bezplatného úložiště, je:

nová int

Tím se vrátí umístění pro celé číslo, které není identifikováno. Následující kód ukazuje, jak použít ukazatel s obchodem zdarma:

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

Výstup je 12.

Chcete-li objekt zničit, použijte výraz odstranění následovně:

smazat ptrInt;

Argument výrazu odstranění je ukazatel. Následující kód ilustruje jeho použití:

int * ptrInt = new int;
* ptrInt = 12;
smazat ptrInt;
cout<< *ptrInt <<'\n';

Výstup je 0, a nic jako null nebo undefined. odstranit nahradí hodnotu pro umístění výchozí hodnotou konkrétního typu umístění, poté umožní umístění pro opakované použití. Výchozí hodnota pro umístění int je 0.

Opětovné použití zdroje

V taxonomii kategorie výrazů je opětovné použití prostředku stejné jako opětovné použití umístění nebo úložiště pro objekt. Následující kód ukazuje, jak lze znovu použít umístění z bezplatného obchodu:

int * ptrInt = new int;
* ptrInt = 12;
cout<< *ptrInt <<'\n';
smazat ptrInt;
cout<< *ptrInt <<'\n';
* ptrInt = 24;
cout<< *ptrInt <<'\n';

Výstupem je:

12
0
24

K neidentifikovanému umístění je nejprve přiřazena hodnota 12. Poté je obsah umístění odstraněn (teoreticky je objekt odstraněn). Hodnota 24 je znovu přiřazena ke stejnému umístění.

Následující program ukazuje, jak je znovu použit celočíselný odkaz vrácený funkcí:

#zahrnout
pomocí jmenného prostoru std;
int & fn ()

int i = 5;
int & j = i;
návrat j;

int main ()

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

Výstupem je:

5
17

Objekt jako i, deklarovaný v místním oboru (obor funkcí), přestane existovat na konci místního oboru. Funkce fn () výše však vrací odkaz na i. Prostřednictvím této vrácené reference název, myInt ve funkci main (), znovu použije místo identifikované i pro hodnotu 17.

lhodnota

Hodnota lvalue je výraz, jehož vyhodnocení určuje identitu objektu, bitového pole nebo funkce. Identita je oficiální identita, jako je ident výše, nebo referenční název lvalue, ukazatel nebo název funkce. Zvažte následující kód, který funguje:

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

++ptr; --ptr;
vrátit myInt;

Zde je myInt hodnota; myRef je referenční výraz lvalue; * ptr je výraz lvalue, protože jeho výsledek je identifikovatelný s ptr; ++ ptr nebo -ptr je výraz lvalue, protože jeho výsledek je identifikovatelný s novým stavem (adresou) ptr a fn je lvalue (výraz).

Zvažte následující segment kódu:

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

Ve druhém prohlášení má umístění pro 'a' 2 a je identifikovatelné pomocí 'a', stejně jako lvalue. Umístění pro b má 8 a je identifikovatelné b, a tak je i lvalue. Umístění pro c bude mít součet a je identifikovatelné podle c, stejně tak lvalue. Ve druhém příkazu jsou výrazy nebo hodnoty 16 a 64 rvalue (viz níže).

Zvažte následující segment kódu:

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

Výstup je 'proti';

seq je pole. Umístění „v“ nebo jakékoli podobné hodnoty v poli je označeno seq [i], kde i je index. Výraz seq [i] je tedy výrazem lvalue. seq, což je identifikátor celého pole, je také hodnotou l.

prvočíslo

Prvalue je výraz, jehož vyhodnocení inicializuje objekt nebo bitové pole nebo vypočítá hodnotu operandu operátoru, jak je specifikováno kontextem, ve kterém se objeví.

V prohlášení,

int myInt = 256;

256 je prvalue (prvalue výraz), který inicializuje objekt identifikovaný myInt. Na tento objekt se neodkazuje.

V prohlášení,

int && ref = 4;

4 je prvalue (výraz prvalue), který inicializuje objekt odkazovaný ref. Tento objekt není oficiálně identifikován. ref je příklad referenčního výrazu rvalue nebo referenčního výrazu prvalue; je to jméno, ale ne oficiální identifikátor.

Zvažte následující segment kódu:

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

6 je prvočíslo, které inicializuje objekt identifikovaný pomocí ident; na objekt odkazuje také ref. Zde je ref odkaz na hodnotu lvalue a ne na odkaz na první hodnotu.

Zvažte následující segment kódu:

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

15 a 63 jsou konstanta, která se počítá sama za sebe a vytváří operand (v bitech) pro operátor sčítání. 15 nebo 63 je tedy výraz první hodnoty.

Libovolný literál, kromě řetězcového literálu, je prvalue (tj.E., výraz první hodnoty). Doslov, například 58 nebo 58.53, nebo true nebo false, je prvočíslo. Literál lze použít k inicializaci objektu nebo k výpočtu pro sebe (do nějaké jiné formy v bitech) jako hodnotu operandu pro operátora. Ve výše uvedeném kódu literál 2 inicializuje objekt, a. Rovněž se počítá jako operand pro operátora přiřazení.

Proč není řetězcový literál prvočíslem? Zvažte následující kód:

char str [] = "láska není nenávist";
cout << str <<'\n';
cout << str[5] <<'\n';

Výstupem je:

láska není nenávist
n

str identifikuje celý řetězec. Výraz, str a ne to, co identifikuje, je tedy lvalue. Každý znak v řetězci lze identifikovat str [i], kde i je index. Výraz str [5], a nikoli znak, který identifikuje, je hodnota. Řetězcový literál je lvalue a nikoli prvalue.

V následujícím příkazu inicializuje pole literál objektu, arr:

ptrInt ++ nebo ptrInt-- 

Zde je ptrInt ukazatel na celé číslo. Celý výraz, a nikoli konečná hodnota umístění, na které ukazuje, je prvalue (výraz). Důvodem je, že výraz, ptrInt ++ nebo ptrInt-, identifikuje původní první hodnotu svého umístění a ne druhou konečnou hodnotu stejného umístění. Na druhou stranu, -ptrInt nebo -ptrInt je lvalue, protože identifikuje jedinou hodnotu zájmu v místě. Dalším způsobem, jak se na to dívat, je, že původní hodnota vypočítá druhou konečnou hodnotu.

Ve druhém prohlášení následujícího kódu lze a nebo b stále považovat za prvočíslo:

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

Takže a nebo b v druhém příkazu je lvalue, protože identifikuje objekt. Je to také první hodnota, protože se počítá na celé číslo operandu pro operátor přidání.

(new int), a nikoli umístění, které zřídí, je prvalue. V následujícím prohlášení je zpáteční adresa umístění přiřazena k objektu ukazatele:

int * ptrInt = nový int

Zde * ptrInt je lvalue, zatímco (new int) je prvalue. Pamatujte, že lvalue nebo prvalue je výraz. (new int) neidentifikuje žádný objekt. Vrácení adresy neznamená identifikaci objektu jménem (například ident, výše). V * ptrInt je název ptrInt tím, co skutečně identifikuje objekt, takže * ptrInt je lvalue. Na druhou stranu (new int) je prvalue, protože vypočítává nové umístění na adresu hodnoty operandu pro operátor přiřazení =.

xvalue

Dnes lvalue znamená Location Value; prvalue znamená „čistá“ rvalue (viz níže rvalue). Dnes xvalue znamená „eXpiring“ lvalue.

Definice xvalue, citovaná ze specifikace C ++, je následující:

„Xvalue je glvalue, která označuje objekt nebo bitové pole, jehož zdroje lze znovu použít (obvykle proto, že je blízko konce své životnosti). [Příklad: Některé druhy výrazů zahrnujících odkazy rvalue přinášejí hodnoty x, například volání funkce, jejíž návratový typ je odkaz rvalue nebo přetypování na typ odkazu rvalue - koncový příklad] “

To znamená, že platnost lvalue i prvalue může vypršet. Následující kód (zkopírovaný shora) ukazuje, jak je úložiště (prostředek) lvalue, * ptrInt znovu použito po jeho odstranění.

int * ptrInt = new int;
* ptrInt = 12;
cout<< *ptrInt <<'\n';
smazat ptrInt;
cout<< *ptrInt <<'\n';
* ptrInt = 24;
cout<< *ptrInt <<'\n';

Výstupem je:

12
0
24

Následující program (zkopírovaný shora) ukazuje, jak je úložiště funkce integer reference, což je odkaz lvalue vrácený funkcí, znovu použito ve funkci main ():

#zahrnout
pomocí jmenného prostoru std;
int & fn ()

int i = 5;
int & j = i;
návrat j;

int main ()

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

Výstupem je:

5
17

Když objekt, jako je i ve funkci fn (), vyjde z rozsahu, přirozeně se zničí. V tomto případě bylo úložiště i stále znovu použito ve funkci main ().

Výše uvedené dva ukázky kódu ilustrují opětovné použití úložiště hodnot. Je možné, aby úložiště bylo znovu použito prvalues ​​(rvalues) (viz dále).

Následující citace týkající se xvalue pochází ze specifikace C ++:

„Obecně platí, že účinkem tohoto pravidla je, že pojmenované odkazy rvalue jsou považovány za lvalue a nepojmenované odkazy rvalue na objekty jsou považovány za xvalues. rvalue odkazy na funkce jsou považovány za lvalues ​​ať už pojmenované nebo ne.”(Viz dále).

Takže xvalue je lvalue nebo prvalue, jejíž prostředky (úložiště) lze znovu použít. xvalues ​​je sada průsečíků lvalue a prvalues.

Existuje více než xvalue, než co bylo řešeno v tomto článku. Xvalue si však zaslouží celý článek sám o sobě, a proto se v tomto článku nezabýváme zvláštními specifikacemi pro xvalue.

Sada taxonomie kategorií výrazů

Další citace ze specifikace C ++:

"Poznámka: Historicky byly hodnoty a hodnoty hodnoceny tak, že se mohly objevit na levé a pravé straně úkolu (i když to již obecně neplatí); glvalue jsou „zobecněné“ lvalue, prvalues ​​jsou „čisté“ rvalue a xvalues ​​jsou „eXpiring“ lvalue. Přes jejich názvy tyto výrazy klasifikují výrazy, nikoli hodnoty. - závěrečná poznámka “

Takže glvalues ​​je sjednocující množina lvalue a xvalues ​​a rvalues ​​jsou sjednocující množina xvalues ​​a prvalues. xvalues ​​je sada průsečíků lvalue a prvalues.

Od této chvíle je taxonomie kategorie výrazů lépe ilustrována Vennovým diagramem následujícím způsobem:

Závěr

Hodnota lvalue je výraz, jehož vyhodnocení určuje identitu objektu, bitového pole nebo funkce.

Prvalue je výraz, jehož vyhodnocení inicializuje objekt nebo bitové pole nebo vypočítá hodnotu operandu operátoru, jak je specifikováno kontextem, ve kterém se objevuje.

Xvalue je lvalue nebo prvalue, s další vlastností, že jeho zdroje (úložiště) lze znovu použít.

Specifikace C ++ ilustruje taxonomii kategorie výrazů pomocí stromového diagramu, což naznačuje, že v taxonomii existuje určitá hierarchie. Od této chvíle v taxonomii neexistuje žádná hierarchie, takže někteří autoři používají Vennův diagram, protože ilustruje taxonomii lépe než stromový diagram.

Hry Porty komerčních her s otevřeným zdrojovým kódem
Porty komerčních her s otevřeným zdrojovým kódem
Zdarma, s otevřeným zdrojovým kódem a multiplatformní herní enginy lze hrát staré i některé z poměrně nedávných herních titulů. Tento článek uvede sez...
Hry Nejlepší hry z příkazového řádku pro Linux
Nejlepší hry z příkazového řádku pro Linux
Příkazový řádek není při používání Linuxu jen vaším největším spojencem - může být také zdrojem zábavy, protože jej můžete použít k hraní mnoha zábavn...
Hry Nejlepší aplikace pro mapování gamepadu pro Linux
Nejlepší aplikace pro mapování gamepadu pro Linux
Pokud rádi hrajete hry na Linuxu s gamepadem místo typického vstupního systému pro klávesnici a myš, máte k dispozici několik užitečných aplikací. Mno...