MIGUEL Á. TOLEDO MARTÍNEZ CONTENIDO DE LA LECCIÓN 16 USO DE REFERENCIAS EN C++ 1. Introducción 2. Parámetros de referencia como alias 2.1. Ejemplo 16.1 3. Variables de referencia como alias 3.1. Ejemplos 16.2, 16.3, 16.4, 16.5 2 2 3 4 4 4. Utilice comentarios en sus programas para explicar las referencias 6 5. Reglas para trabajar con referencias 6 6. Lo que necesita saber 7 FUNCIONES – LECCIÓN 16 16-1 MIGUEL Á. TOLEDO MARTÍNEZ LECCIÓN 16 USO DE REFERENCIAS EN C++ INTRODUCCIÓN Dos maneras de invocar funciones en muchos lenguajes de programación son mediante llamada por valor y llamada por referencia. Cuando se pasa un argumento utilizando una llamada por valor, se hace una copia del argumento, la cual se pasa a la función llamada. Los cambios a la copia no afectan el valor de la variable original en el invocador. Con esto se evitan los efectos secundarios que tanto minan el desarrollo de sistemas de software correctos y confiables. En esta lección presentaremos los parámetros de referencia, que son el primero de dos medios que tiene C++ para efectuar una llamada por referencia. Con una llamada por referencia, el invocador le da a la función llamada la capacidad de acceder directamente a la información del usuario y de modificarla si así lo desea. Mostraremos cómo lograr la ventaje de desempeño de una llamada por referencia mientras, de manera simultánea, se logra la ventaja de ingeniería de software de proteger contra la corrupción a la información del invocador. Un parámetro de referencia es un alias de su argumento correspondiente. Objetivos de esta lección: • • Declarar e inicializar una variable de referencia dentro de su programa. Usar variables de referencia para cambiar el valor de un parámetro dentro de una función. PARÁMETROS DE REFERENCIA COMO ALIAS Un parámetro de referencia es un alias de su argumento correspondiente. Para indicar que el parámetro de una función se pasa por referencia, simplemente ponga un ampersand (&) después de la clase de datos del parámetro en el prototipo de la función; utilice la misma convención al listar la clase de datos del parámetro en el encabezado de la función. Por ejemplo, la declaración int &contador en un encabezado de función se leería como contador es una referencia a un int. En la llamada de función, basta con mencionar por nombre a la variable y ésta se pasará por referencia. Después, mencionar a la variable por su nombre de parámetro en el cuerpo de la función llamada, de hecho hará referencia a la variable original del invocador, la cual podrá ser modificada directamente en la función llamada. Como siempre el prototipo de la función debe coincidir con su encabezado. FUNCIONES – LECCIÓN 16 16-2 MIGUEL Á. TOLEDO MARTÍNEZ Ejemplo 16.1 El siguiente programa, REFERENCIA.CPP, compara una llamada por valor y una llamada por referencia con parámetros de referencia. Los estilos de los argumentos de las llamadas a cuadradoPorValor() y cuadradoPorReferencia(), son idénticos, es decir, ambas variables se mencionan simplemente por su nombre. Sin revisar los prototipos de las funciones ni las definiciones de las funciones, es imposible saber, a partir de las llamadas mismas, si las funciones pueden modificar sus argumentos. No obstante, debido a que los prototipos de función son obligatorios, el compilador no tiene ningún problema para resolver esta ambigüedad. /* El siguiente programa: REFERENCIA.CPP, hace una comparación entre llamada por valor y llamada por referencia con referencia. */ #include <iostream.h> //Para cout y cin int cuadradoPorValor(int); void cuadradoPorReferencia(int &); void main(void) { int x = 2, z = 4; cout << "x = " << x << " antes de cuadradoPorValor()" << endl; cout << "Valor devuelto por cuadradoPorValor(): " << cuadradoPorValor(x) << endl; cout << "x = " << x << " después de cuadradoPorValor()" << endl << endl; cout << "z = " << z << " antes de cuadradoPorReferencia()" << endl; cuadradoPorReferencia(z); cout << "z = " << z << " después de cuadradoPorReferencia()" << endl; }//Fin de main() int cuadradoPorValor(int a) { return a * a; }//Fin de cuadradoPorValor() void cuadradoPorReferencia(int &cRef) { cRef *= cRef; }//Fin de cuadradoPorValor() //El argumento del invocador no se modifica //El argumento del invocador se modifica En otra lección estudiaremos los apuntadores; veremos que los apuntadores permiten una forma alterna de realizar una llamada por referencia donde el estilo de la llamada indica con claridad que se trata de una llamada por referencia (así como el potencial de modificar los argumentos del invocador) Para especificar una referencia a una constante, ponga el calificador const antes de la especificación de clase de dato en la declaración del parámetro. Observe la colocación de & en la lista de parámetros de la función cuadradoPorReferencia. Algunos programadores de C++ prefieren escribir int& cRef, en lugar de int &cRef. FUNCIONES – LECCIÓN 16 16-3 MIGUEL Á. TOLEDO MARTÍNEZ VARIABLES DE REFERENCIA COMO ALIAS Las referencias también pueden servir como alias de otras variables de la función. Por ejemplo, el código: int contador = 1; int &cRef = contador; ++cRef; //Declara la variable entera contador //Crea cRef como alias de contador //Incrementa contador (utilizando su alias) Incrementa la variable contador por medio de su alias cRef. Las variables de referencia deben inicializarse en sus declaraciones y no pueden reasignarse como alias a otras variables. Una vez que una referencia se declara como alias de otra variable, todas las operaciones efectuadas sobre dicho alias (es decir, la referencia) de hecho se realizan en la propia variable original. El alias simplemente es otro nombre de la variable. Si se toma la dirección de una referencia y se comparan las referencias, no sucede un error de sintaxis; en cambio, todas las operaciones suceden en la variable para la que la referencia es un alias. Las funciones pueden devolver referencias, pero esto puede ser peligroso. Al devolverle una referencia a una variable declarada en la función llamada, la variable debe estar declarada como static dentro de dicha función. De otra forma, la referencia es hacia una variable automática que se descarta al terminar la función; se dice que tal variable esta indefinida y el comportamiento del programa será impredecible (algunos compiladores emiten avisos cuando esto sucede) Las referencia a variables indefinidas se llaman referencias colgantes. Ejemplo 16.2 El siguiente programa REFINI1.CPP, ilustra el hecho de que toda variable referenciada debe de ser inicializada. /* El siguiente programa: REFINI1.CPP, ilustra el hecho de que toda variable referenciada debe de ser inicializada. */ #include <iostream.h> //Para cout y cin void main(void) { int x = 3; int &y = x; //Ahora ya es un alias de x cout << "x = " << x << endl << "y = " << y << endl << endl; y = 7; cout << "x = " << x << endl << "y = " << y << endl << endl; }//Fin de main() FUNCIONES – LECCIÓN 16 16-4 MIGUEL Á. TOLEDO MARTÍNEZ Ejemplo 16.3 El siguiente programa REFINI2.CPP, ilustra el hecho de no inicializar una variable de referencia. /* El siguiente programa: REFINI2.CPP, ilustra el hecho de no inicializar una variable por referencia. */ #include <iostream.h> //Para cout y cin void main(void) { int x = 3; int &y; //Ahora ya es un alias de x cout << "x = " << x << endl << "y = " << y << endl << endl; y = 7; cout << "x = " << x << endl << "y = " << y << endl << endl; }//Fin de main() El compilador enviará el siguiente mensaje de error: Reference variable ‘y’ must be initialized Ejemplo 16.4 El siguiente programa: REFVAR.CPP, despliega y altera los valores de una variable usando la variable misma o su referencia. /* El siguiente programa: REFVAR.CPP, ilustra el uso de variables de referencia */ #include <iostream.h> //Para cout y cin void main(void) { int x = 10; int &rx = x; // despliega x usando x y rx cout << "x contiene " << x << endl; cout << "x contiene (usando la referencia rx) " << rx << endl; // altera x y despliega su valor usando rx x *= 2; cout << "x contiene (usando la referencia rx) " << rx << endl; // altera rx y despliega el valor usando x rx *= 2; cout << "x contiene " << x << endl; }//Fin de main() FUNCIONES – LECCIÓN 16 16-5 MIGUEL Á. TOLEDO MARTÍNEZ Ejemplo 16.5 El siguiente programa, INTCREF.CPP, utiliza referencias a dos datos de punto flotante para intercambiar sus valores. /* El siguiente programa: INTCREF.CPP, intercambia el valor entre dos variables utilizando variables de referencia. */ #include <iostream.h> //Para cout y cin void intercambiaValores(float &a, float &b) { float temp; temp = a; a = b; b = temp; }//Fin de intercambiaValores() void main(void) { float grande float pequeno = 10000.0; = 0.00001; intercambiaValores(grande, pequeno); cout << "Grande contiene: " << grande << endl; cout << "Pequeño contiene: " << pequeno << endl; }//Fin de main() Como puede observar, la función intercambiaValores, se vuelve más fácil de comprender. UTILICE COMENTARIOS EN SUS PROGRAMAS PARA EXPLICAR LAS REFERENCIAS Como las referencias dentro de una función no utilizan asterisco, puede pensarse que el valor de los parámetros no cambian, razón por la cual se hace necesario escribir comentarios antes y dentro de las funciones que cambian parámetros utilizando referencias. REGLAS PARA TRABAJAR CON REFERENCIAS Una referencia no es una variable. Una vez que asigna un valor a la referencia, la referencia no puede cambiar. Además, a diferencia de los apuntadores, no puede realizar las operaciones siguientes en las referencias: • • • • • No puede obtener la dirección de la referencia utilizando el operador de dirección de C++. No puede asignarle un apuntador a una referencia. No puede comparar valores de referencias utilizando los operadores relacionales de C++. No puede realizar operaciones aritméticas con una referencia, tal como agregarle un desplazamiento. No puede cambiar una referencia. FUNCIONES – LECCIÓN 16 16-6 MIGUEL Á. TOLEDO MARTÍNEZ LO QUE NECESITA SABER Antes de continuar con la siguiente lección, asegúrese de haber comprendido los siguientes conceptos: !"C++ ofrece una forma directa de llamada por referencia, empleando parámetros de referencia. Para indicar que el parámetro de una función se pasa por referencia, ponga en el prototipo de función, después del tipo del parámetro, un &. En la llamada de la función, mencione por nombre a la variable y será pasada como una llamada por referencia. En la función llamada, la mención de la variable con su nombre local en realidad hace referencia a la variable original del invocador. Por lo tanto, la función llamada puede modificar la variable original. !"También se pueden crear parámetros de referencia para utilizarlos localmente como alias de otras variables dentro de una función. Las variables de referencia deben inicializarse en sus declaraciones y no se pueden reasignar como alias de otras variables. Una vez declarada una variable de referencia como alias de otra variable, todas las operaciones supuestamente efectuadas sobre el alias en realidad se llevan a cabo en la variable. !"Una referencia en C++ es un alias (o segundo nombre) al nombre de una variable. !"Para declarar una referencia, coloque el carácter ampersand (&) inmediatamente después del tipo de variable y luego especifique el nombre de la referencia seguido por un signo igual y el nombre de la variable a la que la referencia le dará un alias. !"Una vez que asigna un valor a la referencia, no puede cambiar el valor de la misma. !"Debe de incluir comentarios antes y dentro de las funciones que utilizan referencias para cambiar el valor de un parámetro. De esta manera se asegura que otro programador que lea su programa pueda reconocer los cambios que está realizando. !"Un sobreuso de referencias puede conducir a programas poco legibles. FUNCIONES – LECCIÓN 16 16-7