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:
- Bevorzugen Sie die Übergabe von Werten für primitive Typen
- Bevorzugen Sie die Referenzübergabe mit Verweisen auf die Konstante für andere Typen
- Wenn die Funktion das Argument ändern muss, verwenden Sie die Referenzübergabe
- Wenn das Argument optional ist, verwenden Sie den Pass-by-Pointer (zur Konstante, wenn der optionale Wert nicht geändert werden soll).
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:
// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
Type tmp = a;
a = b;
b = tmp;
}
int main() {
Type a, b;
Type old_a = a, old_b = b;
swap( a, b );
assert( a == old_b );
assert( b == old_a );
}
Die obige Swap-Funktion ändert beide Argumente durch die Verwendung von Referenzen. Der nächstgelegene Code in Java:
public class C {
// ...
public static void swap( C a, C b ) {
C tmp = a;
a = b;
b = tmp;
}
public static void main( String args[] ) {
C a = new C();
C b = new C();
C old_a = a;
C old_b = b;
swap( a, b );
// a and b remain unchanged a==old_a, and b==old_b
}
}
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.