Notiz: Ein Wert oder ein wörtliches ist immer noch ein Ausdruck, daher klassifizieren diese Begriffe Ausdrücke und nicht wirklich Werte.
Glvalue und RValue sind die beiden Untergruppen aus dem großen Set -Ausdruck. GLVALUE existiert in zwei weiteren Untergruppen: Lvalue und XValue. RValue, die andere Untergruppe für die Expression, existiert ebenfalls in zwei weiteren Untergruppen: XValue und PrValue. XValue ist also eine Untergruppe sowohl von Glvalue als auch von RValue: Das heißt, XValue ist der Schnittpunkt sowohl von glvalue als auch rvalue. Das folgende Taxonomie -Diagramm, das aus der C ++ - Spezifikation entnommen wurde, zeigt die Beziehung aller Sätze:
Prvalue, XValue und LValue sind die primären Kategorienwerte. Glvalue ist die Vereinigung von LVALUES und XVALUES, während Rvalues die Vereinigung von Xvalues und Prvalues sind.
Sie benötigen Grundkenntnisse in C ++, um diesen Artikel zu verstehen. Sie brauchen auch Kenntnisse über den Umfang in C++.
Artikelinhalt
Grundlagen
Um die Taxonomie der Ausdruckskategorie wirklich zu verstehen, müssen Sie zuerst die folgenden Grundfunktionen erinnern oder kennen Ressource.
Ort und Objekt
Betrachten Sie die folgende Erklärung:
int ident;
Dies ist eine Erklärung, die einen Ort im Speicher identifiziert. Ein Ort ist ein bestimmter Satz aufeinanderfolgender Bytes im Speicher. Ein Ort kann aus einem Byte, zwei Bytes, vier Bytes, vierundsechzig Bytes usw. bestehen. Der Ort für eine Ganzzahl für eine 32 -Bit -Maschine beträgt vier Bytes. Außerdem kann der Ort durch eine Kennung identifiziert werden.
In der obigen Erklärung hat der Standort keinen Inhalt. Es bedeutet, dass es keinen Wert hat, da der Inhalt der Wert ist. Ein Kennung identifiziert also einen Ort (kleiner kontinuierlicher Raum). Wenn der Ort einen bestimmten Inhalt erhält, identifiziert die Kennung dann sowohl den Standort als auch den Inhalt. Das heißt, die Kennung identifiziert dann sowohl den Ort als auch den Wert.
Betrachten Sie die folgenden Aussagen:
int ident1 = 5;
int ident2 = 100;
Jede dieser Aussagen ist eine Erklärung und eine Definition. Die erste Kennung hat den Wert (Inhalt) 5 und der zweite Kennung den Wert 100. In einer 32 -Bit -Maschine ist jeder dieser Standorte vier Bytes lang. Die erste Kennung identifiziert sowohl einen Ort als auch einen Wert. Die zweite Kennung identifiziert auch beide.
Ein Objekt ist ein benannter Speicherbereich im Speicher im Speicher. Ein Objekt ist also entweder ein Ort ohne Wert oder ein Ort mit einem Wert.
Objektspeicher und Ressource
Der Ort für ein Objekt wird auch als Speicher oder Ressource des Objekts bezeichnet.
Initialisierung
Betrachten Sie das folgende Codesegment:
int ident;
Ident = 8;
Die erste Zeile erklärt eine Kennung. Diese Deklaration bietet einen Ort (Speicher oder Ressource) für ein ganzzahliges Objekt, in dem er mit dem Namen identifiziert wird, identifiziert. In der nächsten Zeile wird der Wert 8 (in Bits) in den von Ident identifizierten Ort eingeleitet. Das Einsetzen dieses Wertes ist die Initialisierung.
Die folgende Anweisung definiert einen Vektor mit Inhalt 1, 2, 3, 4, 5, der durch VTR identifiziert wurde:
std :: vector vtr 1, 2, 3, 4, 5;
Hier erfolgt die Initialisierung mit 1, 2, 3, 4, 5 in derselben Aussage der Definition (Deklaration). Der Zuordnungsoperator wird nicht verwendet. Die folgende Anweisung definiert ein Array mit Inhalt 1, 2, 3, 4, 5:
int arr [] = 1, 2, 3, 4, 5;
Dieses Mal wurde ein Zuordnungsbetreiber für die Initialisierung verwendet.
Kennung und Referenz
Betrachten Sie das folgende Codesegment:
int ident = 4;
int & ref1 = ident;
int & ref2 = ident;
Cout<< ident <<"<< ref1 <<"<< ref2 << '\n';
Die Ausgabe ist:
4 4 4Identifizierung ist ein Kennung, während Ref1 und Ref2 Referenzen sind; Sie verweisen auf den gleichen Ort. Eine Referenz ist ein Synonym auf eine Kennung. Herkömmlicherweise sind Ref1 und Ref2 unterschiedliche Namen eines Objekts. Die Identifizierung kann jedoch weiterhin als Name des Objekts bezeichnet werden, was bedeutet, Identität, Ref1 und Ref2 denselben Ort nennen.
Der Hauptunterschied zwischen einer Kennung und einer Referenz besteht darin, dass bei der Übergabe als Argument an eine Funktion, wenn sie von der Kennung übergeben wird Funktion. Wenn Sie also an die Identifikator bestehen.
LVALUE -Referenz und RValue -Referenz
Die normale Möglichkeit, eine Referenz zu erstellen, ist wie folgt:
int ident;
Ident = 4;
int & ref = ident;
Der Speicher (Ressource) befindet sich und identifiziert zuerst (mit einem Namen wie Ident. Wenn Sie als Argument an eine Funktion übergeben, wird in der Funktion eine Kopie der Kennung vorgenommen, während für den Fall einer Referenz der ursprüngliche Ort in der Funktion verwendet (genannt) verwendet wird.
Heute ist es möglich, nur eine Referenz zu haben, ohne sie zu identifizieren. Dies bedeutet, dass es möglich ist, zuerst eine Referenz zu erstellen, ohne eine Kennung für den Ort zu haben. Dies verwendet &&, wie in der folgenden Anweisung gezeigt:
int && ref = 4;
Hier gibt es keine vorhergehende Identifizierung. Um auf den Wert des Objekts zuzugreifen, verwenden Sie einfach Ref, wie Sie die oben genannte Identität verwenden würden.
Mit der && Erklärung besteht keine Möglichkeit, ein Argument an eine Funktion durch Bezeichner weiterzugeben. Die einzige Entscheidung ist, mit Referenz zu bestehen. In diesem Fall wird innerhalb der Funktion nur ein Ort verwendet und nicht der zweite kopierte Ort wie bei einer Kennung.
Eine Referenzerklärung mit und wird als LVALUE -Referenz bezeichnet. Eine Referenzerklärung mit && heißt RValue -Referenz, die auch eine prValue -Referenz ist (siehe unten).
Zeiger
Betrachten Sie den folgenden Code:
int ptdint = 5;
int *ptrint;
ptrint = &ptdInt;
Cout<< *ptrInt <<'\n';
Die Ausgabe ist 5.
Hier ist PtDint eine Kennung wie das obige Ident. Hier gibt es zwei Objekte (Positionen) anstelle eines: das von PTDINT identifizierte ptdint- und das von PTRint identifizierte PTDint. & ptdint gibt die Adresse des spitzen Objekts zurück und setzt sie als Wert im Zeiger -Ptrint -Objekt ein. Um den Wert des spitzen Objekts zurückzugeben (.
Notiz: ptdint ist eine Kennung und keine Referenz, während der zuvor erwähnte Name Refus eine Referenz ist.
Die zweite und dritte Zeilen im obigen Code können auf eine Zeile reduziert werden, was zum folgenden Code führt:
int ptdint = 5;
int *ptrint = &ptdInt;
Cout<< *ptrInt <<'\n';
Notiz: Wenn ein Zeiger inkrementiert ist, weist er auf den nächsten Ort hin, was keine Zugabe des Wertes 1 ist. Wenn ein Zeiger dekrementiert ist, weist er auf den vorherigen Ort hin, der keine Subtraktion des Wertes 1 ist.
Kostenloser Laden
Ein Betriebssystem weist Speicher für jedes ausgeführte Programm zu. Ein Speicher, der keinem Programm zugeteilt wird, wird als kostenloser Laden bezeichnet. Der Ausdruck, der einen Ort für eine Ganzzahl aus dem kostenlosen Laden zurückgibt, ist:
Neu int
Dies gibt einen Ort für eine Ganzzahl zurück, die nicht identifiziert wird. Der folgende Code zeigt, wie der Zeiger mit dem kostenlosen Laden verwendet wird:
int *ptrint = new int;
*ptrint = 12;
Cout<< *ptrInt <<'\n';
Die Ausgabe ist 12.
Verwenden Sie zum Zerstören des Objekts den Ausdruck löschen wie folgt:
ptrint löschen;
Das Argument für den Ausdruck des Löschens ist ein Zeiger. Der folgende Code zeigt die Verwendung:
int *ptrint = new int;
*ptrint = 12;
ptrint löschen;
Cout<< *ptrInt <<'\n';
Die Ausgabe ist 0, und nicht so etwas wie null oder undefiniert. Delete ersetzt den Wert für den Ort durch den Standardwert des jeweiligen Typs des Standorts und ermöglicht dann den Speicherort zur Wiederverwendung. Der Standardwert für einen Int -Speicherort beträgt 0.
Eine Ressource wiederverwenden
In der Taxonomie der Ausdruckskategorie ist die Wiederverwendung einer Ressource die gleiche wie die Wiederverwendung eines Standorts oder Speichers für ein Objekt. Der folgende Code zeigt, wie ein Ort aus dem kostenlosen Laden wiederverwendet werden kann:
int *ptrint = new int;
*ptrint = 12;
Cout<< *ptrInt <<'\n';
ptrint löschen;
Cout<< *ptrInt <<'\n';
*ptrint = 24;
Cout<< *ptrInt <<'\n';
Die Ausgabe ist:
12Ein Wert von 12 wird zunächst dem nicht identifizierten Standort zugeordnet. Dann wird der Inhalt des Standorts gelöscht (theoretisch wird das Objekt gelöscht). Der Wert von 24 wird an denselben Ort zugewiesen.
Das folgende Programm zeigt, wie eine ganzzahlige Referenz von einer Funktion wiederverwendet wird:
#enthalten
Verwenden von Namespace STD;
int & fn ()
int i = 5;
int & j = i;
Rückkehr J;
int main ()
int & myInt = fn ();
Cout<< myInt <<'\n';
myint = 17;
Cout<< myInt <<'\n';
Rückkehr 0;
Die Ausgabe ist:
5Ein Objekt wie I, das in einem lokalen Bereich (Funktionsbereich) deklariert wurde, gibt es am Ende des örtlichen Bereichs nicht mehr zu existieren. Die Funktion fn () oben gibt jedoch die Referenz von i zurück. Durch diese zurückgegebene Referenz wiederverwendet der Name, MyInt in der Funktion main (), den von I für den Wert 17 identifizierten Ort.
lvalue
Ein Lvalue ist ein Ausdruck, dessen Bewertung die Identität eines Objekts, eines Bitfelds oder einer Funktion bestimmt. Die Identität ist eine offizielle Identität wie oben oder ein LVALUE -Referenzname, ein Zeiger oder der Name einer Funktion. Betrachten Sie den folgenden Code, der funktioniert:
int myint = 512;
int & myref = myint;
int* ptr = &myInt;
int fn ()
++ptr; -Ptr;
kehre myint zurück;
Hier ist myint ein lvalue; MyRef ist ein LVALUE -Referenzausdruck; *PTR ist ein LVALUE -Expression, da sein Ergebnis mit PTR identifizierbar ist; ++ PTR oder -PTR ist ein LVALUE -Expression, da sein Ergebnis mit dem neuen Zustand (Adresse) von PTR identifiziert werden kann, und FN ist ein LVALUE (Expression).
Betrachten Sie das folgende Codesegment:
int a = 2, b = 8;
int c = a + 16 + b + 64;
In der zweiten Aussage hat der Ort für 'a' 2 und ist durch 'a' identifiziert, ebenso wie ein LVALUE. Der Ort für B hat 8 und ist durch B identifizierbar, ebenso wie ein LVALUE. Der Ort für C hat die Summe und ist durch C identifizierbar, ebenso wie ein Lvalue. In der zweiten Aussage sind die Ausdrücke oder Werte von 16 und 64 Rvalues (siehe unten).
Betrachten Sie das folgende Codesegment:
char seq [5];
seq [0] = 'l', seq [1] = 'o', seq [2] = 'v', seq [3] = 'e', seq [4] = '\ 0';
Cout<< seq[2] <<'\n';
Die Ausgabe ist 'v';
SEQ ist ein Array. Der Ort für 'v' oder einen ähnlichen Wert im Array wird durch sek; i] identifiziert, wobei ich ein Index ist. Der Ausdruck, seq [i], ist also ein LVALUE -Ausdruck. SEQ, der die Kennung für das gesamte Array ist, ist ebenfalls ein LVALUE.
Prvalue
Ein PrValue ist ein Ausdruck, dessen Bewertung ein Objekt oder ein Bitfeld initialisiert oder den Wert des Operanden eines Operators berechnet, wie in dem Kontext angegeben, in dem er erscheint.
In der Aussage,
int myint = 256;
256 ist ein PrValue (PrValue Expression), der das von MyInt identifizierte Objekt initialisiert. Dieses Objekt wird nicht verwiesen.
In der Aussage,
int && ref = 4;
4 ist ein PrValue (PrValue Expression), der das von Ref verwiesene Objekt initialisiert. Dieses Objekt wird nicht offiziell identifiziert. Ref ist ein Beispiel für einen Referenzexpression oder eine prvalue -Referenzexpression; Es ist ein Name, aber keine offizielle Kennung.
Betrachten Sie das folgende Codesegment:
int ident;
Ident = 6;
int & ref = ident;
6 ist ein PrValue, der das durch Identität identifizierte Objekt initialisiert; Das Objekt wird auch durch Ref verwiesen. Hier ist der Schiedsrichter eine LVALUE -Referenz und keine prValue -Referenz.
Betrachten Sie das folgende Codesegment:
int a = 2, b = 8;
int c = a + 15 + b + 63;
15 und 63 sind jeweils eine Konstante, die sich selbst berechnet und einen Operand (in Bits) für den Additionsbetreiber erzeugt. 15 oder 63 ist also ein prValue -Ausdruck.
Jeder buchstäbliche, außer der Streichliteral, ist ein Prvalue (ich.e., ein prorue Ausdruck). Also ein wörtliches wie 58 oder 58.53 oder wahr oder falsch, ist ein Prvalue. Ein wörtliches kann verwendet werden, um ein Objekt zu initialisieren oder zu sich selbst (in eine andere Form in Bits) als Wert eines Operanden für einen Bediener zu berechnen. Im obigen Code initialisiert das literal 2 das Objekt a. Es berechnet sich auch als Operand für den Zuordnungsbetreiber.
Warum ist ein Saitenliteral kein Prorue?? Betrachten Sie den folgenden Code:
char st [] = "Liebe nicht Hass";
Cout << str <<'\n';
Cout << str[5] <<'\n';
Die Ausgabe ist:
Liebe nicht hassenST identifiziert die gesamte Zeichenfolge. Also ist der Ausdruck, Str und nicht das, was er identifiziert, ein Lvalue. Jedes Zeichen in der Zeichenfolge kann durch STR [i] identifiziert werden, wo ich ein Index ist. Der Ausdruck, Str [5] und nicht der Charakter, den es identifiziert, ist ein Lvalue. Das Streichliteral ist ein Lvalue und kein Prvalue.
In der folgenden Erklärung initialisiert ein Array -Literal das Objekt, arr:
ptrint ++ oder ptrint--
Hier ist Ptrint ein Zeiger auf einen ganzzahligen Ort. Der gesamte Ausdruck und nicht der endgültige Wert des Ortes, auf den er zeigt, ist ein PrValue (Ausdruck). Dies liegt daran. Andererseits ist -Ptrint oder -ptrint ein lvalue, da er den einzigen Wert des Interesses am Standort identifiziert. Eine andere Möglichkeit, es zu betrachten, ist, dass der ursprüngliche Wert den zweiten Endwert berechnet.
In der zweiten Anweisung des folgenden Code kann A oder B weiterhin als PrValue angesehen werden:
int a = 2, b = 8;
int c = a + 15 + b + 63;
A oder B in der zweiten Aussage ist also ein lvalue, weil es ein Objekt identifiziert. Es ist auch ein PrValue.
(Neue int), und nicht der Ort, den er festlegt. In der folgenden Anweisung wird die Absenderadresse des Speicherorts einem Zeigerobjekt zugewiesen:
int *ptrint = new int
Hier ist *ptrint ein lvalue, während (neu int) ein Prvalue ist. Denken Sie daran, ein Lvalue oder ein PrValue ist Ausdruck. (New int) identifiziert kein Objekt. Die Rückgabe der Adresse bedeutet nicht, das Objekt mit einem Namen zu identifizieren (z. B. Identität, oben). In *ptrint ist der Name, Ptrint, das Objekt wirklich, also ist *ptrint ein lvalue. Andererseits ist (New Int) ein PrValue, da er einen neuen Ort zu einer Adresse des Operandenwerts für den Zuordnungsbetreiber berechnet =.
XVALUE
Heute steht LVALUE für den Standortwert; Prvalue steht für „reines“ RValue (siehe, für welches RValue unten steht). Heute steht XValue für „läufige“ Lvalue.
Die Definition von XValue, zitiert aus der C ++ - Spezifikation, lautet wie folgt:
„Ein XValue ist ein Glvalue, der ein Objekt oder ein Bitfeld bezeichnet, dessen Ressourcen wiederverwendet werden können (normalerweise, weil es gegen Ende seines Lebens ist). [Beispiel: Bestimmte Arten von Ausdrücken, die RValue-Referenzen beinhalten, ergeben XValues, z
Dies bedeutet, dass sowohl Lvalue als auch Prvalue ausfallen können. Der folgende Code (kopiert von oben) zeigt, wie der Speicher (Ressource) des LVALUE *PTRINT wiederverwendet wird, nachdem er gelöscht wurde.
int *ptrint = new int;
*ptrint = 12;
Cout<< *ptrInt <<'\n';
ptrint löschen;
Cout<< *ptrInt <<'\n';
*ptrint = 24;
Cout<< *ptrInt <<'\n';
Die Ausgabe ist:
12Das folgende Programm (kopiert von oben) zeigt, wie die Speicherung einer Ganzzahlreferenz, bei der es sich um eine von einer Funktion zurückgegebene LVALUE -Referenz handelt, in der Funktion main () wiederverwendet wird:
#enthalten
Verwenden von Namespace STD;
int & fn ()
int i = 5;
int & j = i;
Rückkehr J;
int main ()
int & myInt = fn ();
Cout<< myInt <<'\n';
myint = 17;
Cout<< myInt <<'\n';
Rückkehr 0;
Die Ausgabe ist:
5Wenn ein Objekt wie ich in der FN () -Funktion aus dem Umfang ausgeht, wird es natürlich zerstört. In diesem Fall wurde die Speicherung von Ich in der Haupt- () -Funktion immer noch wiederverwendet.
Die obigen zwei Code-Beispiele veranschaulichen die Wiederverwendung der Speicherung von Lvalues. Es ist möglich, eine Speicherverwendung von Prvalues (Rvalues) zu erhalten (siehe später).
Das folgende Zitat zu XValue stammt aus der C ++ - Spezifikation:
„Im Allgemeinen ist die Auswirkung dieser Regel, dass benannte RValue -Referenzen als LVALUES behandelt werden und nicht genannte RValue -Verweise auf Objekte als XVALUES behandelt werden. RValue -Verweise auf Funktionen werden als lvalues behandelt, ob benannt oder nicht." (später sehen).
Ein XValue ist also ein LVALUE oder ein PrValue, dessen Ressourcen (Speicher) wiederverwendet werden können. XVALUES ist der Schnittsatz von Lvalues und Prvalues.
XValue gibt mehr als das, was in diesem Artikel angesprochen wurde. XValue verdient jedoch einen ganzen Artikel selbst, und so werden die zusätzlichen Spezifikationen für XValue in diesem Artikel nicht behandelt.
Ausdruckskategorie Taxonomie festgelegt
Ein weiteres Zitat aus der C ++ - Spezifikation:
“Notiz: Historisch gesehen waren Lvalues und Rvalues so genannt, weil sie auf der linken und rechten Seite einer Aufgabe erscheinen konnten (obwohl dies im Allgemeinen nicht mehr wahr ist); Glvalues sind "verallgemeinerte" Lvalues, Prvalues sind "reine" Rvalues, und Xwerues "läuft" Lvalues "ausgeläuft". Trotz ihrer Namen klassifizieren diese Begriffe Ausdrücke, nicht Werte. - Endnotiz ”
Also, Glvalues ist die Vereinigung von Lvalues und XValues und Rvalues, die Union von XValues und Prvalues sind die Vereinigung. XVALUES ist der Schnittsatz von Lvalues und Prvalues.
Ab sofort wird die Taxonomie der Ausdruckskategorie besser mit einem Venn -Diagramm veranschaulicht wie folgt:
Abschluss
Ein Lvalue ist ein Ausdruck, dessen Bewertung die Identität eines Objekts, eines Bitfelds oder einer Funktion bestimmt.
Ein PrValue ist ein Ausdruck, dessen Bewertung ein Objekt oder ein Bitfeld initialisiert oder den Wert des Operanden eines Operators berechnet, wie in dem Kontext angegeben, in dem er erscheint.
Ein XValue ist ein Lvalue oder ein Prvalue mit der zusätzlichen Eigenschaft, dass seine Ressourcen (Speicher) wiederverwendet werden können.
Die C ++ - Spezifikation zeigt die Taxonomie der Expressionskategorie mit einem Baumdiagramm, was darauf hinweist, dass die Taxonomie eine gewisse Hierarchie gibt. Ab sofort gibt es keine Hierarchie in der Taxonomie, daher wird ein Venn -Diagramm von einigen Autoren verwendet, da es die Taxonomie besser veranschaulicht.