Pregunta que aparece mucho por aquí en distintas formas: ¿cuándo conviene usar un puntero y cuándo una referencia en C++? Van las diferencias prácticas y una guía rápida.
Diferencias clave
Una referencia debe inicializarse en el momento de declararla y ya no se puede «reapuntar» a otra cosa después, mientras que un puntero se puede reasignar cuantas veces quieras, incluida la posibilidad de no apuntar a nada (nullptr):
int x = 10;
int y = 20;
int &ref = x; // obligatorio inicializarla aquí mismo
ref = y; // esto NO reapunta ref a y, cambia el VALOR de x a 20
int *ptr = &x;
ptr = &y; // esto si reapunta ptr, ahora apunta a y
La sintaxis también cambia: con una referencia usas la variable directamente, sin asteriscos ni ->; con un puntero necesitas *puntero para acceder al valor o comprobar antes si es nullptr.
Guía rápida
Usa referencias cuando: pasas un objeto grande a una función y quieres evitar la copia, pero sabes seguro que el objeto existe y no necesitas reapuntar a otro. Es el caso más común en parámetros de función:
void procesar(const std::string &texto) {
// texto no se copia, y const evita que la funcion la modifique
}
Usa punteros cuando: necesitas representar «puede que no haya nada» (un puntero válido es nullptr, una referencia siempre tiene que referirse a algo), necesitas reapuntar a distintos objetos a lo largo del tiempo, o trabajas con arrays/aritmética de punteros de bajo nivel.
Y si lo que necesitas es gestionar la propiedad de memoria dinámica (quién es responsable de hacer delete), en C++ moderno lo normal ya no es ni puntero crudo ni referencia, sino std::unique_ptr o std::shared_ptr, que gestionan la vida del objeto automáticamente.
¿Alguna duda concreta con algún caso que os haya costado decidir entre uno y otro? Comentadlo por aquí abajo.
Angel Carrero
Añado un matiz importante sobre lo de std::unique_ptr/std::shared_ptr que se menciona al final: elegír entre uno u otro también depende de quién es el «dueño» real del objeto.
std::unique_ptr representa propiedad exclusiva: solo un puntero es responsable de destruir el objeto, no se puede copiar, solo mover (std::move). Es la opción por defecto cuando no hay una razón real para compartir propiedad, que es la mayoría de los casos.
std::shared_ptr permite que varios punteros compartan la propiedad, con conteo de referencias: el objeto se destruye cuando el último shared_ptr que lo referencia desaparece. Útil, pero tiene un gotcha real: si dos objetos se apuntan mutuamente con shared_ptr (una referencia circular), ninguno de los dos llega nunca a contar cero referencias, y tienes una fuga de memoria aunque estés usando punteros «inteligentes». Para romper ese ciclo se usa std::weak_ptr en al menos uno de los dos lados: no cuenta como propietario, así que no bloquea la destrucción, pero permite comprobar si el objeto sigue vivo antes de acceder a él.
Regla práctica: empieza siempre por unique_ptr, y solo sube a shared_ptr cuando de verdad tengas varios dueños legítimos del mismo objeto al mismo tiempo, no «por si acaso».
David Carrero