Wann Verweise auf pointers zu verwenden sind

Ich verstehe die Syntax und die allgemeine Semantik von pointersn gegenüber Referenzen, aber wie sollte ich entscheiden, wann es mehr oder weniger angemessen ist, Referenzen oder pointers in einer API zu verwenden?

Natürlich brauchen einige Situationen das eine oder das andere ( operator++ benötigt ein Referenzargument), aber im Allgemeinen finde ich, dass ich lieber pointers (und Const-pointers) verwende, da die Syntax klar ist, dass die Variablen destruktiv übergeben werden.

ZB im folgenden Code:

 void add_one(int& n) { n += 1; } void add_one(int* const n) { *n += 1; } int main() { int a = 0; add_one(a); // Not clear that a may be modified add_one(&a); // 'a' is clearly being passed destructively } 

Mit dem pointers ist es immer (mehr) offensichtlich, was passiert, so dass für APIs und Ähnliches, wo Klarheit ein großes Anliegen ist, pointers nicht geeigneter sind als Referenzen? Bedeutet das, dass Referenzen nur bei Bedarf verwendet werden sollten (zB operator++ )? Gibt es irgendwelche performancesbedenken mit dem einen oder anderen?

BEARBEITEN (OUTDIERT):

Neben dem Zulassen von NULL-Werten und dem Umgang mit Raw-Arrays scheint die Wahl auf persönliche Präferenzen zurückzuführen zu sein. Ich habe die folgende Antwort akzeptiert, die sich auf den Google C ++ – Stilführer bezieht, da sie die Ansicht “Referenzen können verwirrend sein, da sie eine Wertesyntax aber eine Pointer-Semantik haben”.

Aufgrund der zusätzlichen Arbeit, die erforderlich ist, um pointersargumente zu add_one(0) , die nicht NULL sein sollten (z. B. add_one(0) wird die add_one(0) aufrufen und zur Laufzeit add_one(0) ), ist es aus Sicht der Wartbarkeit sinnvoll, Referenzen zu verwenden, bei denen ein Objekt vorhanden sein muss Es ist eine Schande, die syntaktische Klarheit zu verlieren.

Solutions Collecting From Web of "Wann Verweise auf pointers zu verwenden sind"

Verwenden Sie Referenz, wo immer Sie können, pointers, wo immer Sie müssen.

Vermeiden Sie pointers, bis Sie nicht können.

Der Grund dafür ist, dass pointers das Lesen / Lesen, weniger sichere und weitaus gefährlichere Manipulationen schwieriger machen als andere Konstrukte.

Die Faustregel ist also, pointers nur zu verwenden, wenn es keine andere Wahl gibt.

Beispielsweise ist das Zurückgeben eines pointerss zu einem Objekt eine gültige Option, wenn die function in einigen Fällen nullptr zurückgeben kann, und es wird davon ausgegangen, dass es wird. Das heißt, eine bessere Option wäre, etwas ähnliches wie boost::optional .

Ein anderes Beispiel ist die Verwendung von pointersn auf den Rohspeicher für spezifische Speichermanipulationen. Das sollte in sehr engen Teilen des Codes versteckt und lokalisiert werden, um die gefährlichen Teile der gesamten Codebasis zu begrenzen.

In Ihrem Beispiel ist es sinnlos, einen pointers als Argument zu verwenden, weil:

  1. Wenn Sie nullptr als Argument nullptr , gehen Sie in undefined-behavior-land;
  2. Die Referenzattributversion erlaubt nicht (ohne leicht zu erkennende Tricks) das Problem mit 1.
  3. Die Referenzattributversion ist für den Benutzer einfacher zu verstehen: Sie müssen ein gültiges Objekt bereitstellen, nicht etwas, das null sein könnte.

Wenn das Verhalten der function mit oder ohne ein gegebenes Objekt funktionieren müsste, schlägt die Verwendung eines pointerss als Attribut vor, dass Sie nullptr als Argument übergeben können, und es ist in Ordnung für die function. Das ist eine Art Vertrag zwischen dem Benutzer und der Implementierung.

Die performanceen sind genau gleich, da Referenzen intern als pointers implementiert sind. Somit müssen Sie sich keine Sorgen machen.

Es gibt keine allgemein akzeptierte Konvention bezüglich der Verwendung von Referenzen und pointersn. In einigen Fällen müssen Sie Referenzen zurückgeben oder akzeptieren (z. B. Kopierkonstrukteur), aber abgesehen davon steht es Ihnen frei, das zu tun, was Sie wünschen. Eine ziemlich häufige Konvention, die ich erlebt habe, ist die Verwendung von Referenzen, wenn der Parameter ein vorhandenes Objekt und pointers referenzieren muss, wenn ein NULL-Wert in Ordnung ist.

Einige Codierungskonventionen (wie die von Google ) schreiben vor, dass man immer pointers oder Const-Referenzen verwenden sollte, weil Referenzen ein wenig unklare Syntax haben: Sie haben ein Referenzverhalten, aber eine Wertesyntax.

Aus C ++ – FAQ Lite –

Verwenden Sie Referenzen, wenn Sie können, und pointers, wenn Sie müssen.

Referenzen sind in der Regel gegenüber pointersn vorzuziehen, wenn Sie nicht “neu setzen” müssen. Dies bedeutet normalerweise, dass Referenzen in der öffentlichen Schnittstelle einer class am nützlichsten sind. Referenzen erscheinen normalerweise auf der Haut eines Objekts und pointers auf der Innenseite.

Die Ausnahme zu dem Obengenannten ist, wo der Parameter oder der Rückgabewert einer function eine “Sentinel” -Referenz benötigt – eine Referenz, die sich nicht auf ein Objekt bezieht. Dies wird normalerweise am besten erreicht, indem ein pointers zurückgegeben / aufgenommen wird und dem NULL-pointers diese besondere Bedeutung gegeben wird (Referenzen müssen immer Alias-Objekte sein, nicht ein dereferenzierter NULL-pointers).

Hinweis: Alte Zeile C-Programmierer mögen manchmal keine Referenzen, da sie Referenzsemantiken bereitstellen, die im Code des Aufrufers nicht explizit sind. Nach einigen C ++ – Erfahrungen wird jedoch schnell klar, dass dies eine Form der Informationsverbergung ist, die eher ein Asset als eine Haftung ist. Beispielsweise sollten Programmierer Code in der Sprache des Problems und nicht in der Sprache der Maschine schreiben.

Meine Faustregel ist:

  • Verwenden Sie pointers für ausgehende oder eingehende / ausgehende Parameter. Es ist also ersichtlich, dass der Wert geändert wird. (Sie müssen & )
  • Verwenden Sie pointers, wenn der NULL-Parameter ein akzeptabler Wert ist. (Stellen Sie sicher, dass es const wenn es ein eingehender Parameter ist)
  • Verwenden Sie Referenzen für eingehende Parameter, wenn es nicht NULL sein kann und kein primitiver Typ ( const T& ) ist.
  • Verwenden Sie pointers oder intelligente pointers, wenn Sie ein neu erstelltes Objekt zurückgeben.
  • Verwenden Sie pointers oder intelligente pointers als Struktur- oder classnmitglieder anstelle von Referenzen.
  • Verwenden Sie Referenzen für Aliasing (z. B. int &current = someArray[i] )

Unabhängig davon, welche Sie verwenden, vergessen Sie nicht, Ihre functionen und die Bedeutung ihrer Parameter zu dokumentieren, wenn sie nicht offensichtlich sind.

Wie andere bereits beantwortet: Verwenden Sie immer Referenzen, es sei denn, die Variable NULL / nullptr ist wirklich ein gültiger Zustand.

John Carmacks Standpunkt zu diesem Thema ist ähnlich:

NULL-pointers sind das größte Problem in C / C ++, zumindest in unserem Code. Die doppelte Verwendung eines einzelnen Werts als Flag und als Adresse verursacht eine unglaubliche Anzahl von schwerwiegenden Problemen. C ++ – Verweise sollten nach Möglichkeit gegenüber pointersn bevorzugt werden; während eine Referenz “wirklich” nur ein pointers ist, hat sie den impliziten Vertrag, nicht-NULL zu sein. Führen Sie NULL-Prüfungen durch, wenn pointers in Referenzen umgewandelt werden, und Sie können das Problem danach ignorieren.

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

Bearbeiten 2012-03-13

Benutzer Bret Kuhns bemerkt zu Recht:

Der C ++ 11-Standard wurde fertiggestellt. Ich denke, es ist an der Zeit in diesem Thread zu erwähnen, dass der meiste Code mit einer Kombination von Referenzen, shared_ptr und unique_ptr, vollkommen in Ordnung sein sollte.

Es stimmt zwar, aber die Frage bleibt immer noch, selbst wenn rohe pointers durch intelligente pointers ersetzt werden.

Zum Beispiel können sowohl std::unique_ptr als auch std::shared_ptr durch ihren Standardkonstruktor als “leere” pointers konstruiert werden:

… bedeutet, dass die Verwendung von ihnen, ohne zu überprüfen, dass sie nicht leer sind, einen Absturz bedeutet, und genau darum geht es in J. Carmacks Diskussion.

Und dann haben wir das amüsante Problem: “Wie übergeben wir einen intelligenten pointers als functionsparameter?”

Jons Antwort auf die Frage C ++ – Verweise auf boost :: shared_ptr , und die folgenden Kommentare zeigen, dass selbst dann, wenn ein intelligenter pointers nach Kopie oder Verweis übergeben wird, nicht so klar ist, wie man möchte (ich bevorzuge den ” by-reference “standardmäßig, aber ich könnte falsch liegen).

Disclaimer: Abgesehen von der Tatsache, dass Referenzen weder NULL noch “Rebound” sein können (was bedeutet, dass sie das Objekt, von dem sie Alias ​​sind, nicht ändern können), kommt es wirklich auf eine Frage des Geschmacks an, also werde ich nicht sagen “das ist besser”.

Abgesehen davon bin ich mit Ihrer letzten Aussage in der Post nicht einverstanden, da ich nicht glaube, dass der Code durch Referenzen an Klarheit verliert. In Ihrem Beispiel

 add_one(&a); 

könnte klarer sein als

 add_one(a); 

da Sie wissen, dass sich der Wert von a höchstwahrscheinlich ändern wird. Auf der anderen Seite jedoch die Signatur der function

 void add_one(int* const n); 

ist auch nicht klar: Wird n eine einzelne ganze Zahl oder ein Array sein? Manchmal haben Sie nur Zugriff auf (schlecht dokumentierte) Header und Signaturen wie

 foo(int* const a, int b); 

sind auf den ersten Blick nicht einfach zu interpretieren.

Imho, Referenzen sind so gut wie pointers, wenn keine (Neu-) Zuweisung oder Neubindung (in dem zuvor erläuterten Sinne) benötigt wird. Wenn ein Entwickler nur pointers für Arrays verwendet, sind functionssignaturen etwas wenigerdeutig. Ganz zu schweigen von der Tatsache, dass die Syntax der Operatoren mit Referenzen viel besser lesbar ist.

Es ist keine Frage des Geschmacks. Hier sind einige definitive Regeln.

Wenn Sie auf eine statisch deklarierte Variable innerhalb des Gültigkeitsbereichs, in dem sie deklariert wurde, verweisen möchten, verwenden Sie eine C ++ – Referenz, und das ist absolut sicher. Gleiches gilt für einen statisch deklarierten Smart Pointer. Die Übergabe von Parametern als Referenz ist ein Beispiel für diese Verwendung.

Wenn Sie auf einen Bereich verweisen möchten, der breiter ist als der Bereich, in dem er deklariert ist, sollten Sie einen Smartzeiger mit Referenzzählung verwenden, damit er vollkommen sicher ist.

Sie können sich auf ein Element einer Sammlung mit einer Referenz für syntaktische Bequemlichkeit beziehen, aber es ist nicht sicher; Das Element kann jederzeit gelöscht werden.

Um einen Verweis auf ein Element einer Sammlung sicher zu halten, müssen Sie einen Smartzeiger mit Referenzzählung verwenden.

Jeder performancesunterschied wäre so gering, dass es nicht gerechtfertigt wäre, den Ansatz zu verwenden, der weniger klar ist.

Erstens ist ein Fall, der nicht erwähnt wurde, wo Referenzen im Allgemeinen besser sind, const Referenzen. Bei nicht einfachen Typen wird durch das Übergeben eines const reference vermieden, dass ein temporärer Wert erstellt wird, und verursacht nicht die Verwirrung, um die Sie besorgt sind (da der Wert nicht geändert wird). Wenn Sie eine Person dazu zwingen, einen pointers zu übergeben, führt dies zu der Verwirrung, um die Sie sich sorgen. Wenn Sie die Adresse sehen und an eine function übergeben, können Sie denken, dass sich der Wert geändert hat.

In jedem Fall stimme ich Ihnen grundsätzlich zu. Ich mag keine functionen, die Referenzen verwenden, um ihren Wert zu ändern, wenn es nicht sehr offensichtlich ist, dass dies die function ist. Ich bevorzuge auch in diesem Fall pointers zu verwenden.

Wenn Sie einen Wert in einem komplexen Typ zurückgeben müssen, bevorzuge ich Referenzen. Beispielsweise:

 bool GetFooArray(array &foo); // my preference bool GetFooArray(array *foo); // alternative 

Hier macht der functionsname deutlich, dass Sie Informationen zurück in ein Array bekommen. Also gibt es keine Verwirrung.

Die Hauptvorteile von Referenzen bestehen darin, dass sie immer einen gültigen Wert enthalten, sauberer als pointers sind und Polymorphie unterstützen, ohne dass zusätzliche Syntax benötigt wird. Wenn keiner dieser Vorteile zutrifft, gibt es keinen Grund, einen Verweis auf einen pointers zu bevorzugen.

Aus Wiki kopiert –

Eine Folge davon ist, dass in vielen Implementierungen das Arbeiten an einer Variablen mit automatischer oder statischer Lebensdauer über eine Referenz, obwohl sie syntaktisch dem direkten Zugriff derselben ähnelt, versteckte Dereferenzierungsoperationen beinhalten kann, die teuer sind. Referenzen sind eine syntaktisch kontroverse Eigenschaft von C ++, da sie die Umleitungsebene eines Bezeichners verdecken; das heißt, anders als C-Code, bei dem pointers normalerweise syntaktisch auffallen, ist es in einem großen C ++ – Code nicht sofort offensichtlich, ob das Objekt, auf das zugegriffen wird, als lokale oder globale Variable definiert ist oder ob es eine Referenz (impliziter pointers) ist ein anderer Ort, besonders wenn der Code Referenzen und pointers mischt. Dieser Aspekt kann dazu führen, dass schlecht geschriebener C ++ – Code schwerer zu lesen und zu debuggen ist (siehe Aliasing).

Ich stimme zu 100% damit überein, und deshalb glaube ich, dass Sie nur dann eine Referenz verwenden sollten, wenn Sie einen sehr guten Grund dafür haben.

Es gibt ein Problem mit der Regel ” Referenzen verwenden, wo immer es möglich ist ” und es entsteht, wenn Sie Referenzen für die weitere Verwendung behalten möchten. Um dies anhand eines Beispiels zu veranschaulichen, stellen Sie sich vor, dass Sie folgende classn haben.

 class SimCard { public: explicit SimCard(int id): m_id(id) { } int getId() const { return m_id; } private: int m_id; }; class RefPhone { public: explicit RefPhone(const SimCard & card): m_card(card) { } int getSimId() { return m_card.getId(); } private: const SimCard & m_card; }; 

Zunächst scheint es eine gute Idee zu sein, den Parameter in RefPhone(const SimCard & card) von einem Verweis übergeben zu bekommen, da er verhindert, dass falsche / Null-pointers an den Konstruktor übergeben werden. Es ermutigt irgendwie die Zuweisung von Variablen auf Stack und die Vorteile von RAII.

 PtrPhone nullPhone(0); //this will not happen that easily SimCard * cardPtr = new SimCard(666); //evil pointer delete cardPtr; //muahaha PtrPhone uninitPhone(cardPtr); //this will not happen that easily 

Aber dann kommen die Provisorien, um deine glückliche Welt zu zerstören.

 RefPhone tempPhone(SimCard(666)); //evil temporary //function referring to destroyed object tempPhone.getSimId(); //this can happen 

Wenn Sie sich also blind an Referenzen halten, haben Sie die Möglichkeit, ungültige pointers für die Möglichkeit der Speicherung von Referenzen auf zerstörte Objekte zu übergeben, was im Grunde die gleiche Wirkung hat.

Bearbeiten: Beachten Sie, dass ich mich an die Regel “Benutze Referenz, wo immer Sie können, pointers wo immer Sie müssen. Ich vermeide pointers, bis Sie nicht können.” von der am meisten angenommenen und akzeptierten Antwort (andere Antworten schlagen auch so vor). Obwohl es offensichtlich sein sollte, soll das Beispiel nicht zeigen, dass Referenzen als solche schlecht sind. Sie können jedoch genauso wie pointers missbraucht werden, und sie können ihre eigenen Bedrohungen in den Code einbringen.


Es gibt folgende Unterschiede zwischen pointersn und Referenzen.

  1. Wenn es um das Übergeben von Variablen geht, sieht die Übergabe als Verweis wie ein Übergabewert aus, hat aber eine Pointer-Semantik (wirkt wie ein pointers).
  2. Referenz kann nicht direkt auf 0 (Null) initialisiert werden.
  3. Referenz (Referenz, nicht referenziertes Objekt) kann nicht geändert werden (entspricht “* const” pointers).
  4. const Referenz kann temporäre Parameter akzeptieren.
  5. Lokale Const-Referenzen verlängern die Lebensdauer temporärer Objekte

Unter Berücksichtigung dieser Punkte sind meine derzeitigen Regeln wie folgt.

  • Verwenden Sie Referenzen für Parameter, die innerhalb eines functionsumfangs lokal verwendet werden.
  • Verwenden Sie pointers, wenn 0 (null) ein akzeptabler Parameterwert ist oder Sie Parameter zur weiteren Verwendung speichern müssen. Wenn 0 (Null) akzeptabel ist, füge ich dem Parameter “_n” Suffix hinzu, verwende einen geschützten pointers (wie QPointer in Qt) oder dokumentiere es einfach. Sie können auch intelligente pointers verwenden. Sie müssen mit gemeinsamen pointersn noch vorsichtiger sein als mit normalen pointersn (sonst können Sie durch Design-Speicherlecks und Verantwortlichkeitserrors enden).

Im Folgenden finden Sie einige Richtlinien.

Eine function verwendet übergebene Daten, ohne sie zu ändern:

  1. Wenn das Datenobjekt klein ist, z. B. ein integrierter Datentyp oder eine kleine Struktur, übergeben Sie es als Wert.

  2. Wenn das Datenobjekt ein Array ist, verwenden Sie einen pointers, da dies Ihre einzige Wahl ist. Machen Sie den pointers zu einem pointers auf const.

  3. Wenn das Datenobjekt eine Struktur von guter Größe ist, verwenden Sie einen const-pointers oder eine const-Referenz, um die Effizienz des Programms zu erhöhen. Sie sparen Zeit und Speicherplatz, die zum Kopieren einer Struktur oder eines classndesigns benötigt werden. Machen Sie den pointers oder den Verweis const.

  4. Wenn das Datenobjekt ein classnobjekt ist, verwenden Sie einen const-Verweis. Die Semantik des classnentwurfs erfordert häufig die Verwendung einer Referenz, was der Hauptgrund ist, warum C ++ diese function hinzugefügt hat.

Eine function ändert Daten in der aufrufenden function:

1. Wenn das Datenobjekt ein integrierter Datentyp ist, verwenden Sie einen pointers. Wenn Sie Code wie fixit (& x) finden, wobei x ein int ist, ist es ziemlich klar, dass diese function beabsichtigt, x zu ändern.

2. Wenn das Datenobjekt ein Array ist, verwenden Sie Ihre einzige Auswahl: einen pointers.

3. Wenn das Datenobjekt eine Struktur ist, verwenden Sie eine Referenz oder einen pointers.

4. Wenn das Datenobjekt ein classnobjekt ist, verwenden Sie eine Referenz.

Natürlich sind dies nur Richtlinien, und es könnte Gründe geben, unterschiedliche Entscheidungen zu treffen. Zum Beispiel verwendet cin Referenzen für Grundtypen, so dass Sie cin >> n statt cin >> & n verwenden können.

Einfach meinen Groschen reinstecken. Ich habe gerade einen Test gemacht. Ein schlauer dabei. Ich lasse g ++ die Assemblierungsdateien desselben Miniprogramms mit Hilfe von pointersn erstellen, im Vergleich zur Verwendung von Referenzen. Bei der Betrachtung der Ausgabe sind sie genau gleich. Anders als die Symbolbenennung. Betrachtet man die performance (in einem einfachen Beispiel), gibt es kein Problem.

Jetzt zum Thema pointers gegen Referenzen. Ich denke, Klarheit steht über allem. Sobald ich implizites Verhalten lese beginnen meine Zehen zu rollen. Ich stimme zu, dass es ein nettes implizites Verhalten ist, dass eine Referenz nicht NULL sein kann.

Dereferenzieren eines Nullzeigers ist nicht das Problem. Es wird Ihre Anwendung zum Absturz bringen und wird leicht zu debuggen sein. Ein größeres Problem sind nicht initialisierte pointers, die ungültige Werte enthalten. Dies führt höchstwahrscheinlich zu Speicherbeschädigungen, die zu undefiniertem Verhalten ohne eindeutigen Ursprung führen.

Dies ist, wo ich denke, Referenzen sind viel sicherer als pointers. Und ich stimme einer früheren Aussage zu, dass die Schnittstelle (die klar dokumentiert sein sollte, siehe Vertragsgestaltung, Bertrand Meyer) das Ergebnis der Parameter einer function definiert. Wenn ich das alles in Betracht ziehe, gehen meine Vorlieben dahin, wo immer möglich Referenzen zu verwenden.

Im Allgemeinen sollte eine Elementvariable niemals eine Referenz sein, da dies keinen Sinn hat. Es bewirkt, dass die class nicht zuweisbar ist, wenn Sie keinen Zuweisungsoperator bereitstellen. Sobald Sie den Verweis auf ein Objekt auf ein Objekt festgelegt haben, ist es nicht mehr möglich, dieses Element zum Verweis auf ein anderes Objekt zu ändern. Die am besten geeignete Verwendung einer Referenz ist die Verwendung als ein functionsparameter, der einen Durchgang durch Referenz ermöglicht.

Referenzen sind sauberer und benutzerfreundlicher und sie verbergen Informationen besser. Referenzen können jedoch nicht neu zugewiesen werden. Wenn Sie zuerst auf ein Objekt und dann auf ein anderes Objekt zeigen müssen, müssen Sie einen pointers verwenden. Verweise können nicht null sein. Wenn also eine Wahrscheinlichkeit besteht, dass das fragliche Objekt null ist, dürfen Sie keine Referenz verwenden. Sie müssen einen pointers verwenden. Wenn Sie selbst Objektmanipulationen durchführen möchten, dh wenn Sie Speicherplatz für ein Objekt auf dem Heap und nicht auf dem Stack zuweisen möchten, müssen Sie Pointer verwenden

 int *pInt = new int; // allocates *pInt on the Heap 

Bei pointersn müssen sie auf etwas zeigen, sodass pointers Speicherplatz beanspruchen.

Zum Beispiel nimmt eine function, die einen Integer-pointers verwendet, die Integer-Variable nicht an. Sie müssen also zuerst einen pointers für die function erstellen.

Wie für eine Referenz wird es keinen Speicher kosten. Sie haben eine Integer-Variable und können diese als Referenzvariable übergeben. Das ist es. Sie müssen dafür keine Referenzvariable anlegen.

Zu beachtende Punkte:

  1. pointers können NULL , Referenzen können nicht NULL .

  2. References are easier to use, const can be used for a reference when we don’t want to change value and just need a reference in a function.

  3. Pointer used with a * while references used with a & .

  4. Use pointers when pointer arithmetic operation are required.

  5. You can have pointers to a void type int a=5; void *p = &a; but cannot have a reference to a void type.

Pointer Vs Reference

 void fun(int *a) { cout<  

Verdict when to use what

Pointer : For array, linklist, tree implementations and pointer arithmetic.

Reference : In function parameters and return types.

I prefer to use pointers. At least it is clear what you are doing. I have the feeling that references are mostly used because of STL and its syntax implications on code. Because of that also so many C++ standard library novelties like std::move ….. to get exactly what you want, and not what you intuitively would have thought of.

Use references as a last resort. Allocate an instance on the stack or the heap, use them.

Use references for parameter scope to get the least impact. If you use reference because pointers are too hard for you then move to another language.