1497 votos

¿Cuáles son las diferencias entre la variable puntero y la variable de referencia en C++?

Sé que las referencias son azúcar sintáctico, por lo que el código es más fácil de leer y escribir :)

¿Pero cuáles son las diferencias?

Resumen de las respuestas y los enlaces que figuran a continuación:

  1. Un puntero puede ser reasignado cualquier número de veces mientras que una referencia no puede ser reasignada después de la encuadernación.
  2. Los punteros pueden apuntar a ninguna parte ( NULL ), mientras que la referencia siempre se refiere a un objeto.
  3. No se puede tomar la dirección de una referencia como se hace con los punteros.
  4. No hay "aritmética de referencia" (pero puedes tomar la dirección de un objeto señalado por una referencia y hacer aritmética de puntero sobre él como en &obj + 5).

Para aclarar un concepto erróneo:

El estándar C++ es muy cuidadoso para evitar dictar cómo un compilador debe referencias de implementación, pero cada compilador de C++ implementa referencias como punteros. Es decir, una declaración como:

int &ri = i;

si no se optimiza por completo , asigna la misma cantidad de almacenamiento como un puntero, y coloca la dirección de i en ese almacén.

Así que el puntero y la referencia ocupan la misma cantidad de memoria

Como regla general,

  • Utilizar referencias en los parámetros de función y tipos de retorno para definir interfaces útiles y autodocumentadas.
  • Usar punteros para implementar algoritmos y estructuras de datos.

Interesante lectura:

786voto

Brian R. Bondy Puntos 141769
  1. Un puntero puede ser reasignado:

    int x = 5;
    int y = 6;
    int *p;
    p =  &x;
    p = &y;
    *p = 10;
    assert(x == 5);
    assert(y == 10);

    Una referencia no puede y debe ser asignada en la inicialización:

    int x = 5;
    int y = 6;
    int &r = x;
  2. Un puntero tiene su propia dirección de memoria y tamaño en la pila (4 bytes en x86), mientras que una referencia comparte la misma dirección de memoria (con la variable original) pero también ocupa algo de espacio en la pila. Como una referencia tiene la misma dirección que la propia variable original, es seguro pensar en una referencia como otro nombre para la misma variable. Nota: Lo que un puntero apunta puede estar en la pila o en el montón. Lo mismo ocurre con una referencia. Mi afirmación en esta declaración no es que un puntero deba apuntar a la pila. Un puntero es sólo una variable que contiene una dirección de memoria. Esta variable está en la pila. Ya que una referencia tiene su propio espacio en la pila, y ya que la dirección es la misma que la variable a la que hace referencia. Más sobre stack vs heap . Esto implica que hay una dirección real de una referencia que el compilador no le dirá.

    int x = 0;
    int &r = x;
    int *p = &x;
    int *p2 = &r;
    assert(p == p2);
  3. Puedes tener punteros a punteros a punteros que ofrecen niveles extra de indirección. Mientras que las referencias sólo ofrecen un nivel de indirección.

    int x = 0;
    int y = 0;
    int *p = &x;
    int *q = &y;
    int **pp = &p;
    pp = &q;//*pp = q
    **pp = 4;
    assert(y == 4);
    assert(x == 0);
  4. El puntero puede asignarse directamente a NULL, mientras que la referencia no puede. Si te esfuerzas lo suficiente, y sabes cómo, puedes hacer que la dirección de una referencia sea NULL. De la misma manera, si te esfuerzas lo suficiente puedes tener una referencia a un puntero, y entonces esa referencia puede contener NULL.

    int *p = NULL;
    int &r = NULL; <--- compiling error
  5. Los punteros pueden iterar sobre una matriz, puedes usar ++ para ir al siguiente elemento que un puntero está señalando, y + 4 para ir al 5º elemento. No importa el tamaño del objeto al que apunta el puntero.

  6. Un puntero tiene que ser desmarcado con * para acceder a la ubicación de la memoria a la que apunta, mientras que una referencia puede ser usada directamente. Un puntero a una clase/estructura utiliza -> para acceder a sus miembros mientras que una referencia utiliza un . .

  7. Un puntero es una variable que contiene una dirección de memoria. Independientemente de cómo se implemente una referencia, una referencia tiene la misma dirección de memoria que el elemento al que hace referencia.

  8. Las referencias no se pueden meter en un Array, mientras que los punteros pueden ser (Mencionado por el usuario @litb)

  9. Las referencias a las constantes pueden estar ligadas a las temporales. Los punteros no pueden (no sin alguna indirecta):

    const int &x = int(12); //legal C++
    int *y = &int(12); //illegal to dereference a temporary.

    Esto hace que const& más seguro para su uso en listas de argumentos y demás.

160voto

Christoph Puntos 64389

¿Qué es una referencia C++ ( para los programadores de C )

A referencia puede pensarse como una puntero constante (¡no confundir con un puntero a un valor constante!) con indirección automática, es decir, el compilador aplicará el * operador para usted.

Todas las referencias deben ser inicializadas con un valor no nulo o la compilación fallará. No es posible obtener la dirección de una referencia - el operador de la dirección devolverá en su lugar la dirección del valor referido - ni es posible hacer aritmética sobre las referencias.

A los programadores de C puede que no les gusten las referencias de C++, ya que ya no será obvio cuando ocurra una indirecta o si un argumento se pasa por valor o por puntero sin mirar las firmas de las funciones.

A los programadores de C++ puede que no les guste usar punteros ya que se consideran inseguros -aunque las referencias no son realmente más seguras que los punteros constantes, excepto en los casos más triviales-, carecen de la conveniencia de la indirección automática y llevan una connotación semántica diferente.

Considere la siguiente declaración de la C++ FAQ Lite :

Aunque una referencia se aplica a menudo utilizando una dirección en el el lenguaje de ensamblaje subyacente, por favor haz no Piense en una referencia como una un puntero de aspecto divertido a un objeto. Una referencia es el objeto. Es no un puntero al objeto, ni una copia del objeto. Es es el objeto.

Pero si una referencia realmente eran el objeto, ¿cómo podría haber referencias colgantes? En los lenguajes no administrados, es imposible que las referencias sean más "seguras" que los punteros - ¡generalmente no hay manera de alias fiables de valores a través de los límites del alcance!

Por qué considero útiles las referencias a C++

Viniendo de un fondo de C, las referencias a C++ pueden parecer un concepto un tanto tonto, pero aún así hay que usarlas en lugar de punteros cuando sea posible: Indirección automática es conveniente, y las referencias se vuelven especialmente útiles cuando se trata de la RAII, pero no por ninguna ventaja de seguridad percibida, sino más bien porque hacen que escribir el código idiomático sea menos incómodo.

La RAII es uno de los conceptos centrales de C++, pero interactúa de forma no trivial con la semántica de la copia. Pasar objetos por referencia evita estos problemas ya que no hay copia. Si las referencias no estuvieran presentes en el lenguaje, habría que utilizar en su lugar punteros, que son más engorrosos de usar, violando así el principio de diseño del lenguaje de que la solución de mejores prácticas debería ser más fácil que las alternativas.

95voto

Matt Price Puntos 9674

Si quieres ser realmente pedante, hay una cosa que puedes hacer con una referencia que no puedes hacer con un puntero: extender la vida útil de un objeto temporal. En C++, si atas una referencia constante a un objeto temporal, la vida útil de ese objeto se convierte en la vida útil de la referencia.

std::string s1 = "123";
std::string s2 = "456";

std::string s3_copy = s1 + s2;
const std::string& s3_reference = s1 + s2;

En este ejemplo s3_copy copia el objeto temporal que es el resultado de la concatenación. Mientras que s3_reference en esencia se convierte en el objeto temporal. En realidad es una referencia a un objeto temporal que ahora tiene la misma vida útil que la referencia.

Si intentas esto sin el const debería fallar en la compilación. No se puede vincular una referencia no coherente a un objeto temporal, ni tomar su dirección para ese asunto.

60voto

Mark Ransom Puntos 132545

Contrariamente a la opinión popular, es posible tener una referencia que sea NULL.

int * p = NULL;
int & r = *p;
r = 1;  // crash! (if you're lucky)

Es cierto que es mucho más difícil hacer una referencia, pero si lo logras, te arrancarás el pelo tratando de encontrarla.

Edición: algunas aclaraciones.

Técnicamente, esta es una referencia inválida, no una referencia nula. C++ no soporta referencias nulas como concepto, como se puede encontrar en otros lenguajes. Hay otros tipos de referencias inválidas también.

El error real está en la derivación del puntero NULL, antes de la asignación a una referencia. Pero no estoy al tanto de ningún compilador que genere algún error en esa condición - el error se propaga a un punto más adelante en el código. Eso es lo que hace que este problema sea tan insidioso. La mayoría de las veces, si desreferencias un puntero NULL, te caes justo en ese punto y no se necesita mucha depuración para resolverlo.

Mi ejemplo anterior es corto y artificioso. Aquí hay un ejemplo más real.

class MyClass
{
    ...
    virtual void DoSomething(int,int,int,int,int);
};

void Foo(const MyClass & bar)
{
    ...
    bar.DoSomething(a,Long,list,of,parameters);  // crash occurs here - obvious why?
}

MyClass * GetInstance()
{
    if (somecondition)
        return NULL;
    ...
}

MyClass * p = GetInstance();
Foo(*p);

Editar: Algunos pensamientos adicionales.

Quiero reiterar que la única manera de obtener una referencia nula es a través de un código malformado, y una vez que lo tienes obtienes un comportamiento indefinido. Es nunca tiene sentido comprobar si hay una referencia nula; por ejemplo, puedes intentar if(&bar==NULL)... pero el compilador podría optimizar la declaración de la existencia! Una referencia válida nunca puede ser NULL, así que desde el punto de vista del compilador la comparación es siempre falsa - esta es la esencia del comportamiento indefinido.

La manera apropiada de mantenerse fuera de problemas es evitar desreferenciar un puntero NULL para crear una referencia. Aquí hay una forma automatizada de lograr esto.

template<typename T>
T& ref(T* p)
{
    if (p == NULL)
        throw std::invalid_argument(std::string("NULL reference"));
    return *p;
}

MyClass * p = GetInstance();
Foo(ref(p));

53voto

Orion Edwards Puntos 54939

Olvidaste la parte más importante

acceso de miembros con punteros usa ->
acceso de miembros con referencias usos .

foo.bar es claramente superior a foo->bar de la misma manera que vi es claramente superior al emacs :-)

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X