Il existe certaines différences dans les conventions d'appel en C ++ et Java. En C ++, il n'y a techniquement que deux conventions: pass-by-value et pass-by-reference, avec de la littérature incluant une troisième convention pass-by-pointer (qui est en fait pass-by-value d'un type de pointeur). En plus de cela, vous pouvez ajouter de la constance au type d'argument, améliorant la sémantique.
Passer par référence
Le passage par référence signifie que la fonction recevra conceptuellement votre instance d'objet et non une copie de celle-ci. La référence est conceptuellement un alias de l'objet qui a été utilisé dans le contexte d'appel et ne peut pas être nulle. Toutes les opérations effectuées à l'intérieur de la fonction s'appliquent à l'objet en dehors de la fonction. Cette convention n'est pas disponible en Java ou C.
Passer par valeur (et passer par pointeur)
Le compilateur générera une copie de l'objet dans le contexte d'appel et utilisera cette copie à l'intérieur de la fonction. Toutes les opérations effectuées à l'intérieur de la fonction sont effectuées sur la copie, pas sur l'élément externe. Il s'agit de la convention pour les types primitifs en Java.
Une version spéciale de celui-ci passe un pointeur (adresse de l'objet) dans une fonction. La fonction reçoit le pointeur, et toutes les opérations appliquées au pointeur lui-même sont appliquées à la copie (pointeur), d'autre part, les opérations appliquées au pointeur déréférencé s'appliqueront à l'instance d'objet à cet emplacement de mémoire, donc la fonction peut avoir des effets secondaires. L'effet de l'utilisation de la valeur de passage d'un pointeur sur l'objet permettra à la fonction interne de modifier les valeurs externes, comme avec la référence par passage et permettra également des valeurs facultatives (passez un pointeur nul).
C'est la convention utilisée en C lorsqu'une fonction a besoin de modifier une variable externe, et la convention utilisée en Java avec les types de référence: la référence est copiée, mais l'objet référencé est le même: les modifications apportées à la référence / pointeur ne sont pas visibles à l'extérieur la fonction, mais les modifications apportées à la mémoire pointée sont.
Ajout de const à l'équation
En C ++, vous pouvez attribuer une constante aux objets lors de la définition de variables, de pointeurs et de références à différents niveaux. Vous pouvez déclarer une variable constante, vous pouvez déclarer une référence à une instance constante, et vous pouvez définir tous les pointeurs vers des objets constants, des pointeurs constants vers des objets mutables et des pointeurs constants vers des éléments constants. Inversement, en Java, vous ne pouvez définir qu'un seul niveau de constance (mot-clé final): celui de la variable (instance pour les types primitifs, référence pour les types de référence), mais vous ne pouvez pas définir de référence à un élément immuable (sauf si la classe elle-même est immuable).
Ceci est largement utilisé dans les conventions d'appel C ++. Lorsque les objets sont petits, vous pouvez passer l'objet par valeur. Le compilateur va générer une copie, mais cette copie n'est pas une opération coûteuse. Pour tout autre type, si la fonction ne change pas l'objet, vous pouvez passer une référence à une instance constante (généralement appelée référence constante) du type. Cela ne copiera pas l'objet, mais le passera dans la fonction. Mais en même temps, le compilateur garantira que l'objet n'est pas modifié à l'intérieur de la fonction.
Règles de base
Voici quelques règles de base à suivre:
- Préférez le passage par valeur pour les types primitifs
- Préférez le passage par référence avec des références à la constante pour d'autres types
- Si la fonction doit modifier l'argument, utilisez pass-by-reference
- Si l'argument est facultatif, utilisez pass-by-pointer (à constant si la valeur facultative ne doit pas être modifiée)
Il existe d'autres petits écarts par rapport à ces règles, le premier étant la gestion de la propriété d'un objet. Lorsqu'un objet est alloué dynamiquement avec new, il doit être désalloué avec delete (ou ses versions []). L'objet ou la fonction responsable de la destruction de l'objet est considéré comme le propriétaire de la ressource. Lorsqu'un objet alloué dynamiquement est créé dans un morceau de code, mais que la propriété est transférée vers un élément différent, cela se fait généralement avec une sémantique de passage par pointeur, ou si possible avec des pointeurs intelligents.
Note d'accompagnement
Il est important d'insister sur l'importance de la différence entre les références C ++ et Java. En C ++, les références sont conceptuellement l'instance de l'objet, pas un accesseur à celui-ci. L'exemple le plus simple consiste à implémenter une fonction d'échange:
// 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 );
}
La fonction d'échange ci-dessus modifie ses deux arguments par l'utilisation de références. Le code le plus proche en 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
}
}
La version Java du code modifiera les copies des références en interne, mais ne modifiera pas les objets réels en externe. Les références Java sont des pointeurs C sans arithmétique de pointeur qui sont passés par valeur dans les fonctions.