C ++

Категория на изразите Таксономия в C ++

Категория на изразите Таксономия в C ++

Изчислението е всеки вид изчисление, което следва добре дефиниран алгоритъм. Изразът е последователност от оператори и операнди, която указва изчисление. С други думи, изразът е идентификатор или литерал, или последователност от двете, обединени от оператори.При програмирането изразът може да доведе до стойност и / или да причини някакво случване. Когато води до стойност, изразът е glvalue, rvalue, lvalue, xvalue или prvalue. Всяка от тези категории е набор от изрази. Всеки набор има определение и конкретни ситуации, при които неговото значение преобладава, което го разграничава от друг набор. Всеки набор се нарича стойностна категория.

Забележка: Стойността или литералът все още е израз, така че тези термини класифицират изрази, а не реално стойности.

glvalue и rvalue са двете подгрупи от израза за голям набор. glvalue съществува в две допълнителни подмножества: lvalue и xvalue. rvalue, другото подмножество за израз, също съществува в две допълнителни подмножества: xvalue и prvalue. И така, xvalue е подмножество както на glvalue, така и на rvalue: тоест xvalue е пресечната точка на glvalue и rvalue. Следващата диаграма на таксономията, взета от спецификацията C ++, илюстрира връзката на всички набори:

prvalue, xvalue и lvalue са основните категории. glvalue е обединението на lvalues ​​и xvalues, докато rvalues ​​са обединението на xvalues ​​и prvalues.

Необходими са ви основни познания по C ++, за да разберете тази статия; вие също се нуждаете от знания за обхвата в C++.

Съдържание на статията

Основи

За да разберете наистина таксономията на категорията на изразите, първо трябва да си припомните или да знаете следните основни характеристики: местоположение и обект, съхранение и ресурс, инициализация, идентификатор и препратка, препратки lvalue и rvalue, указател, безплатно съхранение и повторно използване на ресурс.

Местоположение и обект

Обмислете следната декларация:

int ident;

Това е декларация, която идентифицира местоположение в паметта. Местоположението е определен набор от последователни байтове в паметта. Местоположението може да се състои от един байт, два байта, четири байта, шейсет и четири байта и т.н. Мястото за цяло число за 32-битова машина е четири байта. Също така местоположението може да бъде идентифицирано с идентификатор.

В горната декларация местоположението няма никакво съдържание. Това означава, че той няма никаква стойност, тъй като съдържанието е стойността. И така, идентификаторът идентифицира местоположение (малко непрекъснато пространство). Когато местоположението получава конкретно съдържание, идентификаторът тогава идентифицира както местоположението, така и съдържанието; тоест идентификаторът след това идентифицира както местоположението, така и стойността.

Обмислете следните твърдения:

int ident1 = 5;
int ident2 = 100;

Всяко от тези изявления е декларация и дефиниция. Първият идентификатор има стойността (съдържание) 5, а вторият идентификатор има стойността 100. В 32-битова машина всяко от тези местоположения е с дължина четири байта. Първият идентификатор идентифицира едновременно местоположение и стойност. Вторият идентификатор също идентифицира и двете.

Обектът е наименуван регион на съхранение в паметта. И така, обектът е или местоположение без стойност, или местоположение със стойност.

Съхранение на обекти и ресурси

Местоположението на обект се нарича още хранилище или ресурс на обекта.

Инициализация

Помислете за следния кодов сегмент:

int ident;
идентичност = 8;

Първият ред декларира идентификатор. Тази декларация осигурява местоположение (съхранение или ресурс) за цяло число обект, идентифицирайки го с името, ident. Следващият ред поставя стойността 8 (в битове) в местоположението, идентифицирано с ident. Поставянето на тази стойност е инициализация.

Следното изявление определя вектор със съдържание 1, 2, 3, 4, 5, идентифициран от vtr:

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

Тук инициализацията с 1, 2, 3, 4, 5 се извършва в същия израз на дефиницията (декларация). Операторът за присвояване не се използва. Следният израз определя масив със съдържание 1, 2, 3, 4, 5:

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

Този път за инициализация е използван оператор за присвояване.

Идентификатор и справка

Помислете за следния кодов сегмент:

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

Резултатът е:

4 4 4

ident е идентификатор, докато ref1 и ref2 са препратки; те се позовават на едно и също място. Препратката е синоним на идентификатор. Обикновено ref1 и ref2 са различни имена на един обект, докато ident е идентификаторът на един и същ обект. Идентификацията обаче все още може да се нарече името на обекта, което означава, че ident, ref1 и ref2 дават име на същото място.

Основната разлика между идентификатора и препратката е, че когато се предава като аргумент на функция, ако се предава от идентификатор, се прави копие за идентификатора във функцията, докато ако се предава чрез препратка, същото място се използва в функция. Така че преминаването от идентификатора завършва с две местоположения, докато преминаването от референцията завършва със същото едно и също място.

lvalue справка и rvalue справка

Нормалният начин за създаване на препратка е както следва:

int ident;
идентичност = 4;
int & ref = ident;

Първо се намира и идентифицира хранилището (ресурс) (с име като ident), а след това се прави справка (с име като ref). Когато се предава като аргумент на функция, във функцията ще бъде направено копие на идентификатора, докато в случая на препратка ще бъде използвано (посочено) оригиналното местоположение във функцията.

Днес е възможно просто да имате препратка, без да я идентифицирате. Това означава, че е възможно първо да се създаде препратка, без да има идентификатор за местоположението. Това използва &&, както е показано в следното изявление:

int && ref = 4;

Тук няма предходна идентификация. За да получите достъп до стойността на обекта, просто използвайте ref, както бихте използвали идентификатора по-горе.

С декларацията && няма възможност за предаване на аргумент на функция чрез идентификатор. Единственият избор е да се премине по препратка. В този случай във функцията се използва само едно местоположение, а не второто копирано местоположение, както при идентификатор.

Референтна декларация с & се нарича lvalue reference. Декларация за референция с && се нарича rvalue reference, която също е справка за prvalue (вижте по-долу).

Показалец

Обмислете следния код:

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

Изходът е 5.

Тук ptdInt е идентификатор като идентификатора по-горе. Тук има два обекта (местоположения) вместо един: посоченият обект, ptdInt, идентифициран от ptdInt, и обектът на указателя, ptrInt, идентифициран от ptrInt. & ptdInt връща адреса на посочения обект и го поставя като стойност в обекта ptrInt на указателя. За да върнете (получите) стойността на посочения обект, използвайте идентификатора за обекта на показалеца, както в “* ptrInt”.

Забележка: ptdInt е идентификатор, а не препратка, докато името, ref, споменато по-рано, е препратка.

Вторият и третият ред в горния код могат да бъдат намалени до един ред, което води до следния код:

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

Забележка: Когато даден указател се увеличи, той сочи към следващото местоположение, което не е добавяне на стойността 1. Когато указателят се декрементира, той сочи към предишното местоположение, което не е изваждане на стойността 1.

Безплатен магазин

Операционната система разпределя памет за всяка изпълняваща се програма. Памет, която не е разпределена за никоя програма, е известна като безплатен магазин. Изразът, който връща местоположение за цяло число от безплатния магазин, е:

нов инт

Това връща местоположение за цяло число, което не е идентифицирано. Следният код илюстрира как да използвате показалеца с безплатния магазин:

int * ptrInt = нов int;
* ptrInt = 12;
Cout<< *ptrInt  <<'\n';

Изходът е 12.

За да унищожите обекта, използвайте израза за изтриване, както следва:

изтриване на ptrInt;

Аргументът на израза за изтриване е указател. Следният код илюстрира използването му:

int * ptrInt = нов int;
* ptrInt = 12;
изтриване на ptrInt;
Cout<< *ptrInt <<'\n';

Изходът е 0, и не нещо като null или undefined. delete заменя стойността за местоположението със стойността по подразбиране за конкретния тип местоположение, след което позволява местоположението да се използва повторно. Стойността по подразбиране за int местоположение е 0.

Повторно използване на ресурс

В таксономията на категорията на изразите повторното използване на ресурс е същото като повторното използване на местоположение или съхранение за обект. Следният код илюстрира как местоположението от безплатния магазин може да бъде използвано повторно:

int * ptrInt = нов int;
* ptrInt = 12;
Cout<< *ptrInt <<'\n';
изтриване на ptrInt;
Cout<< *ptrInt <<'\n';
* ptrInt = 24;
Cout<< *ptrInt <<'\n';

Резултатът е:

12
0
24

Първо се присвоява стойност 12 на неидентифицираното местоположение. След това съдържанието на местоположението се изтрива (на теория обектът се изтрива). Стойността 24 се пренасочва към същото място.

Следващата програма показва как се използва повторно целочислена референция, върната от функция:

#include
използване на пространство от имена std;
int & fn ()

int i = 5;
int & j = i;
връщане j;

int main ()

int & myInt = fn ();
Cout<< myInt <<'\n';
myInt = 17;
Cout<< myInt <<'\n';
връщане 0;

Резултатът е:

5
17

Обект като i, деклариран в локален обхват (обхват на функция), престава да съществува в края на локалния обхват. Функцията fn () по-горе обаче връща препратката към i. Чрез тази върната препратка името myInt във функцията main () използва повторно местоположението, идентифицирано от i за стойността 17.

стойност

Lvalue е израз, чиято оценка определя идентичността на обект, битово поле или функция. Идентичността е официална идентичност като ident по-горе или име на препратка lvalue, указател или име на функция. Помислете за следния код, който работи:

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

++ptr; --ptr;
върнете myInt;

Тук myInt е стойност; myRef е lvalue референтен израз; * ptr е израз на lvalue, защото резултатът му може да се идентифицира с ptr; ++ ptr или -ptr е израз на lvalue, защото резултатът му може да се идентифицира с новото състояние (адрес) на ptr, а fn е lvalue (израз).

Помислете за следния кодов сегмент:

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

Във второто изявление местоположението на „a“ има 2 и се идентифицира с „a“, както и lvalue. Местоположението на b има 8 и се идентифицира с b, както и lvalue. Мястото за c ще има сумата и може да се идентифицира с c, както и lvalue. Във второто изявление изразите или стойностите на 16 и 64 са rvalues ​​(виж по-долу).

Помислете за следния кодов сегмент:

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

Резултатът е 'v';

seq е масив. Местоположението за 'v' или някаква подобна стойност в масива се идентифицира чрез seq [i], където i е индекс. Така че изразът, seq [i], е израз на стойност. seq, който е идентификаторът за целия масив, също е стойност.

първа стойност

Prvalue е израз, чиято оценка инициализира обект или битово поле или изчислява стойността на операнда на оператор, както е посочено от контекста, в който се появява.

В изявлението,

int myInt = 256;

256 е първа стойност (израз на първа стойност), която инициализира обекта, идентифициран от myInt. Този обект не е референтен.

В изявлението,

int && ref = 4;

4 е първа стойност (израз на първа стойност), която инициализира обекта, посочен от ref. Този обект не е идентифициран официално. ref е пример за референтен израз rvalue или референтен израз prvalue; това е име, но не и официален идентификатор.

Помислете за следния кодов сегмент:

int ident;
идентичност = 6;
int & ref = ident;

6 е първа стойност, която инициализира обекта, идентифициран с ident; обектът се препраща и от реф. Тук ref е референция lvalue, а не референция prvalue.

Помислете за следния кодов сегмент:

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

15 и 63 са константа, която изчислява сама, създавайки операнд (в битове) за оператора за събиране. И така, 15 или 63 е израз на първо значение.

Всеки литерал, с изключение на низовия литерал, е първа стойност (т.е.д., израз на първа стойност). И така, буквал като 58 или 58.53, или вярно или невярно, е първо значение. Литералът може да се използва за инициализиране на обект или би изчислил за себе си (в някаква друга форма в битове) като стойност на операнд за оператор. В горния код литералът 2 инициализира обекта, a. Той също така се изчислява като операнд за оператора за присвояване.

Защо низовият литерал не е първа стойност? Обмислете следния код:

char str [] = "любовта не мрази";
Cout << str <<'\n';
Cout << str[5] <<'\n';

Резултатът е:

любов не омраза
н

str идентифицира целия низ. Така че изразът str, а не това, което той идентифицира, е стойност. Всеки символ в низа може да бъде идентифициран чрез str [i], където i е индекс. Изразът str [5], а не символът, който той идентифицира, е стойност. Низовият литерал е lvalue, а не първа стойност.

В следния оператор литерал на масив инициализира обекта, arr:

ptrInt ++ или ptrInt-- 

Тук ptrInt е указател към целочислено местоположение. Целият израз, а не крайната стойност на местоположението, към което сочи, е първа стойност (израз). Това е така, защото изразът, ptrInt ++ или ptrInt-, идентифицира първоначалната първа стойност на своето местоположение, а не втората крайна стойност на същото местоположение. От друга страна, -ptrInt или -ptrInt е стойност, тъй като идентифицира единствената стойност на интереса в местоположението. Друг начин на разглеждане е, че първоначалната стойност изчислява втората крайна стойност.

Във второто изявление на следния код a или b все още може да се разглежда като първа стойност:

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

И така, a или b във втория оператор е стойност, тъй като идентифицира обект. Той също е първа стойност, тъй като изчислява до цялото число на операнд за оператора за добавяне.

(new int), а не местоположението, което установява, е първа стойност. В следващото изявление адресът за връщане на местоположението се присвоява на обект на указател:

int * ptrInt = нов int

Тук * ptrInt е стойност, докато (new int) е първа стойност. Не забравяйте, че lvalue или prvalue е израз. (new int) не идентифицира нито един обект. Връщането на адреса не означава идентифициране на обекта с име (като ident, по-горе). В * ptrInt името, ptrInt, е това, което наистина идентифицира обекта, така че * ptrInt е стойност. От друга страна, (new int) е първа стойност, тъй като изчислява ново местоположение до адрес на стойност на операнда за оператора за присвояване =.

xvalue

Днес lvalue означава местоположение; prvalue означава "чиста" rvalue (вижте какво означава rvalue по-долу). Днес xvalue означава „eXpiring“ lvalue.

Определението на xvalue, цитирано от спецификацията C ++, е както следва:

„Xvalue е glvalue, която обозначава обект или битово поле, чиито ресурси могат да бъдат използвани повторно (обикновено защото е към края на живота си). [Пример: Някои видове изрази, включващи препратки към rvalue, дават xvalues, като например извикване на функция, чийто връщащ тип е препратка към rvalue или приведение към пример за край на препратка към rvalue] “

Това означава, че lvalue и prvalue могат да изтекат. Следващият код (копиран отгоре) показва как съхранението (ресурсът) на lvalue, * ptrInt се използва повторно, след като е било изтрито.

int * ptrInt = нов int;
* ptrInt = 12;
Cout<< *ptrInt <<'\n';
изтриване на ptrInt;
Cout<< *ptrInt <<'\n';
* ptrInt = 24;
Cout<< *ptrInt <<'\n';

Резултатът е:

12
0
24

Следващата програма (копирана отгоре) показва как съхранението на цяло число референция, което е референция lvalue, върната от функция, се използва повторно във функцията main ():

#include
използване на пространство от имена std;
int & fn ()

int i = 5;
int & j = i;
връщане j;

int main ()

int & myInt = fn ();
Cout<< myInt <<'\n';
myInt = 17;
Cout<< myInt <<'\n';
връщане 0;

Резултатът е:

5
17

Когато обект като i във функцията fn () излезе извън обхвата, той естествено се унищожава. В този случай съхранението на i все още е използвано повторно във функцията main ().

Горните две примерни кодове илюстрират повторното използване на съхранението на lvalues. Възможно е повторно използване на prvalues ​​(rvalues) (вижте по-късно).

Следният цитат относно xvalue е от спецификацията C ++:

„Като цяло ефектът от това правило е, че посочените rvalue препратки се третират като lvalues, а неназованите rvalue препратки към обекти се третират като xvalues. rvalue препратките към функции се третират като lvalues ​​независимо дали са именувани или не.”(Вижте по-късно).

И така, xvalue е lvalue или prvalue, чиито ресурси (съхранение) могат да бъдат използвани повторно. xvalues ​​е пресечната точка на lvalues ​​и prvalues.

Xvalue има повече от това, което е разгледано в тази статия. Въпреки това xvalue заслужава цяла статия сама по себе си и затова допълнителните спецификации за xvalue не са разгледани в тази статия.

Набор на таксономия на категорията на израза

Друг цитат от спецификацията C ++:

Забележка: В исторически план lvalues ​​и rvalues ​​бяха така наречени, защото те можеха да се появят от лявата и дясната страна на задание (въпреки че това вече не е вярно по принцип); glvalues ​​са „обобщени“ lvalues, prvalues ​​са „чисти“ rvalues, а xvalues ​​са „eXpiring“ lvalues. Въпреки имената си, тези термини класифицират изрази, а не стойности. - крайна бележка ”

И така, glvalues ​​е обединеният набор от lvalues ​​и xvalues ​​и rvalues ​​са обединеният набор от xvalues ​​и prvalues. xvalues ​​е пресечната точка на lvalues ​​и prvalues.

Към момента таксономията на категорията на израза е по-добре илюстрирана с диаграма на Вен, както следва:

Заключение

Lvalue е израз, чиято оценка определя идентичността на обект, битово поле или функция.

Prvalue е израз, чиято оценка инициализира обект или битово поле или изчислява стойността на операнда на оператор, както е посочено от контекста, в който се появява.

Xvalue е lvalue или prvalue, с допълнителното свойство, че неговите ресурси (хранилище) могат да бъдат използвани повторно.

Спецификацията C ++ илюстрира таксономия на категорията на израза с дървовидна диаграма, показваща, че в таксономията има някаква йерархия. Към момента в таксономията няма йерархия, така че диаграма на Вен се използва от някои автори, тъй като илюстрира таксономията по-добре от дървовидната диаграма.

Как да заснемете и поточно предадете игралната си сесия на Linux
В миналото играта на игри се смяташе само за хоби, но с течение на времето игралната индустрия отбеляза огромен ръст по отношение на технологиите и бр...
Най-добрите игри за игра с ръчно проследяване
Oculus Quest наскоро представи страхотната идея за ръчно проследяване без контролери. С непрекъснато нарастващия брой игри и дейности, които изпълнява...
Как да покажете OSD наслагване в приложения на цял екран за Linux и игри
Играта на цял екран или използване на приложения в режим на цял екран без разсейване може да ви откъсне от съответната системна информация, видима в п...