En ciertos lenguajes de programación informática , los tipos de datos se clasifican como tipos de valor o tipos de referencia , donde a los tipos de referencia siempre se accede implícitamente a través de referencias , mientras que las variables de tipo de valor contienen directamente los valores en sí. [1] [2]
Incluso entre los idiomas que tienen esta distinción, las propiedades exactas de los tipos de valor y referencia varían de un idioma a otro, pero las propiedades típicas incluyen:
int
(un tipo de valor) en un Integer
objeto (un tipo de objeto) o revertir esto mediante "desencajar".Incluso cuando los argumentos de una función se pasan utilizando la semántica de "llamada por valor" (que siempre es el caso en Java y es el caso por defecto en C#), un valor de un tipo de referencia es intrínsecamente una referencia; por lo tanto, si un parámetro pertenece a un tipo de referencia, el comportamiento resultante tiene cierta similitud con la semántica de "llamada por referencia". Este comportamiento a veces se denomina llamada por uso compartido .
La llamada por compartir se parece a la llamada por referencia en el caso en que una función muta un objeto que recibió como argumento: cuando eso sucede, la mutación también será visible para el que llama, porque el que llama y la función tienen referencias al mismo objeto. Se diferencia de la llamada por referencia en el caso en que una función asigna su parámetro a una referencia diferente; cuando eso sucede, esta asignación no será visible para el que llama, porque el que llama y la función tienen referencias separadas , aunque ambas referencias apuntan inicialmente al mismo objeto.
Muchos lenguajes tienen punteros o referencias explícitas . Los tipos de referencia se diferencian de estos en que las entidades a las que hacen referencia siempre se acceden a través de referencias; por ejemplo, mientras que en C++ es posible tener a y a , donde el primero es una cadena mutable y el segundo es un puntero explícito a una cadena mutable (a menos que sea un puntero nulo), en Java solo es posible tener a , que es implícitamente una referencia a una cadena mutable (a menos que sea una referencia nula).std::string
std::string *
StringBuilder
Si bien el enfoque de C++ es más flexible, el uso de no referencias puede llevar a problemas como la segmentación de objetos , al menos cuando se utiliza herencia ; en lenguajes donde los objetos pertenecen a tipos de referencia, estos problemas se evitan automáticamente, a costa de eliminar algunas opciones del programador.
En la mayoría de los lenguajes de programación, es posible cambiar la variable de un tipo de referencia para hacer referencia a otro objeto, es decir, volver a vincular la variable a otro objeto.
Por ejemplo, en el siguiente código Java:
clase Foo { público int a ; } Foo a = nuevo Foo (); Foo b = a ; a . prop = 3 ; a = nuevo Foo (); a . prop = 1 ;
Foo
es un tipo de referencia, donde a
inicialmente se le asigna una referencia de un nuevo objeto, y b
se asigna a la misma referencia de objeto, es decir, está vinculado al mismo objeto que a
, por lo tanto, los cambios a través de a
también son visibles para b
también. Después, a
se le asigna una referencia (rebound) a otro nuevo objeto, y ahora a
y b
hacen referencia a objetos diferentes. Al final, a
hace referencia al segundo objeto con su prop
campo que tiene el valor 1
, mientras que b
hace referencia al primer objeto con su prop
campo que tiene el valor 3
.
Sin embargo, como en C++, el término "tipo de referencia" se utiliza para significar un alias, y no es posible volver a vincular una variable de un tipo de referencia una vez creada, ya que es un alias del objeto original.
estructura Foo { int a ; }; Foo a ; a . prop = 1 ; Foo & b = a ; Foo c = a ; a . prop = 3 ;
En C++, todos los tipos de clase que no son de referencia tienen semántica de valor. En el ejemplo anterior, b
se declara como una referencia (alias) de a
, y para todos los fines, a
y b
son lo mismo. Es imposible volver a enlazarlo b
para que se convierta en otra cosa. Después de ejecutar el ejemplo anterior, a
y b
son el mismo Foo
objeto con prop
, 3
mientras que c
es una copia del original a
con .prop
1
En C#, aparte de la distinción entre tipos de valor y tipos de referencia, también existe un concepto separado llamado variables de referencia. [3] Una variable de referencia, una vez declarada y enlazada, se comporta como un alias de la variable original, pero también se puede enlazar de nuevo a otra variable mediante el operador de asignación de referencia = ref
. La variable en sí puede ser de cualquier tipo, incluidos los tipos de valor y los tipos de referencia, es decir, al pasar una variable de un tipo de referencia por referencia (alias) a una función, también se puede cambiar el objeto al que apunta la variable de tipo de referencia, además del objeto en sí (si es mutable).
Si un objeto es inmutable y la igualdad de objetos se prueba en función del contenido en lugar de la identidad, la distinción entre el tipo de valor y los tipos de referencia ya no es clara, porque el objeto en sí no se puede modificar, sino solo reemplazar como un todo (para el tipo de valor) / con la referencia apuntando a otro objeto (para el tipo de referencia). Pasar tales objetos inmutables entre variables no tiene diferencias observables si el objeto se copia o se pasa por referencia, a menos que se tome la identidad del objeto. En un lenguaje de programación funcional donde nada es mutable (como Haskell), dicha distinción no existe en absoluto y se convierte en un detalle de implementación.