2009-03-21 6 views
29

Nehmen wir an, Sie haben eine Funktion, die eine Variable ändert.C++ - Funktionen: Ampersand vs Sternchen

Sollten Sie es so schreiben: void myfunc(int *a) oder wie dieses void myfunc(int &a)?

Der ehemalige zwingt Sie, die Funktion mit myfunc(&b) aufrufen, so dass der Anrufer bewusst ist, dass b wird geändert, aber letztere ist kürzer und kann einfach mit myfunc(b) aufgerufen werden. Also was ist besser zu benutzen? Gibt es noch etwas, das mir fehlt?

+0

Es gibt eine Menge von Artikeln und Themen zu diesem Thema http://www.google.ru/search?q=pointer+vs+ Referenz – abatishchev

Antwort

36

Pointer (dh das '*') sollte verwendet werden, wo die Weitergabe "NULL" sinnvoll ist. Beispielsweise können Sie mit einem NULL angeben, dass ein bestimmtes Objekt erstellt werden muss oder dass eine bestimmte Aktion nicht ausgeführt werden muss. Oder wenn es jemals von Nicht-C++ - Code aufgerufen werden muss. (z. B. zur Verwendung in gemeinsamen Bibliotheken)

z. Die libc-Funktion time_t time (time_t *result);

Wenn result nicht NULL ist, wird die aktuelle Uhrzeit gespeichert. Wenn result jedoch NULL ist, wird keine Aktion ausgeführt.

Wenn die zu schreibende Funktion NULL nicht als aussagekräftigen Wert verwenden muss, wird die Verwendung von Referenzen (z. B. '&') wahrscheinlich weniger verwirrend sein - vorausgesetzt, dies ist die Konvention, die Ihr Projekt verwendet.

+1

Ah, guter Punkt. Jetzt fühle ich mich nicht als Es ist viel einfacher, die Funktion mit einem Verweis als einen Zeiger zu schreiben. Danke :) – mpen

12

Wann immer möglich verwende ich Referenzen über Zeiger. Der Grund dafür ist, dass es viel schwieriger ist, eine Referenz zu vermasseln als einen Zeiger. Menschen können immer NULL an einen Zeigerwert übergeben, aber es gibt keine solche Entsprechung zu einer Referenz.

Der einzige wirkliche Nachteil ist, dass Referenz-Parameter in C++ eine fehlende Dokumentation der Aufruf-Site haben. Manche Leute glauben, dass es schwieriger ist, Code zu verstehen (und ich stimme in gewissem Maße zu). Ich definiere normalerweise das Folgende in meinem Code und benutze es für gefälschte Anruf-Site-Dokumentation

#define byref 
... 
someFunc(byref x); 

Dies erzwingt natürlich nicht Call-Site-Dokumentation. Es bietet nur eine sehr lahme Art, es zu dokumentieren. Ich experimentierte mit einer Vorlage, die die Dokumentation der Aufruf-Site erzwingt. Dies ist mehr zum Spaß als für den tatsächlichen Produktionscode.

http://blogs.msdn.com/jaredpar/archive/2008/04/03/reference-values-in-c.aspx

+0

Ich bin damit einverstanden, dass die Call-Site nicht selbstdokumentiert. Beachten Sie jedoch, dass Funktionen, die Referenzparameter modifizieren, fast immer einen passenden Namen haben sollten. Zum Beispiel, Point.GetCoords (int & x, int &y); und, mypoint.GetCoords (x, y); – strager

1

der ehemaligen zwingt Sie die Funktion mit myfunc zu nennen (& b) so dem Anrufer ist sich bewusst, dass b wird

manchmal Funktion übernehmen könnte const Zeiger geändert werden und Anrufer wird falsch denken, dass b geändert wird.

Meine Empfehlung - verwenden Sie Referenzen überall wo es möglich ist (natürlich wo es gebraucht wird). Im Fall mit Funktionsargument - wir erhalten Vorteile:
- Referenzen können nicht NULL sein - es hilft uns, Fehler und unnötige Behauptungen oder Überprüfungen zu vermeiden.
- Referenzen haben nur einen Initialisierungspunkt und in der Funktion boody wissen Sie immer, auf welchen Punkt der Eingangsparameter zeigt.

Ich bin Betreuer auf großes Projekt. Und in beiden Fällen schaue ich auf die Funktionsdefinition, bevor ich sie anrufe. Natürlich, wenn ich auf Funktionsdefinition schaue, sehe ich Argumentdefinition durch Wert, durch Referenz, durch const Referenz oder durch Zeiger.

Aber es scheint wie Frage des Heiligen Krieges, verschiedene Völker haben unterschiedliche Sicht auf diesen Punkt. Z.B. google codding convension recomended Verwendung Zeiger in Argumente, die geändert werden könnten und nur const Referenzen erlaubt: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments

Alle Referenz übergeben Parameter muss const markiert werden.

7

Ich denke, ich würde mit @bb und @JaredPar nicht einverstanden sein und ich lehne mich an die gegenüberliegende Seite des Zauns. Nach jahrelangen Versuchen, C++ - Code anderer Leute zu unterstützen, finde ich oft Probleme, die in nicht offensichtlichen Nebenwirkungen von Referenzargumenten lauern.Mit C# ist es offensichtlich, da Sie Typen von Argumenten mit 'ref'/'out' voranstellen müssen, aber Referenzen in C++ möglicherweise verwirrend sind. Also mag ich Zeiger, weil es wirklich klar ist, dass etwas zurückkommt. Wenn Sie Punkte nicht mögen, ist C++ nicht für Sie.

+4

Ich stimme Ihnen zu. Ein ernsthaftes Problem mit Referenzen ist, dass 'f1 (int), f2 (int &)', aufgerufen werden auf die gleiche Weise 'f1 (a), f2 (a)' aber es gibt einen großen Unterschied. Und das kann wirklich schmerzhaft sein, solchen Code zu debuggen. Und ist ein einfacher Fehler. – Ismael

0

In einer idealen Welt kommt es darauf an, ob der Parameter ein optionaler Ausgang ist oder nicht. Möchten Sie dem Anrufer erlauben, NULL zu übergeben, wenn sie sich nicht darum kümmern? Dann benutze einen Zeiger. Ansonsten ist eine Referenz eine bessere Wahl.

Beachten Sie, dass es in beiden Fällen mühsam ist, einen Ausgabeparameter zu dokumentieren. Es ist viel besser, wenn die gesamte Codebasis const korrekt ist, dann können die Benutzer annehmen, dass jeder nicht-konstante Verweis oder Zeigerparameter eine Ausgabe ist.

4

Ich komme auf die Zeigerseite des Zauns, aus Gründen hier und anderswo zitiert. Ich werde jedoch sagen, dass, was auch immer Sie sich entscheiden, Sie konsistent sein und es in Ihrem Styleguide dokumentieren müssen.

Google C++ - Formatvorlage bans nicht-const Referenzargumente.

+0

Der angegebene Link ist veraltet. Wird es jemand aktualisieren? – Jason

+0

Sie können auch der Meinung sein, dass der Google-Style-Guide möglicherweise nicht die beste Referenz für Good Practices ist: https://www.linkedin.com/pulse/20140503193653-3046051-why-google-style-guide-for-c-is -a-dealbreaker – Jason

+0

Hängt vom Projekt ab. –

0

Ein Unterschied, wie bereits erwähnt, ist, dass Sie keine Nullreferenz übergeben können, aber Sie können einen Nullzeiger übergeben.

Eine andere Sache, erwähnt auch schon, wenn Sie f(a,b) es nennen Verwirrung sein könnte, wenn der Anrufer nicht weiß, dass f möglicherweise den Wert für b

jedoch noch ein weiteres Problem, das eher subtil ändern könnte, aber I still ran into it, ist die Semantik von Referenzen.

Zeiger werden nach Wert übergeben, Referenzen jedoch nicht.

Das bedeutet, wenn Sie einen Parameter per Zeiger übergeben, können Sie den Zeiger ändern und auf etwas anderes zeigen.

Bedenken Sie:

void f1_ptr(type * a) 
{ 
    a = new type(); //no change to passed parameters, you're changing the pointer which was passed by value 
} 

void f2_ptr(type * a) 
{ 
    *a = some_other_value; //now you're changing the value of the parameter that was passed 

    //or, if type is a class or struct: 

    a->some_method_that_modifies_object(); //again, changing the parameter that was passed 
} 

Aber, wenn sie durch Bezugnahme vorbei, können Sie nicht ändern Sie den Verweis auf einen anderen Wert zu verweisen. Sobald die Referenz festgelegt ist, kann sie nicht geändert werden.

+0

In f2_ptr ändert sich eine Änderung für den Bereich von f2_ptr. Mit anderen Worten, obj wird nicht geändert, wenn f2_ptr aufgerufen wird (unter der Annahme, dass der ursprüngliche Wert von a (obj) nicht in f2_ptr verwendet wird). – strager

1

Ich mag die Weitergabe per Referenz, wenn NULL keine Bedeutung hat, aber ich kann die Argumente für beide sehen.Wenn Sie vorsichtig sind über Sie Codierung wahrscheinlich das versehentliche Pass-by-reference Einwand, indem sichergestellt wird beseitigen können Sie immer Ihre Variablen durch const Verweis übergeben, zB:

myfunc(const_cast< const int& >(a)); 

// Alternatively, this approach may require additional handling 
// in the function, but it's cleaner at call point 
myfunc(boost::cref(a)); 

, dass eine Menge zusätzlichen Code für wenig Nutzen ist, obwohl. Wie Kenny darauf hingewiesen hat, hat C# dies vom entgegengesetzten Ende her adressiert (was eine spezifische Weitergabe per Referenz erfordert), aber das ist keine Option für C++ (außer Sie haben beispielsweise Ihre Funktionen so geschrieben, dass sie einen Refrain-Wrapper als Parameter verwenden, zB boost :: ref (param)), zB:

void myfunc(const boost::reference_wrapper<int>& a) { ... } 

der Zeiger Problem Fixing ist problematischer, obwohl ... es gibt keine Kompilierung-Art und Weise der Zeiger gültig ist, um sicherzustellen, so dass Sie entweder mit Laufzeitprobleme am Ende für Zeigerprobleme oder Laufzeitprüfungen oder beides. Tis die Art von Zeigern.

Wie auch immer, das ist nur meine Meinung, für das, was es wert ist.

+0

Rechts. Aber warum würdest du den reference_wrapper selbst als Referenz nehmen und nicht nach Wert? –

+0

Weil es eine Klasse ist, und sie durch const zu übergeben, ist wahrscheinlich schneller als das Kopieren (obwohl es kopierbar ist). Beide werden funktionieren; Dieser Weg ist wahrscheinlich weniger Aufwand. – Nick

+0

Nun, reference_wrapper wiegt genauso viel wie ein Zeiger. Ich dachte, das war offensichtlich. –

1

Etwas zu beachten, wenn Sie STL-Funktoren verwenden, ist es einfacher, wenn der Parameter dem Container-Werttyp entspricht.

void foo(Bar *); 

void frobnicate(vector<Bar *> vecBars) 
{ 
    for_each(vecBars.begin(), 
      vecBars.end(), 
      ptr_fun(&foo)); 
} 

Der obige Code härter ist viel, wenn foo nimmt Bar &