C ++ - Objekte: Wann sollte ich Zeiger oder Referenz verwenden?

8

Ich kann ein Objekt als Zeiger auf es oder seine Referenz verwenden. Ich verstehe, dass der Unterschied darin besteht, dass Zeiger manuell gelöscht werden müssen und Verweise bleiben, bis sie nicht mehr im Geltungsbereich sind.

Wann sollte ich jedes von ihnen benutzen? Was ist der praktische Unterschied?

Keine dieser Fragen hat meine Zweifel beantwortet:

rafaelxy 26.11.2010, 19:23
quelle

9 Antworten

17

Eine Referenz ist im Grunde ein Zeiger mit Einschränkungen (muss bei der Erstellung gebunden sein, kann nicht rebound / null sein). Wenn es sinnvoll ist, dass der Code diese Einschränkungen verwendet, kann der Compiler Sie mithilfe eines Verweises anstelle eines Zeigers davor warnen, dass er sie versehentlich verletzt.

Es ist sehr ähnlich wie der const -Qualifikator: Die Sprache könnte ohne sie existieren, sie ist nur als eine Art Bonus vorhanden, die es einfacher macht, sicheren Code zu entwickeln.

    
suszterpatt 26.11.2010, 19:32
quelle
8

"Zeiger, die ich löschen muss und auf die sie verweisen, bleiben bis zum Abschluss ihres Bereichs erhalten."

Nein, das ist völlig falsch.

Objekte, die mit new belegt sind, müssen gelöscht werden [*]. Objekte, die nicht mit new belegt sind, dürfen nicht gelöscht werden. Es ist möglich, einen Zeiger auf ein Objekt zu haben, das nicht mit new zugeordnet wurde, und es ist möglich, einen Verweis auf ein Objekt zu haben, das mit new zugeordnet wurde.

Ein Zeiger oder eine Referenz ist eine Möglichkeit, auf ein Objekt zuzugreifen, ist aber nicht das Objekt selbst und hat keinen Einfluss darauf, wie das Objekt erstellt wurde. Der konzeptionelle Unterschied besteht darin, dass eine Referenz ein Name für ein Objekt ist und ein Zeiger ein Objekt ist, das die Adresse eines anderen Objekts enthält. Die praktischen Unterschiede, wie Sie wählen, welche zu verwenden ist, beinhalten die Syntax jedes einzelnen und die Tatsache, dass Referenzen nicht null sein können und nicht wieder eingefügt werden können.

[*] mit delete . Ein Array mit new[] muss mit delete[] gelöscht werden. Es gibt Tools, die Ihnen helfen können, zugewiesene Ressourcen zu verfolgen und diese Anrufe für Sie zu erledigen, sogenannte Smart Pointer, so dass es ziemlich selten ist, den Anruf selbst zu tätigen, anstatt ihn einfach zu arrangieren, aber nichtsdestotrotz muss gemacht werden.

    
Steve Jessop 26.11.2010 20:51
quelle
3

Sie haben viele Situationen, in denen ein Parameter nicht existiert oder ungültig ist und dies kann von der Laufzeit-Semantik des Codes abhängen. In solchen Situationen können Sie einen Zeiger verwenden und auf NULL (0) setzen, um diesen Zustand zu signalisieren. Abgesehen davon,

  • Ein Zeiger kann einem neuen zugewiesen werden Zustand. Eine Referenz kann nicht. Das ist in einigen Situationen wünschenswert.
  • Ein Zeiger hilft bei der Übertragung der Ownership-Semantik. Dies ist besonders nützlich in einer Multithread-Umgebung, wenn der Parameterstatus zum Ausführen in verwendet wird ein separater Thread und Sie pollen normalerweise nicht, bis der Thread beendet wurde. Jetzt kann der Thread es löschen.
Abhay 26.11.2010 19:32
quelle
3

suszterpatt hat schon eine gute Erklärung gegeben. Wenn Sie eine Faustregel haben möchten, die leicht zu merken ist, würde ich Folgendes vorschlagen:

Wenn möglich, verwenden Sie Verweise, wenn Sie sie nicht vermeiden können.

Noch kürzer: Referenzen über Zeiger bevorzugen.

    
SebastianK 26.11.2010 20:22
quelle
3

Hier ist eine andere Antwort (vielleicht hätte ich die erste editieren sollen, aber da sie einen anderen Fokus hat, dachte ich, es wäre in Ordnung, sie getrennt zu haben).

Wenn Sie einen Zeiger mit new erstellen, ist der Speicher dafür reserviert und bleibt bestehen, bis Sie delete aufrufen - aber die Lebensdauer des Bezeichners ist immer noch auf das Ende des Codeblocks beschränkt. Wenn Sie Objekte in einer Funktion erstellen und an eine externe Liste anhängen, können die Objekte nach dem Zurückkehren der Funktion sicher im Speicher verbleiben und Sie können immer noch ohne den Bezeichner darauf verweisen.

Hier ist ein (vereinfachtes) Beispiel von Umbra, einem C ++ Framework, das ich entwickle. Es gibt eine Liste von Modulen (Zeiger auf Objekte), die in der Engine gespeichert sind. Die Engine kann ein Objekt an diese Liste anhängen:

%Vor%

Abrufen eines:

%Vor%

Jetzt kann ich Module hinzufügen und erhalten, ohne ihre Kennungen zu kennen:

%Vor%

Die Liste der Module bleibt während der gesamten Laufzeit des Programms bestehen, ich muss mich nicht um die Lebensdauer der Module kümmern, wenn sie mit new :) instanziiert werden. Das ist es also - mit Zeigern können Sie langlebige Objekte haben, die niemals einen Bezeichner (oder einen Namen, wenn Sie so wollen) benötigen, um sie zu referenzieren:).

Ein weiteres schönes, aber sehr einfaches Beispiel ist das:

%Vor%     
mingos 26.11.2010 20:26
quelle
2

Ähm ... nicht genau. Es ist der IDENTIFIER, der einen Bereich hat. Wenn Sie ein Objekt mit new erstellen, dessen Gültigkeitsbereich jedoch endet, kann es zu einem Speicherverlust kommen (oder nicht - hängt davon ab, was Sie erreichen möchten) - das Objekt befindet sich im Speicher, Sie haben aber keine Möglichkeit referenziere es nicht mehr.

Der Unterschied ist, dass ein Zeiger eine Adresse im Speicher ist, also wenn Sie sagen, diesen Code:

%Vor%

a ist ein Zeiger. Sie können es drucken - und Sie erhalten etwas wie "0x0023F1" - es ist nur das: eine Adresse. Es hat keinen Wert (obwohl ein Wert im Speicher unter dieser Adresse gespeichert ist).

%Vor%

b ist eine Variable mit einem Wert von 10. Wenn Sie es drucken, erhalten Sie 10 .

Wenn Sie jetzt möchten, dass a auf die Adresse von b zeigt, können Sie Folgendes tun:

%Vor%

oder wenn die Adresse% a den Wert b haben soll:

%Vor%

Bitte kompilieren Sie dieses Beispiel und kommentieren Sie die Zeilen 13 und 14, um den Unterschied zu sehen (beachten Sie, wo die Bezeichner zeigen und auf welchen Wert). Ich hoffe, die Ausgabe wird selbsterklärend sein.

%Vor%     
mingos 26.11.2010 19:49
quelle
2

Lassen Sie uns zuerst die letzte Frage beantworten. Dann wird die erste Frage mehr Sinn machen.

F: "Was ist der praktische Unterschied [zwischen einem Zeiger und einer Referenz]?"

A: Eine Referenz ist nur ein lokales Pseudonym für eine andere Variable. Wenn Sie einen Parameter als Referenz übergeben, entspricht dieser Parameter genau der Variablen, die in der aufrufenden Anweisung aufgeführt war. Intern gibt es jedoch normalerweise keinen Unterschied zwischen einem Zeiger und einer Referenz. Referenzen bieten "syntax sugar", indem Sie die Anzahl der Eingaben reduzieren können, wenn Sie wirklich nur auf eine einzelne Instanz einer bestimmten Variable zugreifen wollten.

F: "Wann sollte ich jedes von ihnen verwenden?"

A: Das wird eine Frage der persönlichen Präferenz sein. Hier ist die Grundregel, der ich folge. Wenn ich eine Variable in einem anderen Bereich manipulieren muss, und diese Variable ist entweder ein intrinsischer Typ, eine Klasse, die wie ein intrinsischer Typ (dh std :: string, etc ...) verwendet werden soll, oder eine Konstante Klasseninstanz, dann passiere ich als Referenz. Ansonsten passiere ich mit Zeiger.

    
Arkain 26.11.2010 19:55
quelle
0

Das Problem ist, dass Sie eine Referenz auf ein anderes Objekt nicht erneut binden können. Referenzen sind gebundene Kompilierzeit und können nicht Null oder Rebound sein. Also Zeiger sind nicht redundant, wenn Ihr Zweifel das war :)

    
Armen Tsirunyan 26.11.2010 19:27
quelle
0

Wie mein C ++ - Lehrer es ausdrückte, verweisen Zeiger auf den Speicherort, während Referenzen Aliase sind. Daher besteht der Hauptvorteil darin, dass sie genauso verwendet werden können wie der Objektname, auf den sie sich beziehen, aber in einem Bereich, in dem das Objekt nicht verfügbar ist, indem es dort übergeben wird.

Während Zeiger zu einem anderen Ort umgeleitet werden können, können Referenzen, die wie konstante Zeiger sind, nicht umgeleitet werden. So können Referenzen nicht zum Durchlaufen von Arrays in Funktionen usw. verwendet werden.

Ein Zeiger, der eine separate Entität darstellt, benötigt zwar etwas Speicher, aber die Referenz, die mit dem referenzierten Objekt übereinstimmt, nimmt keinen zusätzlichen Platz ein. Dies ist einer seiner Vorteile.

Ich habe auch gelesen, dass die Bearbeitungszeit für Referenzen geringer ist,

als

int & amp; i = b;

i ++; braucht weniger Zeit als

int * j = b;

(* j) ++;

aber ich muss das noch bestätigen. Wenn jemand diese Behauptung erhellen kann, wäre das großartig.

Kommentare sind willkommen :)

    
S..K 27.11.2010 05:41
quelle

Tags und Links