Diferencia entre revisiones de «Referencias (programación)»

Línea 1: Línea 1:
{{Normalizar|motivo= Enriquecer con híper vínculos necesarios, estructurar bien los encabezados según el [[Manual de Estilo]].}}
 
 
<div align="justify">
 
<div align="justify">
{{Definición|Nombre=Referencias (programación)|imagen=   |concepto=Son un tipo de dato estrechamente relacionado con los punteros. Es un recurso para pasar argumentos a funciones permitiendo que los argumentos no sean simples variables locales de la función, sino objetos del ámbito que realiza la invocación
+
{{Definición
}}'''Referencias (programación).''' Son un tipo de dato estrechamente relacionado con los punteros. Una referencia de un objeto no es un objeto, en el sentido que no tiene su propio espacio de almacenamiento como ocurre con los punteros, y en consecuencia no pueden realizarse con ellas muchas de las operaciones que se relacionan con objetos. Por ejemplo obtener su dirección, crearlas con el operador new, o crear matrices de referencias.
+
|Nombre=Referencias (programación)
 
+
|imagen=
Una referencia es una especie de alias o "alter ego" del objeto. Es un recurso para pasar argumentos a funciones permitiendo que los argumentos no sean simples variables locales de la función, sino objetos del ámbito que realiza la invocación, lo que permite que la función pueda modificar objetos externos a ella.
+
|concepto=Son un tipo de dato estrechamente relacionado con los punteros. Es un recurso para pasar argumentos a funciones permitiendo que los argumentos no sean simples variables locales de la función, sino objetos del ámbito que realiza la invocación
 +
}}
 +
'''Referencias (programación) '''. Son un tipo de dato estrechamente relacionado con los punteros. Una referencia de un objeto no es un objeto, en el sentido que no tiene su propio espacio de almacenamiento como ocurre con los punteros, y en consecuencia no pueden realizarse con ellas muchas de las operaciones que se relacionan con objetos. Por ejemplo obtener su dirección, crearlas con el operador nuevo, o crear matrices de referencias.
 +
 +
Una referencia es una especie de alias o "alter ego" del objeto. Es un recurso para pasar argumentos a funciones permitiendo que los argumentos no sean simples variables locales de la función, sino objetos del ámbito que realiza la invocación, lo que permite que la [[función]] pueda modificar [[objetos externos]] a ella.
 
== Sintaxis: ==
 
== Sintaxis: ==
La declaración de una [[Variable en programación|variable]] de este tipo se realiza mediante el declarador de referencia &. La sintaxis general es:
+
La declaración de una [[Variable en programación|variable]] de este tipo se realiza mediante el declarador de referencia. La sintaxis general es:
<tipo_objeto> & <etiqueta_referencia> [ = <iniciador> ]
+
<tipo_objeto> & <etiqueta_referencia> [= <iniciador>]
 
Ejemplo:
 
Ejemplo:
 
int x;
 
int x;
 
...
 
...
int & z = x; // decimos que x es el 'iniciador' y que z es la 'referencia'
+
int & z = x; // decimos que x es el 'iniciador' y que z es la 'referencia'
Estas sentencias declaran e inicia la variable z como referencia-a-entero, y la asocia con la variable x (que es un entero). En adelante z actúa como un alias de x, de forma que cualquier operación sobre z equivale a hacerla sobre x. En realidad puede considerarse que z es "casi" un sinónimo de x (como si fuesen la misma variable).  
+
Estas [[sentencias]] declaran e inicia la variable z como referencia-a-entero, y la asocia con la variable x (que es un entero). En adelante z actúa como un alias de x, de forma que cualquier operación sobre z equivale a hacerla sobre x. En realidad puede considerarse que z es "casi" un sinónimo de x (como si fuesen la misma [[variable]]).  
 
== Observaciones ==
 
== Observaciones ==
En muchas situaciones prácticas puede ser indiferente utilizar una referencia o un puntero, sin embargo mantienen importantes diferencias que conviene conocer y que resumimos en el cuadro adjunto antes de comentarlas más detenidamente. En cualquier caso, la mejor regla es recordar que las referencias pueden considerarse como un sustituto del identificador de un objeto, mientras que los punteros deben ser considerados siempre como objetos en sí mismos. Además sus aritméticas son distintas.  
+
En muchas situaciones prácticas puede ser indiferente utilizar una referencia o un puntero, sin embargo mantienen importantes diferencias que conviene conocer y que resumimos en el cuadro adjunto antes de comentarlas más detenidamente. En cualquier caso, la mejor regla es recordar que las referencias pueden considerarse como un sustituto del identificador de un objeto, mientras que los punteros deben ser considerados siempre como objetos en sí mismos. Además sus [[aritméticas]] son distintas.  
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Línea 23: Línea 26:
 
| Declaración independiente de la definición
 
| Declaración independiente de la definición
 
| No
 
| No
| Si
+
| Si  
 
|-
 
|-
 
| Asignarles un nuevo valor
 
| Asignarles un nuevo valor
 
| No
 
| No
| Si
+
| Si  
 
|-
 
|-
 
| Referirse a un objeto de su mismo tipo
 
| Referirse a un objeto de su mismo tipo
 
| No
 
| No
| Si
+
| Si  
 
|-
 
|-
 
| Asignarles el valor void
 
| Asignarles el valor void
 
| No
 
| No
| Si
+
| Si  
 
|}
 
|}
== Declaración ==
+
== Declaración ==
Las referencias no pueden ser declaradas aisladas, de forma que tienen que estar indefectiblemente unidas a un objeto en su propia definición (deben ser inicializadas en la declaración). Además, una vez declaradas no pueden ser reasignadas a otro objeto (como los punteros), por lo que resultan unidas de por vida al objeto inicial. Por ejemplo:
+
Las referencias no pueden ser declaradas aisladas, de forma que tienen que estar indefectiblemente unidas a un objeto en su propia [[definición]] (deben ser inicializadas en la declaración). Además, una vez declaradas no pueden ser reasignadas a otro objeto (como los punteros), por lo que resultan unidas de por vida al objeto inicial. Por ejemplo:
int& z;       // Error.
+
int& z; // Error.
int& z = x   // Ok.
+
int& z = x // Ok.
int& z = y;   // Error.
+
int& z = y; // Error.
 
Por la razón anterior, puesto que tienen que estar unidas a un objeto, no pueden referenciar a void:
 
Por la razón anterior, puesto que tienen que estar unidas a un objeto, no pueden referenciar a void:
int& z = void;   // Error.
+
int& z = void; // Error.
 
Sí pueden ser inicializadas a otra referencia del mismo tipo, en cuyo caso señalan al objeto inicial.  
 
Sí pueden ser inicializadas a otra referencia del mismo tipo, en cuyo caso señalan al objeto inicial.  
 
+
Las referencias son una especie de punteros constantes, pero que no aceptan el álgebra de punteros ni pueden ser manipuladas igual.
+
Las referencias son una especie de punteros constantes, pero que no aceptan el [[álgebra de punteros]] ni pueden ser manipuladas igual.
 
== Reasignación de referencias ==
 
== Reasignación de referencias ==
 
Después de la definición inicial, las sucesivas asignaciones a las referencias deben ser con objetos del mismo tipo. Por ejemplo, a una referencia-a-int solo se le pueden asignar tipos int. Pero estas asignaciones son en realidad al objeto inicialmente referenciado. Ejemplo:
 
Después de la definición inicial, las sucesivas asignaciones a las referencias deben ser con objetos del mismo tipo. Por ejemplo, a una referencia-a-int solo se le pueden asignar tipos int. Pero estas asignaciones son en realidad al objeto inicialmente referenciado. Ejemplo:
 
int x = 10, y = 20;
 
int x = 10, y = 20;
int& refi = x;             // definición inicial
+
int& refi = x; // definición inicial
cout << "X = " << refi;   // -> X = 10 (valor de x)
+
cout << "X = " << refi; // -> X = 10 (valor de x)
refi = y;                 // Ojo!! Equivale a: x = y
+
refi = y; // Ojo!! Equivale a: x = y
cout << "X = " << refi;   // -> X = 20 (x es ahora 20)
+
cout << "X = " << refi; // -> X = 20 (x es ahora 20)
cout << "X = " << x;       // -> X = 20 (comprobación)
+
cout << "X = " << x; // -> X = 20 (comprobación)
== Modelado ==
+
== Modelado ==
 
En ocasiones el [[compilador]] realiza automáticamente determinadas promociones de tipo para hacer posible la asignación. Por ejemplo, siguiendo con las definiciones anteriores:
 
En ocasiones el [[compilador]] realiza automáticamente determinadas promociones de tipo para hacer posible la asignación. Por ejemplo, siguiendo con las definiciones anteriores:
 
enum COLOR { ROJO, VERDE, AZUL};
 
enum COLOR { ROJO, VERDE, AZUL};
 
COLOR c1 = VERDE;
 
COLOR c1 = VERDE;
refi = c1;                     // L.3: Ok!! (ahora x = 1)
+
refi = c1; // L.3: Ok!! (Ahora x = 1)
cout << "X = " << refi;       // -> X = 1
+
cout << "X = " << refi; // -> X = 1
 
float f = 12.5;
 
float f = 12.5;
refi = f;                     // L.6: Ok!!
+
refi = f; // L.6: Ok!!
cout << "F = " << f;           // -> F = 12.5
+
cout << "F = " << f; // -> F = 12.5
cout << "X = " << refi;       // -> X = 12 (Atención!!)
+
cout << "X = " << refi; // -> X = 12 (Atención!!)
En la asignación L.3, la variable enumerada c1 es promovida a entero (cosa perfectamente factible, el resultado es asignado a refi, lo que a la postre equivale a asignarlo a x. Un caso parecido es el de la asignación L.6, donde el float f es promovido a entero (lo que implica una pérdida de precisión) y el resultado asignado nuevamente a refi, la diferencia resultante entre f y x se muestra en las dos últimas salidas.
+
En la asignación L.3, la variable enumerada c1 es promovida a [[entero]] (cosa perfectamente factible, el resultado es asignado a refi, lo que a la postre equivale a asignarlo a x. Un caso parecido es el de la asignación L.6, donde el float f es promovido a entero (lo que implica una pérdida de precisión) y el resultado asignado nuevamente a refi, la diferencia resultante entre f y x se muestra en las dos últimas salidas.
 
== Punteros y referencias a referencias ==
 
== Punteros y referencias a referencias ==
 
No es posible declarar punteros-a-referencias ni referencias-a-referencias:
 
No es posible declarar punteros-a-referencias ni referencias-a-referencias:
 
int x = 10;
 
int x = 10;
int& rti = x;       // Ok referencia-a-x
+
int& rti = x; // Ok referencia-a-x
int&* ptrti = &rti // Error!! Puntero-a-referencia
+
int&* ptrti = &rti // Error!! Puntero-a-referencia
int&& rar = rti     // Error!! Referencia-a-referencia
+
int&& rar = rti // Error!! Referencia-a-referencia
 
+
 
Puesto que las referencias-a-tipoX son en realidad un "alter ego" del tipo referenciado, sí es posible utilizar referencias para la definición de otras referencias al mismo tipo:
 
Puesto que las referencias-a-tipoX son en realidad un "alter ego" del tipo referenciado, sí es posible utilizar referencias para la definición de otras referencias al mismo tipo:
int& rti2 = rti;   // Ok otra referencia-a-x
+
int& rti2 = rti; // Ok otra referencia-a-x
 
En contra de lo que ocurre con sus parientes cercanos los punteros, no es posible iniciar referencias con el operador new.
 
En contra de lo que ocurre con sus parientes cercanos los punteros, no es posible iniciar referencias con el operador new.
 
== Referencias a punteros ==
 
== Referencias a punteros ==
 
Aunque de poca importancia práctica, la referencia-a-puntero, es un alias que puede ser utilizado a todos los efectos como si fuese el propio puntero. Ejemplo:
 
Aunque de poca importancia práctica, la referencia-a-puntero, es un alias que puede ser utilizado a todos los efectos como si fuese el propio puntero. Ejemplo:
 
#include <iostream.h>
 
#include <iostream.h>
int main() {               // ======
+
int main() { // ======
 
int x = 10;
 
int x = 10;
int* ptr = &x;           // puntero-a-int
+
int* ptr = &x; // puntero-a-int
int*& ref = ptr;         // referencia-a-puntero-a-int
+
int*& ref = ptr; // referencia-a-puntero-a-int
 
cout << "ptr-a-X = " << ref << endl;
 
cout << "ptr-a-X = " << ref << endl;
 
cout << "ptr-a-X = " << ptr << endl;
 
cout << "ptr-a-X = " << ptr << endl;
cout << "Valor X = " << *ref << endl; // M.6
+
cout << "Valor X = " << *ref << endl; // M.6
 
cout << "Valor X = " << *ptr << endl;
 
cout << "Valor X = " << *ptr << endl;
 
}
 
}
Línea 93: Línea 96:
 
Valor X = 10
 
Valor X = 10
 
Valor X = 10
 
Valor X = 10
En este ejemplo es digna de mención la forma de declaración de ref, referencia-a-puntero; y como a todos los efectos, incluso para aplicarle el operador de indirección * , la referencia se comporta en M.6 como un alias perfecto del puntero, señalando al mismo objeto que aquel.
+
En este ejemplo es digna de mención la forma de declaración de ref, referencia-a-puntero; y como a todos los efectos, incluso para aplicarle el operador de in dirección *, la referencia se comporta en M.6 como un alias perfecto del puntero, señalando al mismo objeto que aquel.
 
== Referencias a funciones ==
 
== Referencias a funciones ==
[[C++]] permite definir referencias a [[Función (programación)|funciones]], aunque carecen de importancia práctica. La sintaxis para su declaración es la misma que con los punteros, aunque como es usual, su inicialización debe hacerse en el punto de la declaración. Así mismo, pueden invocarse funciones a través de sus referencias como si se tratara de punteros.
+
[[C++]] permite definir referencias a [[Función (programación)|funciones]], aunque carecen de importancia práctica. La sintaxis para su declaración es la misma que con los punteros, aunque como es usual, su inicialización debe hacerse en el punto de la declaración. Así mismo, pueden invocarse [[funciones]] a través de sus referencias como si se tratara de [[punteros]].
 
+
 
Si en la declaración de una referencia, el iniciador es una constante o un objeto de tipo diferente que el referenciado, entonces se crea un objeto temporal para el que la referencia actúa como un alias. Considere el siguiente ejemplo:
 
Si en la declaración de una referencia, el iniciador es una constante o un objeto de tipo diferente que el referenciado, entonces se crea un objeto temporal para el que la referencia actúa como un alias. Considere el siguiente ejemplo:
int& z = 6;
+
int& z = 6;
Se crea un objeto temporal tipo int que recibe el valor 6; se crea también una referencia-a-int de nemónico z, a ese objeto temporal. El compilador avisa de esta circunstancia con una advertencia: Temporary used to initialize 'z' in function.... el objeto temporal solo es accesible a través de su referencia.
+
Se crea un objeto temporal tipo int que recibe el valor 6; se crea también una referencia-a-int de nemónico z, a ese objeto temporal. El [[compilador]] avisa de esta circunstancia con una advertencia: Temporary used to initialize 'z' in function.... el objeto temporal solo es accesible a través de su referencia.
 
 
== Argumentos por referencia ==
 
== Argumentos por referencia ==
 
La razón última de la introducción de referencias en [[C++]] es posibilitar la [[Sobrecarga de operadores|sobrecarga de operadores]], en la práctica su uso más frecuente es el paso de argumentos a funciones "por referencia"; en especial cuando se trata de objetos definidos por el usuario (instancias de clases). Observe que mientras en C clásico solo se pasan argumentos por valor, en [[C++]] es posible pasar argumentos por valor y por referencia.
 
La razón última de la introducción de referencias en [[C++]] es posibilitar la [[Sobrecarga de operadores|sobrecarga de operadores]], en la práctica su uso más frecuente es el paso de argumentos a funciones "por referencia"; en especial cuando se trata de objetos definidos por el usuario (instancias de clases). Observe que mientras en C clásico solo se pasan argumentos por valor, en [[C++]] es posible pasar argumentos por valor y por referencia.
 
Ejemplo:
 
Ejemplo:
void func1 (int);   // declara argumento de tipo int
+
void func1 (int); // declara argumento de tipo int
void func2 (int&); // declara argumento de tipo referencia-a-int
+
void func2 (int&); // declara argumento de tipo referencia-a-int
 
...
 
...
 
int sum = 3;
 
int sum = 3;
func1(sum);         // sum pasa por valor
+
func1(sum); // sum pasa por valor
func2(sum);         // sum pasa por referencia
+
func2(sum); // sum pasa por referencia
 
+
 
La utilización del argumento es idéntica en la invocación de ambas funciones, como si fuese "por valor". La diferencia en uno y otro caso estriba solo en la forma de declarar los argumentos en la definición de la función.
 
La utilización del argumento es idéntica en la invocación de ambas funciones, como si fuese "por valor". La diferencia en uno y otro caso estriba solo en la forma de declarar los argumentos en la definición de la función.
 
+
El argumento sum pasado por referencia en func2, puede ser modificado directamente desde dentro de esta función. Por contra, en func1 el argumento pasa por valor; la función recibe una copia de la variable sum, por lo que no puede modificar el valor original (la variable sum existente fuera de la función func1).  
+
El [[argumento]] sum pasado por referencia en func2, puede ser modificado directamente desde dentro de esta función. Por contra, en func1 el argumento pasa por valor; la función recibe una copia de la variable sum, por lo que no puede modificar el valor original (la variable sum existente fuera de la función func1).  
 
+
 
En ocasiones, especialmente cuando los argumentos son objetos (instancias de clases), el verdadero motivo de pasar objetos "por referencia" no es precisamente para que la función pueda modificar el argumento (incluso se intenta evitar esto declarando el argumento como referencia constante), sino por razones de eficacia del [[Código fuente|código]].
 
En ocasiones, especialmente cuando los argumentos son objetos (instancias de clases), el verdadero motivo de pasar objetos "por referencia" no es precisamente para que la función pueda modificar el argumento (incluso se intenta evitar esto declarando el argumento como referencia constante), sino por razones de eficacia del [[Código fuente|código]].
 
Más que alguna nueva "funcionalidad", el paso de argumentos a funciones "por referencia" solo proporciona cierta comodidad adicional a la funcionalidad proporcionada por los punteros
 
Más que alguna nueva "funcionalidad", el paso de argumentos a funciones "por referencia" solo proporciona cierta comodidad adicional a la funcionalidad proporcionada por los punteros
 
== Valores devueltos por referencia ==
 
== Valores devueltos por referencia ==
Otro uso común de este tipo de objetos es su utilización como valor de retorno de una función. Es el caso del valor devuelto por la función adjunta
+
Otro uso común de este tipo de objetos es su utilización como valor de retorno de una función. Es el caso del [[valor]] devuelto por la función adjunta
int& max (int& a, int& b) { if (a >= b) return a; return b; }
+
int& max (int& a, int& b) { if (a >= b) return a; return b; }
== Criterios de eficiencia ==
+
== Criterios de eficiencia ==
En ocasiones la utilización de referencias para el paso de argumentos a [[Función (programación)|funciones]] o en el valor devuelto, está motivada solo por criterios de eficacia. En efecto, las secuencias de llamada y retorno de funciones implican la creación de todas las [[Variable en programación|variables]] locales de la función (incluyendo el valor que será devuelto), así como la invocación del constructor-copia para todos los argumentos que no han sido pasados por referencia. Cuando estos objetos son muy grandes. Por ejemplo, en instancias de clases con muchos datos o que derivan de jerarquías complejas, se requieren procesos de creación y destrucción, a veces muy complicados, que pueden evitarse parcialmente utilizando referencias. En tales casos suele recurrirse a declarar constantes los argumentos pasados "por referencia" para evitar que la función pueda alterar su valor.
+
En ocasiones la utilización de referencias para el paso de argumentos a [[Función (programación)|funciones]] o en el valor devuelto, está motivada solo por criterios de eficacia. En efecto, las secuencias de llamada y retorno de funciones implican la creación de todas las [[Variable en programación|variables]] locales de la función (incluyendo el valor que será devuelto), así como la invocación del constructor-copia para todos los argumentos que no han sido pasados por referencia. Cuando estos objetos son muy grandes. Por ejemplo, en instancias de clases con muchos datos o que derivan de [[jerarquías]] complejas, se requieren procesos de creación y destrucción, a veces muy complicados, que pueden evitarse parcialmente utilizando referencias. En tales casos suele recurrirse a declarar constantes los argumentos pasados "por referencia" para evitar que la función pueda alterar su valor.
== Puede Consultar ==
+
== Puede Consultar ==
 
*[[Declaración de funciones|Declaración de funciones]]  
 
*[[Declaración de funciones|Declaración de funciones]]  
 
*[[Función (programación)|Función (programación)]]
 
*[[Función (programación)|Función (programación)]]
Línea 128: Línea 130:
 
*[[Parámetros y argumentos (programación)|Parámetros y argumentos (programación)]]
 
*[[Parámetros y argumentos (programación)|Parámetros y argumentos (programación)]]
 
*[[Invocación de funciones (programación)|Invocación de funciones (programación)]]
 
*[[Invocación de funciones (programación)|Invocación de funciones (programación)]]
== Fuente ==
+
== Fuente ==
 
* [http://www.zator.com/Cpp/E4_2_3.htm Referencias]
 
* [http://www.zator.com/Cpp/E4_2_3.htm Referencias]
 
* [http://www.mitecnologico.com/Main/ReferenciasProgramacion Referencias Programacion]
 
* [http://www.mitecnologico.com/Main/ReferenciasProgramacion Referencias Programacion]
 
* [http://unoyunodiez.wordpress.com/2011/03/01/el-lenguaje-de-programacion-c-referencias/ El lenguaje de programación C++: referencias]
 
* [http://unoyunodiez.wordpress.com/2011/03/01/el-lenguaje-de-programacion-c-referencias/ El lenguaje de programación C++: referencias]
 
+
 
[[Category:Informática]] [[Category:Lenguajes_de_programación]] [[Category:Programación]]
 
[[Category:Informática]] [[Category:Lenguajes_de_programación]] [[Category:Programación]]

Revisión del 14:07 8 mar 2012

Referencias (programación)
Información sobre la plantilla
Concepto:Son un tipo de dato estrechamente relacionado con los punteros. Es un recurso para pasar argumentos a funciones permitiendo que los argumentos no sean simples variables locales de la función, sino objetos del ámbito que realiza la invocación

Referencias (programación) . Son un tipo de dato estrechamente relacionado con los punteros. Una referencia de un objeto no es un objeto, en el sentido que no tiene su propio espacio de almacenamiento como ocurre con los punteros, y en consecuencia no pueden realizarse con ellas muchas de las operaciones que se relacionan con objetos. Por ejemplo obtener su dirección, crearlas con el operador nuevo, o crear matrices de referencias.

Una referencia es una especie de alias o "alter ego" del objeto. Es un recurso para pasar argumentos a funciones permitiendo que los argumentos no sean simples variables locales de la función, sino objetos del ámbito que realiza la invocación, lo que permite que la función pueda modificar objetos externos a ella.

Sintaxis:

La declaración de una variable de este tipo se realiza mediante el declarador de referencia. La sintaxis general es: <tipo_objeto> & <etiqueta_referencia> [= <iniciador>] Ejemplo: int x; ... int & z = x; // decimos que x es el 'iniciador' y que z es la 'referencia' Estas sentencias declaran e inicia la variable z como referencia-a-entero, y la asocia con la variable x (que es un entero). En adelante z actúa como un alias de x, de forma que cualquier operación sobre z equivale a hacerla sobre x. En realidad puede considerarse que z es "casi" un sinónimo de x (como si fuesen la misma variable).

Observaciones

En muchas situaciones prácticas puede ser indiferente utilizar una referencia o un puntero, sin embargo mantienen importantes diferencias que conviene conocer y que resumimos en el cuadro adjunto antes de comentarlas más detenidamente. En cualquier caso, la mejor regla es recordar que las referencias pueden considerarse como un sustituto del identificador de un objeto, mientras que los punteros deben ser considerados siempre como objetos en sí mismos. Además sus aritméticas son distintas.

Posibilidad de Referencias Punteros
Declaración independiente de la definición No Si
Asignarles un nuevo valor No Si
Referirse a un objeto de su mismo tipo No Si
Asignarles el valor void No Si

Declaración

Las referencias no pueden ser declaradas aisladas, de forma que tienen que estar indefectiblemente unidas a un objeto en su propia definición (deben ser inicializadas en la declaración). Además, una vez declaradas no pueden ser reasignadas a otro objeto (como los punteros), por lo que resultan unidas de por vida al objeto inicial. Por ejemplo: int& z; // Error. int& z = x // Ok. int& z = y; // Error. Por la razón anterior, puesto que tienen que estar unidas a un objeto, no pueden referenciar a void: int& z = void; // Error. Sí pueden ser inicializadas a otra referencia del mismo tipo, en cuyo caso señalan al objeto inicial.

Las referencias son una especie de punteros constantes, pero que no aceptan el álgebra de punteros ni pueden ser manipuladas igual.

Reasignación de referencias

Después de la definición inicial, las sucesivas asignaciones a las referencias deben ser con objetos del mismo tipo. Por ejemplo, a una referencia-a-int solo se le pueden asignar tipos int. Pero estas asignaciones son en realidad al objeto inicialmente referenciado. Ejemplo: int x = 10, y = 20; int& refi = x; // definición inicial cout << "X = " << refi; // -> X = 10 (valor de x) refi = y; // Ojo!! Equivale a: x = y cout << "X = " << refi; // -> X = 20 (x es ahora 20) cout << "X = " << x; // -> X = 20 (comprobación)

Modelado

En ocasiones el compilador realiza automáticamente determinadas promociones de tipo para hacer posible la asignación. Por ejemplo, siguiendo con las definiciones anteriores: enum COLOR { ROJO, VERDE, AZUL}; COLOR c1 = VERDE; refi = c1; // L.3: Ok!! (Ahora x = 1) cout << "X = " << refi; // -> X = 1 float f = 12.5; refi = f; // L.6: Ok!! cout << "F = " << f; // -> F = 12.5 cout << "X = " << refi; // -> X = 12 (Atención!!) En la asignación L.3, la variable enumerada c1 es promovida a entero (cosa perfectamente factible, el resultado es asignado a refi, lo que a la postre equivale a asignarlo a x. Un caso parecido es el de la asignación L.6, donde el float f es promovido a entero (lo que implica una pérdida de precisión) y el resultado asignado nuevamente a refi, la diferencia resultante entre f y x se muestra en las dos últimas salidas.

Punteros y referencias a referencias

No es posible declarar punteros-a-referencias ni referencias-a-referencias: int x = 10; int& rti = x; // Ok referencia-a-x int&* ptrti = &rti // Error!! Puntero-a-referencia int&& rar = rti // Error!! Referencia-a-referencia

Puesto que las referencias-a-tipoX son en realidad un "alter ego" del tipo referenciado, sí es posible utilizar referencias para la definición de otras referencias al mismo tipo: int& rti2 = rti; // Ok otra referencia-a-x En contra de lo que ocurre con sus parientes cercanos los punteros, no es posible iniciar referencias con el operador new.

Referencias a punteros

Aunque de poca importancia práctica, la referencia-a-puntero, es un alias que puede ser utilizado a todos los efectos como si fuese el propio puntero. Ejemplo:

  1. include <iostream.h>

int main() { // ====== int x = 10; int* ptr = &x; // puntero-a-int int*& ref = ptr; // referencia-a-puntero-a-int cout << "ptr-a-X = " << ref << endl; cout << "ptr-a-X = " << ptr << endl; cout << "Valor X = " << *ref << endl; // M.6 cout << "Valor X = " << *ptr << endl; } Salida: ptr-a-X = 0065FE00 ptr-a-X = 0065FE00 Valor X = 10 Valor X = 10 En este ejemplo es digna de mención la forma de declaración de ref, referencia-a-puntero; y como a todos los efectos, incluso para aplicarle el operador de in dirección *, la referencia se comporta en M.6 como un alias perfecto del puntero, señalando al mismo objeto que aquel.

Referencias a funciones

C++ permite definir referencias a funciones, aunque carecen de importancia práctica. La sintaxis para su declaración es la misma que con los punteros, aunque como es usual, su inicialización debe hacerse en el punto de la declaración. Así mismo, pueden invocarse funciones a través de sus referencias como si se tratara de punteros.

Si en la declaración de una referencia, el iniciador es una constante o un objeto de tipo diferente que el referenciado, entonces se crea un objeto temporal para el que la referencia actúa como un alias. Considere el siguiente ejemplo: int& z = 6; Se crea un objeto temporal tipo int que recibe el valor 6; se crea también una referencia-a-int de nemónico z, a ese objeto temporal. El compilador avisa de esta circunstancia con una advertencia: Temporary used to initialize 'z' in function.... el objeto temporal solo es accesible a través de su referencia.

Argumentos por referencia

La razón última de la introducción de referencias en C++ es posibilitar la sobrecarga de operadores, en la práctica su uso más frecuente es el paso de argumentos a funciones "por referencia"; en especial cuando se trata de objetos definidos por el usuario (instancias de clases). Observe que mientras en C clásico solo se pasan argumentos por valor, en C++ es posible pasar argumentos por valor y por referencia. Ejemplo: void func1 (int); // declara argumento de tipo int void func2 (int&); // declara argumento de tipo referencia-a-int ... int sum = 3; func1(sum); // sum pasa por valor func2(sum); // sum pasa por referencia

La utilización del argumento es idéntica en la invocación de ambas funciones, como si fuese "por valor". La diferencia en uno y otro caso estriba solo en la forma de declarar los argumentos en la definición de la función.

El argumento sum pasado por referencia en func2, puede ser modificado directamente desde dentro de esta función. Por contra, en func1 el argumento pasa por valor; la función recibe una copia de la variable sum, por lo que no puede modificar el valor original (la variable sum existente fuera de la función func1).

En ocasiones, especialmente cuando los argumentos son objetos (instancias de clases), el verdadero motivo de pasar objetos "por referencia" no es precisamente para que la función pueda modificar el argumento (incluso se intenta evitar esto declarando el argumento como referencia constante), sino por razones de eficacia del código. Más que alguna nueva "funcionalidad", el paso de argumentos a funciones "por referencia" solo proporciona cierta comodidad adicional a la funcionalidad proporcionada por los punteros

Valores devueltos por referencia

Otro uso común de este tipo de objetos es su utilización como valor de retorno de una función. Es el caso del valor devuelto por la función adjunta int& max (int& a, int& b) { if (a >= b) return a; return b; }

Criterios de eficiencia

En ocasiones la utilización de referencias para el paso de argumentos a funciones o en el valor devuelto, está motivada solo por criterios de eficacia. En efecto, las secuencias de llamada y retorno de funciones implican la creación de todas las variables locales de la función (incluyendo el valor que será devuelto), así como la invocación del constructor-copia para todos los argumentos que no han sido pasados por referencia. Cuando estos objetos son muy grandes. Por ejemplo, en instancias de clases con muchos datos o que derivan de jerarquías complejas, se requieren procesos de creación y destrucción, a veces muy complicados, que pueden evitarse parcialmente utilizando referencias. En tales casos suele recurrirse a declarar constantes los argumentos pasados "por referencia" para evitar que la función pueda alterar su valor.

Puede Consultar

Fuente