Wenn der C ++ - Compiler sehr ähnlichen Assembler-Code für eine Referenz und einen Zeiger generiert, warum werden Referenzen im Vergleich zu Zeigern bevorzugt (und als sicherer angesehen)?
Ich habe
gesehenEDIT-1:
Ich habe mir den Assembler-Code angeschaut, der von g ++ für dieses kleine Programm erzeugt wurde:
%Vor%Es wird als sicherer angesehen, weil viele Leute "gehört" haben, dass es sicherer ist, und dann anderen gesagt hat, die jetzt auch "gehört" haben, dass es sicherer ist.
Nicht eine einzige Person, die Referenzen versteht, wird Ihnen sagen, dass sie sicherer sind als Zeiger, sie haben die gleichen Fehler und das Potenzial, ungültig zu werden.
z.B.
%Vor% EDIT: Da es einige Verwirrung darüber gibt, ob eine Referenz ein Alias für ein Objekt ist oder an einen Speicherort gebunden ist, hier der Absatz aus dem Standard (Entwurf 3225, Abschnitt [basic.life]
), der eindeutig eine Referenz angibt bindet an Speicher und kann das Objekt überleben, das beim Erstellen der Referenz vorhanden war:
Wenn die Lebensdauer eines Objekts abgelaufen ist und bevor der Speicher, den das Objekt belegt hat, wiederverwendet wird oder freigegeben, wird an dem Speicherort, den das ursprüngliche Objekt belegt hat, ein neues Objekt erzeugt, ein Zeiger, der verweist auf das ursprüngliche Objekt, eine Referenz, die sich auf das ursprüngliche Objekt bezieht, oder den Namen des Originals Objekt wird automatisch auf das neue Objekt verweisen und kann, sobald die Lebensdauer des neuen Objekts begonnen hat, verwendet werden, um das neue Objekt zu manipulieren, wenn:
- der Speicher für das neue Objekt überlagert exakt den Speicherort, den das ursprüngliche Objekt belegt hat, und
- Das neue Objekt ist vom selben Typ wie das ursprüngliche Objekt (ignoriert die cv-Qualifizierer auf oberster Ebene) und
- Der Typ des ursprünglichen Objekts ist nicht konsti-quali fi ziert und enthält bei einem Klassentyp keinen nicht-statischen Typ Datenelement, dessen Typ const-quali fi ziert ist, oder ein Referenztyp und
- Das ursprüngliche Objekt war das am meisten abgeleitete Objekt vom Typ
T
und das neue Objekt ist das am meisten abgeleitete Objekt vom TypT
(dh es handelt sich nicht um Unterobjekte der Basisklasse).
Es hängt davon ab, wie Sie "sicherer" definieren.
Der Compiler lässt Sie nicht eine nicht initialisierte Referenz erstellen, oder eine, die auf NULLness zeigt, und es wird nicht zulassen, dass Sie Ihre Referenz versehentlich auf eine andere Stelle verweisen, während Sie sie verwenden. Diese strengeren Regeln bedeuten auch, dass Ihr Compiler Ihnen mehr Warnungen vor häufigen Fehlern geben kann, während es bei Zeigern nie wirklich sicher ist, ob Sie für das, was Sie getan haben, verwenden oder nicht.
Andererseits macht es die Transparenz der Syntax - nämlich, was Alexandre C. darüber gesagt hat, wie es auf der Call-Site aussieht, um ein Argument als Referenz zu übergeben - es ganz einfach nicht zu erkennen, dass du eine Referenz passierst. Folglich werden Sie möglicherweise nicht erkennen, dass Sie Besitz und Lebensdauer des Arguments beibehalten sollen oder dass Ihr Argument dauerhaft geändert werden kann.
Weil Verweise immer initialisiert werden müssen und da sie auf ein existierendes Objekt verweisen müssen, ist es viel schwieriger (aber keineswegs unmöglich), mit dangling Referenzen zu enden, als mit nicht initialisierten / dangling Pointern. Außerdem ist es einfacher, Verweise zu manipulieren, da Sie sich keine Gedanken darüber machen müssen, Adressen zu nehmen und sie zu dereferenzieren.
Aber um Ihnen zu zeigen, dass eine Referenz allein Ihr Programm nicht zu 100% sicher macht, sollten Sie folgendes beachten:
%Vor%Oder das:
%Vor%Eine Referenz wird immer von einem vorhandenen Objekt initialisiert, daher kann sie niemals NULL sein, während eine Zeigervariable NULL sein darf.
BEARBEITEN: Vielen Dank für alle Antworten. Ja, eine Referenz kann tatsächlich auf Müll hinweisen, ich habe vergessen, Referenzen zu hängen.
Es ist ein wenig sicherer, aber nicht das Gleiche. Beachten Sie, dass Sie die gleichen Probleme mit "dangling references" haben wie mit "dangling pointers". Wenn Sie z. B. einen Verweis von einem Objekt mit einem Bereich zurückgeben, erhalten Sie ein nicht definiertes Verhalten, genau wie Zeiger:
%Vor%Der einzige Vorteil ist, dass Sie keine NULL-Referenz erstellen erstellen können. Selbst wenn Sie es versuchen:
%Vor%Als Klassenmitglieder werden Zeiger bevorzugt, da sie eine bessere Zuweisungssemantik haben: Sie können eine Referenz nicht neu zuweisen, wenn sie initialisiert wurde. Der Compiler kann keinen Standardzuweisungsoperator bereitstellen, wenn Referenzmember in der Klasse vorhanden sind.
Daher kann C ++ Zeiger nicht loswerden, und Sie können sie frei verwenden: Indem Sie Argumente als Zeiger und nicht als (nicht konstante) Verweise übergeben, machen Sie an der Aufrufstelle deutlich, dass das Objekt geändert wird. Dies kann ein wenig Sicherheit hinzufügen, da Sie mit bloßem Auge sehen, welche Funktionen Objekte verändern.
Ich spiele ein wenig den Anwalt des Teufels, aber Referenzen sind leicht zu missbrauchen.
Nun, die Antwort, auf die Sie hinweisen, beantwortet das. Aus der Sicht "sicherer" denke ich, dass es im Grunde schwer ist, einen Code wie folgt zu schreiben:
%Vor%Als Referenz wird immer initialisiert und
%Vor%Aber wie in der Frage gesagt, die Sie erwähnen, ist das nicht nur, weil sie sicherer sind, dass Referenzen verwendet werden ...
my2c
Angenommen, Sie hätten in der Sprache nicht den Referenzoperator <type>&
. Dann, wann immer Sie einen Verweis auf ein Objekt an eine Funktion übergeben wollten, müssten Sie tun, was in C getan wurde, das Argument als &<myobject>
übergeben und in der Funktion als Zeigerparameter <type>* p
erhalten. Dann müssten Sie sich selbst disziplinieren nicht , um etwas wie p++
zu tun.
Hier kommt die Sicherheit ins Spiel. Das Verweisen auf Referenz ist sehr nützlich, und wenn Sie den Referenzoperator verwenden, vermeiden Sie das Risiko, dass Sie den Zeiger ändern.
(Vielleicht würde ein Const-Zeiger dasselbe bewirken, aber Sie müssen zugeben, dass &
sauberer ist. Und es kann auf andere Variablen außer Parametern angewendet werden.)
Ein Zeiger ist eine unabhängige Variable, die so zugeordnet werden kann, dass sie auf ein anderes Datumselement, nicht-entialisierten Speicher oder gar keine Stelle verweist (NULL). Ein Zeiger kann inkrementiert, dekrementiert, von einem anderen Zeiger des gleichen Typs usw. subtrahiert werden. Eine Referenz ist an eine existierende Variable gebunden und ist einfach ein Alias für den Variablennamen.