Ich bin neu in der C ++ - Programmierung, habe aber Erfahrung in Java. Ich benötige Anleitungen zum Übergeben von Objekten an Funktionen in C ++.
Muss ich Zeiger, Referenzen oder Nicht-Zeiger- und Nicht-Referenzwerte übergeben? Ich erinnere mich, dass es in Java keine derartigen Probleme gibt, da wir nur die Variable übergeben, die auf die Objekte verweist.
Es wäre großartig, wenn Sie auch erklären könnten, wo jede dieser Optionen verwendet werden soll.
c++
pointers
pass-by-reference
pass-by-value
c++-faq
Rakesh K.
quelle
quelle
Antworten:
Faustregeln für C ++ 11:
Übergeben Sie den Wert , außer wenn
const
Bezug ,const
non-lvalre reference .const
Referenz übergeben werden soll oder nicht.)Das Übergeben eines Zeigers wird praktisch nie empfohlen. Optionale Parameter werden am besten als
std::optional
(boost::optional
für ältere Standardbibliotheken) ausgedrückt , und das Aliasing wird anhand der Referenz problemlos durchgeführt.Die Bewegungssemantik von C ++ 11 macht das Übergeben und Zurückgeben nach Wert auch für komplexe Objekte viel attraktiver.
Faustregeln für C ++ 03:
Übergeben von Argumenten durch
const
Verweis , es sei denn,const
ReferenzNULL
/0
/nullptr
statt; Wenden Sie die vorherige Regel an, um zu bestimmen, ob Sie einen Zeiger auf einconst
Argument übergeben sollen(Hier wird "Übergeben von Wert" als "Übergeben von Kopie" bezeichnet, da das Übergeben von Wert in C ++ 03 immer eine Kopie erstellt.)
Es gibt noch mehr, aber diese wenigen Anfängerregeln bringen Sie ziemlich weit.
quelle
Es gibt einige Unterschiede bei den Aufrufkonventionen in C ++ und Java. In C ++ gibt es technisch gesehen nur zwei Konventionen: Pass-by-Value und Pass-by-Reference, wobei einige Literaturstellen eine dritte Pass-by-Pointer-Konvention enthalten (dh tatsächlich Pass-by-Value eines Zeigertyps). Darüber hinaus können Sie dem Typ des Arguments Konstanz hinzufügen und so die Semantik verbessern.
Als Referenz übergeben
Das Übergeben als Referenz bedeutet, dass die Funktion Ihre Objektinstanz konzeptionell empfängt und keine Kopie davon. Die Referenz ist konzeptionell ein Alias für das Objekt, das im aufrufenden Kontext verwendet wurde, und kann nicht null sein. Alle innerhalb der Funktion ausgeführten Operationen gelten für das Objekt außerhalb der Funktion. Diese Konvention ist in Java oder C nicht verfügbar.
Wert übergeben (und Zeiger übergeben)
Der Compiler generiert eine Kopie des Objekts im aufrufenden Kontext und verwendet diese Kopie innerhalb der Funktion. Alle innerhalb der Funktion ausgeführten Operationen werden für die Kopie ausgeführt, nicht für das externe Element. Dies ist die Konvention für primitive Typen in Java.
Eine spezielle Version davon übergibt einen Zeiger (Adresse des Objekts) an eine Funktion. Die Funktion empfängt den Zeiger, und alle Operationen, die auf den Zeiger selbst angewendet werden, werden auf die Kopie (Zeiger) angewendet. Andererseits werden Operationen, die auf den dereferenzierten Zeiger angewendet werden, auf die Objektinstanz an diesem Speicherort angewendet, also auf die Funktion kann Nebenwirkungen haben. Durch die Verwendung der Wertübergabe eines Zeigers auf das Objekt kann die interne Funktion externe Werte wie bei der Referenzübergabe ändern und optionale Werte zulassen (Übergabe eines Nullzeigers).
Dies ist die in C verwendete Konvention, wenn eine Funktion eine externe Variable ändern muss, und die in Java verwendete Referenzkonvention mit Referenztypen: Die Referenz wird kopiert, das referenzierte Objekt ist jedoch dasselbe: Änderungen an der Referenz / dem Zeiger sind außerhalb nicht sichtbar die Funktion, aber Änderungen am spitzen Speicher sind.
Hinzufügen von const zur Gleichung
In C ++ können Sie Objekten Konstanz zuweisen, wenn Sie Variablen, Zeiger und Referenzen auf verschiedenen Ebenen definieren. Sie können eine Variable als konstant deklarieren, einen Verweis auf eine konstante Instanz deklarieren und alle Zeiger auf konstante Objekte, konstante Zeiger auf veränderbare Objekte und konstante Zeiger auf konstante Elemente definieren. Umgekehrt können Sie in Java nur eine Konstantenebene (endgültiges Schlüsselwort) definieren: die der Variablen (Instanz für primitive Typen, Referenz für Referenztypen), aber Sie können keine Referenz auf ein unveränderliches Element definieren (es sei denn, die Klasse selbst ist dies unveränderlich).
Dies wird häufig in C ++ - Aufrufkonventionen verwendet. Wenn die Objekte klein sind, können Sie das Objekt als Wert übergeben. Der Compiler generiert eine Kopie, aber diese Kopie ist keine teure Operation. Wenn die Funktion das Objekt bei keinem anderen Typ ändert, können Sie eine Referenz an eine konstante Instanz (normalerweise als konstante Referenz bezeichnet) des Typs übergeben. Dadurch wird das Objekt nicht kopiert, sondern an die Funktion übergeben. Gleichzeitig garantiert der Compiler, dass das Objekt innerhalb der Funktion nicht verändert wird.
Faustregeln
Dies sind einige Grundregeln, die befolgt werden müssen:
Es gibt andere kleine Abweichungen von diesen Regeln, von denen die erste die Behandlung des Eigentums an einem Objekt betrifft. Wenn ein Objekt dynamisch mit new zugewiesen wird, muss es mit delete (oder den [] -Versionen davon) freigegeben werden. Das Objekt oder die Funktion, die für die Zerstörung des Objekts verantwortlich ist, gilt als Eigentümer der Ressource. Wenn ein dynamisch zugewiesenes Objekt in einem Codeteil erstellt wird, der Besitz jedoch auf ein anderes Element übertragen wird, erfolgt dies normalerweise mit der Semantik des Pass-by-Pointers oder wenn möglich mit intelligenten Zeigern.
Randnotiz
Es ist wichtig, auf die Bedeutung des Unterschieds zwischen C ++ - und Java-Referenzen zu bestehen. In C ++ sind Referenzen konzeptionell die Instanz des Objekts und kein Accessor dafür. Das einfachste Beispiel ist die Implementierung einer Swap-Funktion:
Die obige Swap-Funktion ändert beide Argumente durch die Verwendung von Referenzen. Der nächstgelegene Code in Java:
Die Java-Version des Codes ändert die Kopien der Referenzen intern, die tatsächlichen Objekte jedoch nicht extern. Java-Referenzen sind C-Zeiger ohne Zeigerarithmetik, die als Wert an Funktionen übergeben werden.
quelle
Es sind mehrere Fälle zu berücksichtigen.
Parameter geändert (Parameter "out" und "in / out")
In diesem Fall geht es hauptsächlich um Stil: Möchten Sie, dass der Code wie call (obj) oder call (& obj) aussieht ? Es gibt jedoch zwei Punkte, an denen der Unterschied von Bedeutung ist: den optionalen Fall unten, und Sie möchten eine Referenz verwenden, wenn Sie Operatoren überladen.
... und optional
Parameter nicht geändert
Dies ist der interessante Fall. Als Faustregel gilt, dass "billig zu kopierende" Typen als Wert übergeben werden - dies sind im Allgemeinen kleine Typen (aber nicht immer) -, während andere als const ref übergeben werden. Wenn Sie jedoch unabhängig von Ihrer Funktion eine Kopie erstellen müssen, sollten Sie den Wert übergeben . (Ja, dies enthüllt einige Implementierungsdetails. C'est le C ++. )
... und optional
Hier gibt es den geringsten Unterschied zwischen allen Situationen. Wählen Sie also die Situation, die Ihnen das Leben am einfachsten macht.
Const by Value ist ein Implementierungsdetail
Diese Deklarationen haben tatsächlich genau die gleiche Funktion! Bei der Übergabe von Werten ist const lediglich ein Implementierungsdetail. Versuch es:
quelle
const
dass es sich bei der Übergabe von Werten um eine Implementierung handelt.Wert übergeben:
Übergeben Sie Variablen als Wert, wenn die Funktion vollständig von der Umgebung isoliert werden muss, dh um zu verhindern, dass die Funktion die ursprüngliche Variable ändert, und um zu verhindern, dass andere Threads ihren Wert ändern, während die Funktion ausgeführt wird.
Der Nachteil sind die CPU-Zyklen und der zusätzliche Speicher, der zum Kopieren des Objekts aufgewendet wird.
Pass by const Referenz:
Dieses Formular emuliert das Verhalten beim Übergeben von Werten, während der Kopieraufwand entfernt wird. Die Funktion erhält Lesezugriff auf das ursprüngliche Objekt, kann jedoch dessen Wert nicht ändern.
Der Nachteil ist die Thread-Sicherheit: Jede Änderung, die von einem anderen Thread am Originalobjekt vorgenommen wird, wird in der Funktion angezeigt, während sie noch ausgeführt wird.
Übergeben Sie eine nicht konstante Referenz:
Verwenden Sie diese Option, wenn die Funktion einen Wert in die Variable zurückschreiben muss, der letztendlich vom Aufrufer verwendet wird.
Genau wie der const-Referenzfall ist dies nicht threadsicher.
Übergeben Sie den const-Zeiger:
Funktionell identisch mit übergeben durch const-Referenz, mit Ausnahme der unterschiedlichen Syntax sowie der Tatsache, dass die aufrufende Funktion den NULL-Zeiger übergeben kann, um anzuzeigen, dass keine gültigen Daten übergeben werden müssen.
Nicht threadsicher.
Übergeben Sie einen nicht konstanten Zeiger:
Ähnlich wie bei einer nicht konstanten Referenz. Der Aufrufer setzt die Variable normalerweise auf NULL, wenn die Funktion keinen Wert zurückschreiben soll. Diese Konvention wird in vielen glibc-APIs verwendet. Beispiel:
Genau wie alle, die als Referenz / Zeiger übergeben werden, nicht threadsicher.
quelle
Da niemand erwähnt, dass ich es hinzufüge, wird beim Übergeben eines Objekts an eine Funktion in c ++ der Standardkopierkonstruktor des Objekts aufgerufen, wenn Sie keinen haben, der einen Klon des Objekts erstellt und ihn dann an die Methode übergibt Wenn Sie die Objektwerte ändern, die sich auf die Kopie des Objekts anstelle des ursprünglichen Objekts auswirken, ist dies das Problem in c ++. Wenn Sie also alle Klassenattribute als Zeiger festlegen, kopieren die Kopierkonstruktoren die Adressen des Zeigerattribute Wenn also die Methode das Objekt aufruft, das die in den Zeigerattributadressen gespeicherten Werte bearbeitet, werden die Änderungen auch im ursprünglichen Objekt wiedergegeben, das als Parameter übergeben wird. Dies kann sich also wie bei Java verhalten, aber vergessen Sie nicht, dass Ihre gesamte Klasse Attribute müssen Zeiger sein, außerdem sollten Sie die Werte von Zeigern ändern.wird mit Code-Erklärung viel klarer sein.
Dies ist jedoch keine gute Idee, da Sie am Ende viel Code mit Zeigern schreiben werden, die für Speicherlecks anfällig sind und nicht vergessen, Destruktoren aufzurufen. Um dies zu vermeiden, verfügt c ++ über Kopierkonstruktoren, in denen Sie neuen Speicher erstellen, wenn die Objekte, die Zeiger enthalten, an Funktionsargumente übergeben werden, die die Manipulation anderer Objektdaten stoppen. Java übergibt den Wert und der Wert ist die Referenz, sodass keine Kopierkonstruktoren erforderlich sind.
quelle
Es gibt drei Methoden, um ein Objekt als Parameter an eine Funktion zu übergeben:
Gehen Sie das folgende Beispiel durch:
Ausgabe:
quelle
Im Folgenden finden Sie die Möglichkeiten, Argumente / Parameter an C ++ zu übergeben.
1. nach Wert.
2. durch Bezugnahme.
3. nach Objekt.
quelle