Ich weiß, dass Referenzen syntaktischer Zucker sind, damit der Code leichter zu lesen und zu schreiben ist.
Aber was ist der Unterschied zwischen einer Zeigervariablen und einer Referenzvariablen?
Ich weiß, dass Referenzen syntaktischer Zucker sind, damit der Code leichter zu lesen und zu schreiben ist.
Aber was ist der Unterschied zwischen einer Zeigervariablen und einer Referenzvariablen?
Obwohl sowohl Referenzen als auch Zeiger verwendet werden, um indirekt auf einen anderen Wert zuzugreifen, gibt es zwei wichtige Unterschiede zwischen Referenzen und Zeigern. Der erste ist, dass ein Verweis immer auf ein Objekt verweist: Es ist ein Fehler, eine Referenz zu definieren, ohne sie zu initialisieren. Der zweite wichtige Unterschied ist das Verhalten bei der Zuweisung: Die Zuweisung an eine Referenz ändert das Objekt, an das die Referenz gebunden ist; sie bindet die Referenz nicht an ein anderes Objekt neu. Einmal initialisiert, bezieht sich eine Referenz immer auf dasselbe zugrunde liegende Objekt.
Betrachten Sie diese beiden Programmfragmente. Im ersten weisen wir einen Zeiger einem anderen zu:
int ival = 1024, ival2 = 2048;
int *pi = &ival, *pi2 = &ival2;
pi = pi2; // pi now points to ival2
Nach der Zuweisung, ival, bleibt das durch pi adressierte Objekt unverändert. Die Zuweisung ändert den Wert von pi, so dass er auf ein anderes Objekt zeigt. Betrachten wir nun ein ähnliches Programm, das zwei Referenzen zuweist:
int &ri = ival, &ri2 = ival2;
ri = ri2; // assigns ival2 to ival
Diese Zuweisung ändert ival, den von ri referenzierten Wert, und nicht die Referenz selbst. Nach der Zuweisung verweisen die beiden Referenzen immer noch auf ihre ursprünglichen Objekte, und der Wert dieser Objekte ist nun ebenfalls derselbe.
Eine Referenz ist ein Alias für eine andere Variable, während ein Zeiger die Speicheradresse einer Variablen enthält. Referenzen werden im Allgemeinen als Funktionsparameter verwendet, so dass das übergebene Objekt nicht die Kopie, sondern das Objekt selbst ist.
void fun(int &a, int &b); // A common usage of references.
int a = 0;
int &b = a; // b is an alias for a. Not so common to use.
Was ist eine Referenz in C++? Eine bestimmte Instanz eines Typs, die kein Objekttyp ist .
Was ist ein Zeiger in C++? Eine bestimmte Instanz eines Typs, die ist ein Objekttyp .
De die ISO-C++-Definition des Objekttyps :
Eine Objekt Typ ist ein (möglicherweise cv -qualifizierter) Typ, der weder ein Funktionstyp noch ein Referenztyp ist, und nicht cv nichtig.
Es ist vielleicht wichtig zu wissen, dass der Objekttyp eine Top-Level-Kategorie des Typuniversums in C++ ist. Referenz ist ebenfalls eine Kategorie der obersten Ebene. Aber Zeiger ist nicht.
Zeiger und Verweise werden zusammen erwähnt im Rahmen von Verbindungstyp . Dies liegt im Wesentlichen an der von C geerbten (und erweiterten) Syntax des Deklarators, der keine Referenzen kennt. (Außerdem gibt es seit C++ 11 mehr als eine Art von Deklarator für Referenzen, während Zeiger immer noch "unityped" sind: &
+ &&
vs. *
.) Daher ist es in diesem Zusammenhang durchaus sinnvoll, eine Sprache zu entwerfen, die eine "Erweiterung" im Stil von C darstellt. (Ich werde immer noch argumentieren, dass die Syntax der Deklaratoren die syntaktische Ausdruckskraft verschwendet viel macht sowohl menschliche Benutzer als auch Implementierungen frustriert. Daher sind nicht alle von ihnen qualifiziert zu sein eingebaut in einem neuen Sprachdesign. Dies ist jedoch ein völlig anderes Thema über PL-Design).
Ansonsten ist es unbedeutend, dass Zeiger als eine bestimmte Art von Typen mit Verweisen zusammen qualifiziert werden können. Sie haben einfach zu wenig gemeinsame Eigenschaften außer der Ähnlichkeit der Syntax, so dass es in den meisten Fällen keinen Grund gibt, sie zusammenzufassen.
Beachten Sie, dass in den obigen Ausführungen nur "Zeiger" und "Referenzen" als Typen genannt werden. Es gibt einige interessierte Fragen über ihre Instanzen (wie Variablen). Es gibt auch zu viele Missverständnisse.
Aus den Unterschieden der Oberkategorien lassen sich bereits viele konkrete Unterschiede ableiten, die nicht direkt mit Zeigern zusammenhängen:
cv
Qualifikanten. Referenzen können nicht.Ein paar weitere Sonderregeln für Referenzen:
&&
Parameter (als "Weiterleitungsreferenzen") auf der Grundlage der Referenzkollabierung während der Ableitung der Vorlagenparameter ermöglichen "perfekte Weiterleitung" der Parameter.std::initializer_list
folgt einigen ähnlichen Regeln der Verlängerung der Referenzlebensdauer. Das ist ein weiteres Wespennest.Ich weiß, dass Referenzen syntaktischer Zucker sind, damit der Code leichter zu lesen und zu schreiben ist.
Technisch gesehen ist dies schlichtweg falsch. Referenzen sind kein syntaktischer Zucker anderer Funktionen in C++, denn sie können nicht genau durch andere Funktionen ohne semantische Unterschiede ersetzt werden.
(Ähnlich, lambda-Ausdruck s sind no syntaktischer Zucker aller anderen Funktionen in C++, weil sie nicht genau mit "unspezifizierten" Eigenschaften wie die Reihenfolge der Deklaration der erfassten Variablen was wichtig sein kann, weil die Reihenfolge der Initialisierung solcher Variablen von Bedeutung sein kann).
C++ hat nur wenige Arten von syntaktischen Zuckern in diesem strengen Sinne. Ein Beispiel ist der (von C geerbte) eingebaute (nicht überladene) Operator []
die ist genau so definiert, dass es dieselben semantischen Eigenschaften spezifischer Kombinationsformen über den eingebauten Operator unary *
und binär +
.
Ein Zeiger und ein Verweis verwenden also beide die gleiche Menge an Speicher.
Die obige Aussage ist schlichtweg falsch. Um solche Missverständnisse zu vermeiden, sollten Sie sich stattdessen die ISO-C++-Regeln ansehen:
De [intro.object]/1 :
... Ein Objekt belegt einen Speicherbereich in seiner Entstehungszeit, während seiner gesamten Lebensdauer und in der Zeit seiner Zerstörung. ...
De [dcl.ref]/4 :
Es ist nicht spezifiziert, ob eine Referenz gespeichert werden muss oder nicht.
Beachten Sie, dass es sich um semantisch Eigenschaften.
Selbst wenn Zeiger nicht qualifiziert genug sind, um mit Referenzen im Sinne des Sprachdesigns zusammengelegt zu werden, gibt es immer noch einige Argumente, die es fragwürdig machen, in einigen anderen Zusammenhängen eine Wahl zwischen ihnen zu treffen, z.B. wenn es um die Wahl von Parametertypen geht.
Aber das ist nicht die ganze Geschichte. Ich meine, es gibt mehr Dinge als Zeiger vs. Referenzen, die Sie berücksichtigen müssen.
Wenn Sie sich nicht an solch übermäßig spezifische Auswahlmöglichkeiten halten müssen, ist die Antwort in den meisten Fällen kurz: Sie brauchen keine Zeiger zu verwenden, also brauchen Sie auch keine . Zeiger sind in der Regel schlecht genug, weil sie zu viele Dinge implizieren, die man nicht erwartet, und sich auf zu viele implizite Annahmen stützen, die die Wartbarkeit und (sogar) Portabilität des Codes untergraben. Unnötigerweise auf Zeiger zu setzen, ist definitiv ein schlechter Stil und sollte im Sinne von modernem C++ vermieden werden. Überdenken Sie Ihr Ziel und Sie werden feststellen, dass Zeiger ist das Merkmal der letzten Art in den meisten Fällen.
&
Referenztyp als 1. Parametertyp. (Und normalerweise sollte es sein const
qualifiziert).&&
Referenztyp als 1. Parametertyp. (Und normalerweise sollte es keine Qualifier geben.)operator=
als spezielle Mitgliedsfunktionen erfordern Referenztypen ähnlich dem 1. Parameter von Kopier-/Verschiebekonstruktoren.++
erfordert Dummy int
.unique_ptr
y shared_ptr
(oder sogar mit selbstgebauten, wenn Sie diese benötigen, um undurchsichtig ), statt roher Zeiger.std::optional
anstelle von rohen Zeigern.observer_ptr
in der Bibliothek Fundamental TS.Die einzigen Ausnahmen können in der derzeitigen Sprache nicht umgangen werden:
operator new
. (Allerdings, cv - void*
ist immer noch ganz anders und sicherer als gewöhnliche Objektzeiger, weil es unerwartete Zeigerarithmetik ausschließt, es sei denn, Sie verlassen sich auf eine nicht konforme Erweiterung von void*
wie die von GNU).In der Praxis ist die Antwort also so offensichtlich: im Zweifelsfall Zeiger vermeiden . Sie müssen Zeiger nur dann verwenden, wenn es ganz eindeutige Gründe dafür gibt, dass nichts anderes besser geeignet ist. Abgesehen von einigen oben erwähnten Ausnahmefällen sind solche Entscheidungen fast immer nicht rein C++-spezifisch (aber wahrscheinlich sprachimplementierungsspezifisch). Solche Fälle können sein:
Wenn Sie sich die Frage über einige Google-Suchergebnisse (nicht spezifisch für C++) ist dies höchstwahrscheinlich der falsche Ort.
Referenzen in C++ sind ziemlich "seltsam", da sie im Wesentlichen nicht erstklassig sind: sie werden als die Objekte oder die Funktionen behandelt, auf die verwiesen wird daher haben sie keine Chance, einige erstklassige Operationen zu unterstützen, wie z.B. den linken Operanden von der Betreiber des Mitgliederzugangs unabhängig vom Typ des referenzierten Objekts. Andere Sprachen können ähnliche Beschränkungen für ihre Referenzen haben oder auch nicht.
Referenzen in C++ behalten wahrscheinlich nicht die Bedeutung in verschiedenen Sprachen. Zum Beispiel implizieren Referenzen im Allgemeinen keine Nulleigenschaften für Werte wie in C++, so dass solche Annahmen in einigen anderen Sprachen nicht funktionieren (und Sie werden ziemlich leicht Gegenbeispiele finden, z. B. Java, C#, ...).
Es kann noch einige gemeinsame Eigenschaften zwischen Referenzen in verschiedenen Programmiersprachen im Allgemeinen geben, aber lassen wir das für andere Fragen in SO.
(Eine Randbemerkung: Die Frage kann schon bedeutend sein, bevor irgendwelche "C-ähnlichen" Sprachen beteiligt sind, wie ALGOL 68 vs. PL/I .)
Dies basiert auf dem Lehrgang . Was geschrieben steht, macht es deutlicher:
>>> The address that locates a variable within memory is
what we call a reference to that variable. (5th paragraph at page 63)
>>> The variable that stores the reference to another
variable is what we call a pointer. (3rd paragraph at page 64)
Einfach, um sich daran zu erinnern,
>>> reference stands for memory location
>>> pointer is a reference container (Maybe because we will use it for
several times, it is better to remember that reference.)
Was mehr ist, wie wir fast jeder Zeiger Tutorial beziehen können, ist ein Zeiger ein Objekt, das durch Zeiger Arithmetik unterstützt wird, die Zeiger ähnlich wie ein Array macht.
Sehen Sie sich die folgende Aussage an,
int Tom(0);
int & alias_Tom = Tom;
alias_Tom
kann verstanden werden als eine alias of a variable
(anders als bei typedef
das ist alias of a type
) Tom
. Es ist auch OK zu vergessen, dass die Terminologie einer solchen Aussage darin besteht, einen Verweis auf Tom
.
Und wenn eine Klasse eine Referenzvariable hat, sollte sie entweder mit einem nullptr oder einem gültigen Objekt in der Initialisierungsliste initialisiert werden.
Die Formulierung in dieser Antwort ist zu verwirrend, als dass sie wirklich von Nutzen sein könnte. Außerdem, @Misgevolution, empfehlen Sie den Lesern ernsthaft, eine Referenz mit einer nullptr
? Haben Sie eigentlich irgendeinen anderen Teil dieses Threads gelesen, oder...?
Es spielt keine Rolle, wie viel Speicherplatz er beansprucht, da man (ohne Code auszuführen) nicht sehen kann, welchen Nebeneffekt der beanspruchte Speicherplatz hat.
Ein wesentlicher Unterschied zwischen Referenzen und Zeigern besteht jedoch darin, dass temporäre Elemente, die const-Referenzen zugewiesen sind, so lange leben, bis die const-Referenz den Gültigkeitsbereich verlässt.
Zum Beispiel:
class scope_test
{
public:
~scope_test() { printf("scope_test done!\n"); }
};
...
{
const scope_test &test= scope_test();
printf("in scope\n");
}
wird gedruckt:
in scope
scope_test done!
Dies ist der Sprachmechanismus, der die Arbeit von ScopeGuard ermöglicht.
Die Adresse eines Verweises kann nicht übernommen werden, aber das bedeutet nicht, dass sie keinen Platz beanspruchen. Wenn man von Optimierungen absieht, können sie das sehr wohl.
Ungeachtet der Auswirkungen ist die Aussage "Eine Referenz auf dem Stapel nimmt überhaupt keinen Platz ein" offensichtlich falsch.
@Tomalak, nun, das hängt auch vom Compiler ab. Aber ja, das zu sagen ist ein bisschen verwirrend. Ich vermute, es wäre weniger verwirrend, das einfach zu entfernen.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
1 Stimmen
Ein lokaler Verweis (d.h. ein Verweis, der nicht in einer Struktur oder Klasse enthalten ist) weist nicht unbedingt Speicherplatz zu. Das erkennen Sie an dem Unterschied zwischen sizeof(int &) und sizeof(struct { int &x; }).
136 Stimmen
Ich denke, Punkt 2 sollte lauten: "Ein Zeiger darf NULL sein, eine Referenz jedoch nicht. Nur fehlerhafter Code kann eine NULL-Referenz erzeugen und ihr Verhalten ist undefiniert."
32 Stimmen
Zeiger sind nur ein weiterer Objekttyp, und wie jedes Objekt in C++ können sie eine Variable sein. Referenzen hingegen sind niemals Objekte, nur Variablen.
31 Stimmen
Dies kompiliert ohne Warnungen:
int &x = *(int*)0;
auf gcc. Referenz kann tatsächlich auf NULL zeigen.7 Stimmen
Ich bin mir nicht sicher, ob ich zustimme, dass "Referenzen syntaktischer Zucker" sind. Wie würden Sie Kopierkonstruktoren in Ihrer Sprache entwerfen, wenn Sie keine Referenzen hätten?
1 Stimmen
Wahrscheinlich könnte man die Adresse eines Verweises nehmen, wie es bei der Implementierung des Zuweisungsoperators üblich ist, d. h.
T& T::operator=(const T& rhs) { if (this == &rhs) return *this; ... }
.2 Stimmen
Auch hilfreich lien
9 Stimmen
Ich fühle mich nicht qualifiziert, den Teil "Klärung eines Missverständnisses" dieser Frage zu bearbeiten, aber ich denke, es sollte beachtet werden, dass, wenn
int i
ein ganzes Leben lang in einem Register lebt, ist der Verweis wahrscheinlich kein Zeiger - er kann einfach ein Alias für das gleiche Register sein.29 Stimmen
Referenz ist ein Variablen-Alias
4 Stimmen
Referenzen sind nicht nur syntaktischer Zucker: Sie befinden sich IMMER in einem definierten Zustand. Dies wird durch den Compiler erzwungen. Zeiger haben keine solche Garantie.
26 Stimmen
Mir gefällt, dass schon der erste Satz ein völliger Irrtum ist. Referenzen haben ihre eigene Semantik.
9 Stimmen
@Calmarius Nein, das ist nicht richtig. Das ist ein Verweis auf das, was sich an dieser Speicherstelle befindet. Der Verweis selbst ist gültig. Der Zugriff auf das, was sich dort befindet, ist jedoch undefiniert.
17 Stimmen
@Calmarius natürlich, um überhaupt dorthin zu gelangen, haben Sie einen Null-Zeiger dereferenziert. der selbst undefiniert ist. . .
1 Stimmen
Diese Frage zeigt eine Zeigerarithmetik, die mit einer Referenz ("
&obj + 5
"). Verstößt dies nicht gegen die Aliasing-Annahmen, bei denen die aufrufende Seite davon ausgeht, dass eine aufgerufene Funktion keine solche Arithmetik durchführt. Das bedeutet, dass die Referenzarithmetik UB ist?1 Stimmen
Ein Zeiger speichert grundsätzlich die Adresse seines Zeigers.
5 Stimmen
Int *p = NULL; int &r=*p; Referenz zeigt auf NULL; if(r){} -> boOm ;)
1 Stimmen
@QiangXu Nein, das nimmt die Adresse des Ziels der Referenz. Lies es noch einmal: Eine Referenz ist ein Alias auf ein anderes Objekt. (Abgesehen von der Erstellungsphase) ist die Semantik völlig identisch mit der, wenn Sie den ursprünglichen Namen des Ziels verwenden.
1 Stimmen
Ich würde nicht sagen, dass Referenzen als Zeiger in C++ implementiert sind, es sind zwei verschiedene Abstraktionen für Adresse eines Objekts. Denken Sie daran, wie die Hardware funktioniert: In der Hardware gibt es keine "Zeiger", sondern Register, Speicher und Anweisungen, die mit den Registern und dem Speicher arbeiten.
2 Stimmen
Ich stelle mir gerne vor, dass Variablen Menschen auf einer Party sind, dann eine Zeiger ist jemand, der normalerweise mit dem Finger auf jemand anderen zeigt, und wer das ist, kann geändert werden, und in einigen Fällen verlässt entweder der andere die Partei und kann nicht mehr auf ihn gezeigt werden (außerhalb des Geltungsbereichs) oder er wird ermordet (zerstört, es ist ein schlecht Party!), bei anderen Gelegenheiten kann der Zeiger Person auf eine neue Person (Wert geändert) oder den Boden (auf 0,
nullptr
usw.). Im Vergleich dazu ist eine Referenz variabel ist ein neues Namensschild, das sich eine Person selbst anlegen kann (und das auch ein Zeiger verwenden kann!)2 Stimmen
a pointer and a reference both occupy the same amount of memory
- Das ist falsch. Ein Zeiger auf eine virtuelle Funktion kann mehr Speicherplatz belegen als eine Referenz.4 Stimmen
Bin ich der Einzige, der der Meinung ist, dass Zeiger leichter zu lesen sind und den Code besser verdeutlichen als Referenzen?
1 Stimmen
Ein sehr wichtiger Punkt: Der Programmierer muss den Speicher von Zeigern verwalten, aber nicht den von Referenzen.
2 Stimmen
Ein Zeiger kann null sein, d.h. einen Wert annehmen, der eindeutig auf kein gültiges Objekt oder keine gültige Funktion zeigt. Andererseits kann eine Null-Referenz in einem wohldefinierten Programm nicht existieren, da die einzige Möglichkeit, eine solche Referenz zu erzeugen, darin bestünde, sie an das Objekt oder die Funktion zu binden, das/die durch Dereferenzierung eines Null-Zeigers erhalten wurde, was zu undefiniertem Verhalten führt.
0 Stimmen
fbb-git.github.io/cppannotations/cppannotations/html/ Könnte auch von Interesse sein und möglicherweise auch fbb-git.github.io/cppannotations/cppannotations/html/ - aus The C++ Annotations, das für C-Programmierer nützlich ist - was auch immer es wert sein mag, ist vielleicht für einige von Interesse. Ich persönlich hatte mehrere sehr schlechte Reaktionen auf C++ - das Aussehen ist für mich in höchstem Maße unästhetisch, und das ist nur ein Problem von vielen anderen ... also werde ich nicht in der Lage sein, mehr als die Links hinzuzufügen, aber vielleicht sind sie für einige nützlich oder interessant.
2 Stimmen
Ist keine der Antworten eine Lösung für Ihre Frage? Wenn nicht, bin ich sehr neugierig zu erfahren, warum.