Menu

Referencias en C++: paso por referencia, const& y alias

Las referencias en C++ explicadas: cómo el & en un parámetro crea un alias, por qué el paso por referencia evita copias y permite que una función modifique las variables de quien la llama, y cuándo usar const& y referencias en lugar de devolver valores.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Otro nombre para lo mismo

En la página de parámetros de función, cada argumento se copiaba dentro de la función. Esa copia es la razón por la que una función no puede cambiar la variable del llamador: solo ve su propio duplicado. Una referencia rompe ese muro. Es un alias: un segundo nombre ligado a una variable existente, que comparte exactamente la misma memoria.

Creas una referencia con & en la declaración. Una vez ligada, la referencia y el original son indistinguibles:

Dos reglas hacen que las referencias sean seguras y predecibles: una referencia debe inicializarse en el momento en que se declara (int& r; es un error de compilación) y nunca puede reasignarse para apuntar a otra cosa después. Asignar a una referencia siempre escribe en aquello a lo que se ligó originalmente.

Paso por referencia: deja que una función alcance hacia atrás

La verdadera ventaja está en las funciones. Pon & en un parámetro y la función recibe un alias del argumento del llamador en lugar de una copia. Ahora los cambios dentro de la función son visibles fuera de ella:

Quita el & y addBonus incrementaría una copia desechable, dejando total en 100. Ese único carácter es toda la diferencia. Esta es la forma canónica de escribir una función que devuelve más de un resultado o edita su entrada en el sitio. El ejemplo clásico es intercambiar dos variables:

Sin referencias, swapValues solo intercambiaría copias locales y x/y se quedarían en 1 2. (La biblioteca estándar ya tiene std::swap, pero escribirlo tú mismo muestra exactamente lo que te aporta un parámetro por referencia.)

Referencias const: lee rápido, promete no tocar

El paso por referencia también evita copiar, y para un objeto grande esa copia puede ser costosa. Pero un parámetro T& simple señala "podría modificar esto", lo cual es engañoso cuando solo quieres leer. La solución es const T&: obtienes la velocidad sin copia de una referencia y una promesa, garantizada por el compilador, de que la función no cambiará el argumento.

Una referencia no const solo puede ligarse a una variable modificable, pero una referencia const también puede ligarse a literales y temporales; por eso greet("literal works too") compila. Una regla práctica para elegir el tipo de un parámetro:

void f(int x)              // tipo barato, solo lectura -> simplemente cópialo
void f(const string& s)   // tipo pesado, solo lectura -> referencia const
void f(string& s)         // tienes la intención de modificar el objeto del llamador

Usa const T& por defecto para cualquier tipo de clase que solo leas (string, vector, tus propios structs), y reserva una referencia no const para cuando realmente quieras escribir de vuelta.

Devolver una referencia

Una función también puede devolver una referencia, entregando al llamador un alias de algo que ya existe. Esto es común en código de tipo contenedor: es lo que permite que v[i] = 5 funcione y lo que hace operator[] por dentro:

Como at devuelve int&, la expresión de llamada at(data, 1) es en sí misma un lvalue al que puedes asignar. Devuelve un int simple y at(data, 1) = 42 no compilaría: estarías asignando a una copia temporal.

El gran peligro: referencias colgantes

Una referencia no posee nada; solo apunta a memoria que vive en otro lugar. Si esa memoria muere mientras la referencia sigue en uso, tienes una referencia colgante, y leer a través de ella es comportamiento indefinido: puede imprimir basura, fallar, o parecer que funciona hasta que te arruina el día en producción. El error clásico es devolver una referencia a una variable local:

int& broken() {
    int local = 42;
    return local;   // BUG: local se destruye cuando broken() retorna
}                   // la referencia devuelta queda colgante

int main() {
    int& r = broken();
    cout << r << "\n";   // COMPORTAMIENTO INDEFINIDO - lee memoria muerta
}

La variable local desaparece en el instante en que broken retorna, así que la referencia apunta a espacio de pila ya recuperado. Devuelve solo una referencia a algo que sobreviva a la llamada: un parámetro pasado por referencia, un dato miembro o un static. Si el valor se calcula dentro de la función, devuélvelo por valor y deja que el compilador optimice la copia. La misma trampa afecta a los bucles basados en rango y a cualquier referencia ligada a un temporal: nunca conserves una referencia más allá del tiempo de vida de aquello que nombra.

Siguiente: sobrecarga de funciones

Las referencias te dan una segunda perilla en cada parámetro (copia frente a alias, mutable frente a const) y esa perilla interactúa directamente con el siguiente tema. A continuación, la sobrecarga de funciones te permite definir varias funciones con el mismo nombre pero distintas listas de parámetros, y el compilador elige la correcta haciendo coincidir los tipos de los argumentos, incluyendo si se pasan por valor, por referencia o por referencia const.

Preguntas frecuentes

¿Qué es una referencia en C++?

Una referencia es un alias de una variable existente: otro nombre para la misma memoria. Se crea con & en la declaración: int& r = x;. A partir de ahí, r y x son intercambiables; cambiar una cambia la otra. Las referencias deben inicializarse al declararse y nunca pueden reasignarse para apuntar a otra variable.

¿Cuál es la diferencia entre paso por valor y paso por referencia en C++?

El paso por valor (void f(int x)) copia el argumento, así que la función trabaja sobre su propia copia y la variable del llamador queda intacta. El paso por referencia (void f(int& x)) le da a la función acceso directo a la variable del llamador, por lo que los cambios son visibles tras la llamada, y no se hace ninguna copia, lo que importa con objetos grandes.

¿Cuándo debo usar parámetros por referencia constante en C++?

Usa const T& cuando una función solo necesita leer un parámetro pero el tipo es costoso de copiar (string, vector, structs grandes). Obtienes la velocidad sin copia de una referencia más la garantía del compilador de que la función no modificará el valor del llamador. Para tipos baratos como int o double, el simple paso por valor es más sencillo e igual de rápido.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR