
elfogger
Las referencias de C++ tienen dos propiedades:
- Siempre apuntan al mismo objeto.
- No pueden ser 0.
Los punteros son lo contrario:
- Pueden señalar diferentes objetos.
- Pueden ser 0.
¿Por qué no hay una “referencia o puntero no anulable y reiniciable” en C++? No puedo pensar en una buena razón por la que las referencias no se puedan volver a colocar.
Editar:
La pregunta surge a menudo porque suelo usar referencias cuando quiero asegurarme de que una “asociación” (estoy evitando las palabras “referencia” o “puntero” aquí) nunca sea inválida.
No creo que alguna vez pensé “genial que esta referencia siempre se refiera al mismo objeto”. Si las referencias se pudieran volver a colocar, aún se podría obtener el comportamiento actual de esta manera:
int i = 3;
int& const j = i;
Esto ya es C++ legal, pero no tiene sentido.
Reitero mi pregunta así: “¿Cuál fue la razón detrás de la ‘una referencia es el diseño del objeto? ¿Por qué se consideró útil tener referencias siempre ser el mismo objeto, en lugar de solo cuando se declara como const?”
Saludos, Félix

miguel rebabas
La razón por la que C ++ no le permite volver a vincular referencias se da en “Diseño y evolución de C ++” de Stroustrup:
No es posible cambiar a qué se refiere una referencia después de la inicialización. Es decir, una vez que se inicializa una referencia de C++, no se puede hacer que se refiera a un objeto diferente más adelante; no se puede volver a encuadernar. En el pasado me habían mordido las referencias de Algol68 donde r1=r2
puede asignar a través de r1
al objeto al que se hace referencia o asignar un nuevo valor de referencia al r1
(reencuadernación r1
) dependiendo del tipo de r2
. Quería evitar tales problemas en C++.

rlbond
En C++, a menudo se dice que “la referencia es el objeto”. En cierto sentido, es cierto: aunque las referencias se manejan como punteros cuando se compila el código fuente, la referencia pretende significar un objeto que no se copia cuando se llama a una función. Dado que las referencias no son directamente direccionables ( por ejemplo, las referencias no tienen dirección y devuelve la dirección del objeto), semánticamente no tendría sentido reasignarlos. Además, C++ ya tiene punteros, que maneja la semántica de restablecer.
Porque entonces no tendría un tipo que se pueda volver a colocar que no puede ser 0. A menos que haya incluido 3 tipos de referencias/punteros. Lo que simplemente complicaría el idioma por muy poca ganancia (¿Y entonces, por qué no agregar el cuarto tipo también? ¿Referencia no reiniciable que puede ser 0?)
Una mejor pregunta puede ser, ¿por qué querrías que las referencias se puedan volver a colocar? Si lo fueran, eso los haría menos útiles en muchas situaciones. Sería más difícil para el compilador realizar análisis de alias.
Parece que la razón principal por la que las referencias en Java o C# se pueden volver a colocar es porque hacen el trabajo de los punteros. Señalan objetos. No son alias para un objeto.
¿Cuál debería ser el efecto de lo siguiente?
int i = 42;
int& j = i;
j = 43;
En C++ hoy, con referencias que no se pueden volver a colocar, es simple. j es un alias para i, e i termina con el valor 43.
Si las referencias se hubieran vuelto a colocar, la tercera línea vincularía la referencia j a un valor diferente. Ya no sería el alias i, sino el literal entero 43 (que no es válido, por supuesto). O quizás un ejemplo más simple (o al menos sintácticamente válido):
int i = 42;
int k = 43;
int& j = i;
j = k;
Con referencias reubicables. j apuntaría a k después de evaluar este código. Con las referencias que no se pueden volver a colocar de C++, j todavía apunta a i, y se le asigna el valor 43.
Hacer que las referencias se puedan volver a colocar cambia la semántica del lenguaje. La referencia ya no puede ser un alias para otra variable. En su lugar, se convierte en un tipo de valor independiente, con su propio operador de asignación. Y entonces uno de los usos más comunes de las referencias sería imposible. Y nada se ganaría a cambio. La funcionalidad recientemente adquirida para las referencias ya existía en forma de punteros. Así que ahora tendríamos dos formas de hacer lo mismo, y ninguna forma de hacer lo que hacen las referencias en el lenguaje C++ actual.

Brian R. Bondy
Una referencia no es un puntero, puede implementarse como un puntero en segundo plano, pero su concepto central no es equivalente a un puntero. Una referencia debe ser mirado así *is*
el objeto al que se refiere. Por lo tanto, no puede cambiarlo y no puede ser NULL.
Un puntero es simplemente una variable que contiene una dirección de memoria. El puntero en sí tiene una dirección de memoria propia, y dentro de esa dirección de memoria contiene otra dirección de memoria. que se dice que apunta. Una referencia no es lo mismo, no tiene una dirección propia y, por lo tanto, no se puede cambiar para “retener” otra dirección.
Pienso que el parashift C++ Preguntas frecuentes sobre referencias lo dice mejor:
Nota importante: aunque una referencia a menudo se implementa usando una dirección en el lenguaje ensamblador subyacente, no piense en una referencia como un puntero de aspecto extraño a un objeto. Una referencia es el objeto. No es un puntero al objeto, ni una copia del objeto. es el objeto
y de nuevo en Preguntas frecuentes 8.5 :
A diferencia de un puntero, una vez que una referencia se vincula a un objeto, no se puede “volver a colocar” en otro objeto. La referencia en sí no es un objeto (no tiene identidad; tomar la dirección de una referencia te da la dirección del referente; recuerda: la referencia es su referente).
Una referencia que se puede volver a colocar sería funcionalmente idéntica a un puntero.
Con respecto a la nulabilidad: no puede garantizar que dicha “referencia que se puede volver a colocar” no sea NULL en tiempo de compilación, por lo que cualquier prueba de este tipo debería realizarse en tiempo de ejecución. Puede lograr esto usted mismo escribiendo una plantilla de clase de estilo de puntero inteligente que arroja una excepción cuando se inicializa o se le asigna NULL:
struct null_pointer_exception { ... };
template<typename T>
struct non_null_pointer {
// No default ctor as it could only sensibly produce a NULL pointer
non_null_pointer(T* p) : _p(p) { die_if_null(); }
non_null_pointer(non_null_pointer const& nnp) : _p(nnp._p) {}
non_null_pointer& operator=(T* p) { _p = p; die_if_null(); }
non_null_pointer& operator=(non_null_pointer const& nnp) { _p = nnp._p; }
T& operator*() { return *_p; }
T const& operator*() const { return *_p; }
T* operator->() { return _p; }
// Allow implicit conversion to T* for convenience
operator T*() const { return _p; }
// You also need to implement operators for +, -, +=, -=, ++, --
private:
T* _p;
void die_if_null() const {
if (!_p) { throw null_pointer_exception(); }
}
};
Esto puede ser útil en ocasiones: una función que toma un non_null_pointer<int>
El parámetro ciertamente comunica más información a la persona que llama que una función que toma int*
.

dhaumann
Curiosamente, muchas respuestas aquí son un poco confusas o incluso no vienen al caso (por ejemplo, no es porque las referencias no puedan ser cero o similares, de hecho, puede construir fácilmente un ejemplo donde una referencia es cero).
La verdadera razón por la que no es posible restablecer una referencia es bastante simple.
-
Los punteros le permiten hacer dos cosas: Cambiar el valor detrás del puntero (ya sea a través del ->
o la *
operador), y para cambiar el propio puntero (asignación directa =
). Ejemplo:
int a;
int * p = &a;
- Cambiar el valor requiere desreferenciar:
*p = 42;
- Cambiando el puntero:
p = 0;
-
Las referencias le permiten cambiar solo el valor. ¿Por qué? Dado que no hay otra sintaxis para expresar el reinicio. Ejemplo:
int a = 10;
int b = 20;
int & r = a;
r = b; // re-set r to b, or set a to 20?
En otras palabras, sería ambiguo si se le permitiera restablecer una referencia. Tiene aún más sentido cuando se pasa por referencia:
void foo(int & r)
{
int b = 20;
r = b; // re-set r to a? or set a to 20?
}
void main()
{
int a = 10;
foo(a);
}
Espero que ayude 🙂

snemarch
¿Probablemente hubiera sido menos confuso nombrar las referencias de C++ como “alias”? Como han mencionado otros, las referencias en C++ deben tenerse en cuenta como La variable se refieren a, no como un puntero/referencia a la variable. Como tal, no puedo pensar en una buena razón por la que deberían ser reiniciable.
cuando se trata de punteros, a menudo tiene sentido permitir nulo como valor (y de lo contrario, probablemente desee una referencia en su lugar). Si desea específicamente no permitir mantener nulo, siempre puede codificar su propio tipo de puntero inteligente;)
Sé que es un poco tarde, pero la respuesta de Michael Burr parece ser la más completa. Explica las decisiones filosóficas de diseño de Stroustrup. Todos los demás parecen estar rogando por la discusión (de forma indirecta).
– jww
16 de diciembre de 2015 a las 10:57