Existen algunas diferencias en las convenciones de llamadas en C ++ y Java. En C ++, técnicamente solo hay dos convenciones: paso por valor y paso por referencia, con cierta literatura que incluye una tercera convención de paso por puntero (que en realidad es paso por valor de un tipo de puntero). Además de eso, puede agregar constancia al tipo de argumento, mejorando la semántica.
Pase por referencia
Pasar por referencia significa que la función recibirá conceptualmente su instancia de objeto y no una copia de la misma. La referencia es conceptualmente un alias del objeto que se utilizó en el contexto de la llamada y no puede ser nulo. Todas las operaciones realizadas dentro de la función se aplican al objeto fuera de la función. Esta convención no está disponible en Java o C.
Pasar por valor (y pasar por puntero)
El compilador generará una copia del objeto en el contexto de la llamada y usará esa copia dentro de la función. Todas las operaciones realizadas dentro de la función se realizan en la copia, no en el elemento externo. Esta es la convención para tipos primitivos en Java.
Una versión especial de él es pasar un puntero (dirección del objeto) a una función. La función recibe el puntero, y todas y cada una de las operaciones aplicadas al puntero en sí se aplican a la copia (puntero), por otro lado, las operaciones aplicadas al puntero desreferenciado se aplicarán a la instancia del objeto en esa ubicación de memoria, por lo que la función puede tener efectos secundarios El efecto de usar el paso por valor de un puntero al objeto permitirá que la función interna modifique los valores externos, como con el paso por referencia y también permitirá valores opcionales (pasar un puntero nulo).
Esta es la convención usada en C cuando una función necesita modificar una variable externa, y la convención usada en Java con tipos de referencia: la referencia se copia, pero el objeto referido es el mismo: los cambios en la referencia / puntero no son visibles fuera la función, pero los cambios en la memoria puntiaguda son.
Agregar const a la ecuación
En C ++ puede asignar constancia a los objetos al definir variables, punteros y referencias en diferentes niveles. Puede declarar que una variable es constante, puede declarar una referencia a una instancia constante y puede definir todos los punteros a objetos constantes, punteros constantes a objetos mutables y punteros constantes a elementos constantes. Por el contrario, en Java solo puede definir un nivel de constancia (palabra clave final): el de la variable (instancia para tipos primitivos, referencia para tipos de referencia), pero no puede definir una referencia a un elemento inmutable (a menos que la clase en sí sea inmutable).
Esto se usa ampliamente en convenciones de llamadas de C ++. Cuando los objetos son pequeños, puede pasar el objeto por valor. El compilador generará una copia, pero esa copia no es una operación costosa. Para cualquier otro tipo, si la función no cambiará el objeto, puede pasar una referencia a una instancia constante (generalmente llamada referencia constante) del tipo. Esto no copiará el objeto, sino que lo pasará a la función. Pero al mismo tiempo, el compilador garantizará que el objeto no se cambie dentro de la función.
Reglas de juego
Estas son algunas reglas básicas a seguir:
- Prefiero pasar por valor para tipos primitivos
- Prefiere pasar por referencia con referencias a constante para otros tipos
- Si la función necesita modificar el argumento use pass-by-reference
- Si el argumento es opcional, use el paso por puntero (a constante si el valor opcional no debe modificarse)
Hay otras pequeñas desviaciones de estas reglas, la primera de las cuales es manejar la propiedad de un objeto. Cuando un objeto se asigna dinámicamente con nuevo, se debe desasignar con eliminar (o las versiones [] del mismo). El objeto o función responsable de la destrucción del objeto se considera el propietario del recurso. Cuando se crea un objeto asignado dinámicamente en un fragmento de código, pero la propiedad se transfiere a un elemento diferente, generalmente se hace con una semántica de paso por puntero, o si es posible con punteros inteligentes.
Nota al margen
Es importante insistir en la importancia de la diferencia entre las referencias de C ++ y Java. En C ++, las referencias son conceptualmente la instancia del objeto, no un descriptor de acceso al mismo. El ejemplo más simple es implementar una función de intercambio:
// 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 función de intercambio anterior cambia sus argumentos mediante el uso de referencias. El código más cercano 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 versión Java del código modificará las copias de las referencias internamente, pero no modificará los objetos reales externamente. Las referencias Java son punteros en C sin aritmética de punteros que se pasan por valor a las funciones.