2010-11-26 16 views
8

? Puedo usar un objeto como puntero a él, o su referencia. Entiendo que la diferencia es que los punteros deben eliminarse manualmente, y las referencias permanecen hasta que están fuera del alcance.Objetos de C++: ¿Cuándo debo usar el puntero o referencia

¿Cuándo debo usar cada uno de ellos? ¿Cuál es la diferencia práctica?

Ninguna de estas preguntas contestadas mis dudas:

+0

que no tienen que eliminar el puntero en sí, así como la referencia no elimina automáticamente el objeto referenciado. ¿Estás seguro de que no estás confundiendo referencias y valores/objetos? – DaVinci

+0

Estoy bastante seguro de la diferencia entre la diferencia entre valor/referencia. Mi problema es cuándo declarar un objeto como un puntero y pasar el puntero, o declararlo como un valor y pasar su referencia. – rafaelxy

Respuesta

17

Una referencia es básicamente un puntero con restricciones (tiene que estar vinculado a la creación, no puede ser rebote/nulo). Si tiene sentido que su código utilice estas restricciones, el uso de una referencia en lugar de un puntero permite al compilador advertirle sobre la violación accidental de los mismos.

Se parece mucho al calificador const: el idioma podría existir sin él, solo está ahí como una característica extra que facilita el desarrollo de código seguro.

+0

¿Quieres decir que si tengo un puntero const, se comporta como una referencia? – rafaelxy

+1

No, la comparación 'const' no debe ser relevante para los punteros per se: los punteros const y las referencias no son las mismas (más sobre esto aquí: http://www.parashift.com/c++-faq-lite /references.html#faq-8.5). Fue solo un ejemplo de una función de idioma que impone cierta semántica, por lo que no es necesario. – suszterpatt

+3

"el lenguaje podría existir sin él" - C++ podría existir sin referencias, pero luego habría algunas cosas que ya no podría hacer, que puede hacer hoy (por lo que no sería el * mismo * idioma). Por ejemplo, algunas construcciones de sobrecarga del operador solo pueden hacerse con referencias, no con punteros. Ejemplo: si desea utilizar el encadenamiento de operador con el operador de asignación. – Dan

0

Lo que pasa es que no se puede volver a enlazar una referencia a otro objeto. Las referencias están vinculadas al tiempo de compilación y no pueden ser nulas o rebotadas. Por lo tanto, los punteros no son redundantes si su duda es que :)

+0

Bueno, tu respuesta ayuda, pero no resuelves todo. Todavía tengo dudas sobre cuándo usar la referencia al valor del objeto o usar un puntero al mismo – rafaelxy

3

Tiene muchas situaciones en las que un parámetro no existe o no es válido y esto puede depender de la semántica del código en tiempo de ejecución. En tales situaciones, puede usar un puntero y configurarlo en NULL (0) para señalar este estado. Además de esto,

  • Un puntero se puede volver a asignar a un nuevo estado . Una referencia no puede. Esto es deseable en algunas situaciones.
  • Un puntero ayuda a transferir la semántica de propietario-barco. Esto es especialmente útil en el entorno de subprocesos múltiples si el parámetro-estado se usa para ejecutar en un subproceso separado y no suele sondear hasta que el subproceso ha salido. Ahora el hilo puede eliminarlo.
2

Erm ... no exactamente. Es el IDENTIFICADOR el que tiene un alcance. Cuando crea un objeto usando new, pero el alcance de su identificador finaliza, puede terminar con una pérdida de memoria (o no, depende de lo que desee lograr); el objeto está en la memoria, pero no tiene forma de referenciarlo nunca más.

La diferencia es que un puntero es una dirección en la memoria, por lo que si usted tiene, por ejemplo, este código:

int * a = new int; 

a es un puntero. Puede imprimirlo, y obtendrá algo como "0x0023F1", es solo eso: una dirección. No tiene ningún valor (aunque algún valor se almacena en la memoria en esa dirección).

int b = 10; 

b es una variable con un valor de 10. Si lo imprima, obtendrá 10.

Ahora, si quieres a para apuntar a b 'dirección de s, que puede hacer:

a = &b; //a points to b's address 

o si desea que la dirección apuntada por a tener b' s Valor:

*a = b; //value of b is assigned to the address pointed by a 

Compile esta muestra y comente/comente las líneas 13 y 14 para ver la diferencia (observe DONDE señalan los identificadores y QUÉ VALOR). Espero que la salida sea autoexplicativa.

#include <iostream> 

using namespace std; 

int main() 
{ 
    int * a = new int; 
    int b = 10; 
    cout << "address of a: " << a << endl; 
    cout << "address of b: " << &b << endl; 
    cout << "value of a: " << *a << endl; 
    cout << "value of b: " << b << endl; 
    a = &b; //comment/uncomment 
    //*a = b; //comment/uncomment 
    cout << "address of a: " << a << endl; 
    cout << "address of b: " << &b << endl; 
    cout << "value of a: " << *a << endl; 
    cout << "value of b: " << b << endl; 
} 
2

Respondamos la última pregunta primero. Entonces la primera pregunta tendrá más sentido.

P: "¿Cuál es la diferencia práctica [entre un puntero y una referencia]?"

A: Una referencia es solo un seudónimo local para otra variable. Si pasa un parámetro por referencia, entonces ese parámetro es exactamente la misma variable que el que figura en la declaración de llamada. Sin embargo, internamente no suele haber diferencia entre un puntero y una referencia. Las referencias proporcionan "azúcar de sintaxis" al permitirle reducir la cantidad de tipeo que tiene que hacer cuando todo lo que realmente deseaba era acceder a una instancia única de una variable determinada.

P: "¿Cuándo debo usar cada una de ellas?"

A: Eso será una cuestión de preferencia personal. Aquí está la regla básica que sigo. Si voy a necesitar manipular una variable en otro ámbito, y esa variable es un tipo intrínseco, una clase que debe usarse como un tipo intrínseco (por ejemplo, std :: string, etc.), o una const instancia de clase, luego paso por referencia. De lo contrario, pasaré por el puntero.

3

suszterpatt ya dieron una buena explicación. Si desea una regla práctica que sea fácil de recordar, sugeriría lo siguiente:

Si es posible use referencias, use punteros solo si no puede evitarlas.

Aún más corto: preferir referencias sobre punteros.

3

Aquí hay otra respuesta (tal vez debería haber editado la primera, pero como tiene un enfoque diferente, pensé que estaría bien tenerlos separados).

Cuando crea un puntero con new, la memoria está reservada y persiste hasta que llame al delete, pero la duración del identificador sigue estando limitada al final del bloque de código. Si crea objetos en una función y los agrega a una lista externa, los objetos pueden permanecer seguros en la memoria después de que la función retorna y todavía puede hacer referencia a ellos sin el identificador.

Aquí hay un ejemplo (simplificado) de Umbra, un framework C++ que estoy desarrollando. Hay una lista de módulos (punteros a objetos) almacenados en el motor.El motor puede añadir un objeto a esa lista:

void UmbraEngine::addModule (UmbraModule * module) { 
    modules.push(module); 
    module->id = modules.size() - 1; 
} 

recuperar uno:

UmbraModule * UmbraEngine::getModule (int id) { 
    for (UmbraModule **it=modules.begin(); it != modules.end(); it++) { 
     if ((*it)->id == id) return *it; 
    } 
} 

Ahora, puedo añadir y obtener módulos sin conocer sus identificadores:

int main() { 
    UmbraEngine e; 
    for (int i = 0; i < 10; i++) { 
     e.addModule(new UmbraModule()); 
    } 
    UmbraModule * m = e.getModule(5); //OK 
    cout << m << endl; //"0x127f10" or whatever 
    for (int j = 0; k < 10; j++) { 
     UmbraModule mm; //not a pointer 
     e.addModule(&mm); 
    } 
    m = e.getModule(15); 
    cout << m << endl; //{null} 
} 

Los módulos La lista persiste durante toda la duración del programa, no es necesario que me preocupe la duración de los módulos si se crean instancias con new :). Entonces eso es básicamente todo: con los punteros, puedes tener objetos de larga vida que nunca necesitan un identificador (o un nombre, si lo deseas) para referenciarlos :).

Otro ejemplo agradable, pero muy simple es la siguiente:

void getVal (int * a) { 
    *a = 10; 
} 
int main() { 
    int b; 
    getVal(&b); 
    return b; 
} 
8

"punteros tengo que borrar y de referencia se mantendrá hasta que alcance su acabado."

No, eso es completamente incorrecto.

Los objetos asignados con new deben borrarse [*]. Los objetos que no están asignados con new no se deben eliminar. Es posible tener un puntero a un objeto que no fue asignado con new, y es posible tener una referencia a un objeto que fue asignado con new.

Un puntero o una referencia es una forma de acceder a un objeto, pero no es el objeto en sí, y no tiene ninguna relación con la forma en que se creó el objeto. La diferencia conceptual es que una referencia es un nombre para un objeto, y un puntero es un objeto que contiene la dirección de otro objeto. Las diferencias prácticas, cómo elegir cuál usar, incluyen la sintaxis de cada una, y el hecho de que las referencias no pueden ser nulas y no pueden volverse a colocar.

[*] con delete. Una matriz asignada con new[] se debe eliminar con delete[]. Hay herramientas disponibles que pueden ayudar a realizar un seguimiento de los recursos asignados y hacer estas llamadas, llamadas punteros inteligentes, por lo que sería bastante raro hacer la llamada usted mismo de forma explícita, en lugar de simplemente hacer los arreglos para que se haga, pero sin embargo Debe ser hecho.

0

Como solía decir mi profesor de C++, los punteros apuntan a la ubicación de la memoria mientras que las referencias son alias. Por lo tanto, la principal ventaja es que se pueden usar de la misma manera que el nombre del objeto al que se refieren, pero en un ámbito donde el objeto no está disponible pasándolo allí.

Mientras que los punteros se pueden redirigir a otra ubicación, las referencias que son como punteros constantes, no se pueden redireccionar. Por lo tanto, las referencias no se pueden usar para recorrer matrices en una función, etc.

Sin embargo, un puntero es una entidad separada que ocupa algo de memoria, pero la referencia es la misma que el objeto referido y no ocupa espacio adicional. Esta es una de sus ventajas.

I también han leído que el tiempo de procesamiento para las referencias son menos,

como

int & i = b;

i ++; toma menos tiempo que

int * j = b;

(* j) ++;

pero aún no lo he confirmado. Si alguien puede arrojar luz sobre esta afirmación, sería genial.

comentarios son bienvenidos :)