una - tipos de variables en c++ y sus rangos




¿Cuándo es apropiada una variable de referencia y por qué? ¿Puedes explicar la sintaxis real y la ubicación? (7)

Esta pregunta ya tiene una respuesta aquí:

Soy nuevo en C ++. Recientemente hemos comenzado a explorar las variables de referencia en clase, y estoy muy confundido acerca de ellas. No necesariamente cómo hacerlo, ya que entiendo que cambian los valores de las variables, sino más bien en la línea de POR QUÉ un desarrollador querría hacer una cosa así. ¿Qué logran ellos? ¿Guardan memoria? ¿Evitan tener que devolver información?

Aquí es parte del proyecto en el que estamos trabajando. Necesitamos incluir al menos una variable de referencia. Puedo ver cómo escribiría el programa sin la variable de referencia, pero no veo dónde una variable de referencia sería útil o necesaria.

"El usuario puede desear obtener una estimación de una a muchas habitaciones. Las tarifas se basan en los pies cuadrados de las paredes y / o el techo. La compañía estima que se necesitan 2.5 horas para pintar 200 pies cuadrados de espacio de la pared y 3.2 horas para pinte la misma área en el techo. La tasa de mano de obra es de $ 40 por hora. Si el trabajo para pintar WALLS totaliza más de 1400 pies cuadrados de espacio, el cliente recibe un descuento del 15% para todos los pies cuadrados que superen los 1400 pies cuadrados. Descuentos para techos de pintura.

El programa imprimirá un informe final de los costos estimados en un formato profesional.

El programa preguntará al usuario si desea realizar más cálculos antes de salir ".

No los estoy buscando para que hagan mi tarea, y para referencia, acabamos de terminar con las funciones de aprendizaje. Estoy bastante bien, pero hay MUCHAS cosas leyendo estos sitios que no entiendo.

Y, esencialmente, studentID se establecería en 21654. ¿Estoy entendiendo esto correctamente?

Intentemos esto de nuevo:

He revisado esta duplicación sugerida. Si bien cubre los aspectos básicos de los pros / contras de usar variables de referencia en lugar de indicadores y discute multitud de razones para usar ambas, sigo cuestionando la idea básica de cuándo (cuándo es apropiado y no es necesario) y por qué (por qué). es apropiado en ciertas circunstancias, ¿qué ventajas le da al programa?)

Debería usar tales variables y también cómo (la sintaxis y ubicación reales). Casi todos los que están aquí han sido excelentes, y he aprendido mucho sobre el tema a través de mis interacciones con ustedes. Aunque gran parte de esto es repetitivo e irritante para los programadores experimentados, todo esto es nuevo para mí, y necesitaba participar en la conversación tanto como necesitaba la información. He usado Stack Overflow para muchos proyectos, por ejemplo, aprendiendo sobre newString.equalsIgnoreCase () de Java, y admiro tu conocimiento. Solo puedo decirte la verdad, si eso no es lo suficientemente bueno, entonces es lo que es.

Bien, déjame revisar mi entendimiento hasta ahora:

Las variables de referencia tienden a reducir la modificación no deseada de las variables dentro de una función y / o programa.

Las variables de referencia se utilizan para modificar las variables existentes dentro de las funciones.

Esto es útil ya que "mueve" los valores al mismo tiempo que minimiza la copia de esos valores.

Las variables de referencia modifican las variables existentes dentro de las funciones / programas.

No sé si todavía pueden leer esto o no, ya que se ha marcado un duplicado. He estado jugando con algunos de los miniprogramas que me han dado, releído partes de mi libro, he investigado un poco más, etc., y creo que entiendo a nivel rudimentario. Estas variables de referencia le permiten alterar y / o usar otras variables dentro de su código sin tener que introducirlas directamente en su código. No puedo recordar qué usuario estaba usando el ejemplo foo (hubble, bubble), pero fue su código el que finalmente lo hizo clic. En lugar de solo usar el valor, en realidad está usando y / o reasignando la variable.


Casi siempre se podrían usar variables de referencia (en lugar de pasar por valor): por ejemplo ...

// this function creates an estimate
// input parameter is the Rooms to be painted
// passed as a const reference because this function doesn't modify the rooms
// return value is the estimated monetary cost
Money createEstimate(const Rooms& rooms)
{
  ...
}

// this function adds paint to the rooms
// input parameter is the Rooms to be painted
// passed as a non-const reference because this function modifies the rooms
void paintRooms(Rooms& rooms)
{
  ...
}

Cuando pasas por valor en lugar de paso por referencia, creas y pasas implícitamente una copia de la cosa ...

// creates and passes a copy of the Rooms to the createEstimate function
Money createEstimate(Rooms rooms)
{
  ...
}

... que (crear una copia) es (a menudo, un poco) más lento que pasar por referencia (además, crear una copia puede tener efectos secundarios).

Como posible optimización ligera del rendimiento, y por convención (porque a las personas no les importa), es común pasar de valor en lugar de pasar de referencia cuando el tipo es pequeño y simple (también conocido como tipo "primitivo"), por ejemplo:

// passes a copy of the x and y values
// returns the sum
int add(int x, int y)
{
  ...
}

... en lugar de ...

// passes a reference to x and y
// returns the sum
int add(const int& x, const int& y)
{
  ...
}

Consulte también Pasar un parámetro modificable a la función c ++ y ¿Por qué tener parámetros de puntero?


Daré tres razones, pero hay muchas más.

  1. Evitando copias innecesarias.

    Supongamos que escribes una función así:

    double price(std::vector<Room> rooms)
    {
           ...
    }

    Ahora, cada vez que lo llames, se copiará el vector de Room . Si solo calcula los precios de unas pocas habitaciones, está bien, pero si desea calcular el costo de volver a pintar la totalidad de las oficinas del Empire State Building, comenzará a copiar objetos enormes, y esto lleva tiempo.

    En este caso, es mejor utilizar una referencia constante que proporcione acceso de solo lectura a los datos:

    double price(const std::vector<Room>& rooms) { ... }
  2. Usando el polimorfismo

    Supongamos que ahora tiene diferentes tipos de habitaciones, tal vez un CubicRoom y un CylindricalRoom , que ambos heredan de la misma clase base, Room .

    No es posible escribir:

    double price(Room room) { ... }

    y luego llamar

    price(CylindricalRoom());
    //or
    price(CubicRoom());

    pero puede hacerlo si define el price siguiente manera:

    double price(Room& room);

    Entonces todo funciona igual que si pasara por valor.

  3. Evitando devoluciones

    Supongamos que cada vez que calcula un precio, desea agregar una cotización con formato a un informe. En C ++ solo puede devolver un solo objeto desde una función, por lo que no puede escribir:

    return price, fmtQuote

    Sin embargo, usted puede hacer:

    double price(Room room, std::vector<std::string>& quotes)
    {
        ...
        quotes.push_back(fmtQuote);
        return price
    }

    Obviamente, podría devolver un par de objetos std::pair<double, std::string> , pero esto significa que la persona que llama debe desempaquetar el resultado. Si tiene la intención de llamar a menudo la función anterior, esto rápidamente se volverá feo. En este caso, esto se vincula con el primer punto: el registro de todas las comillas aumentará y no desea copiarlo para cada llamada.

    Este es un patrón de acceso típico para los recursos compartidos : desea que algunas funciones / objetos obtengan un identificador de un recurso, no una copia de ese recurso.


Hay otra ventaja, más general, de las referencias que los punteros no proporcionan. Las referencias por su propia naturaleza le permiten expresar a través de la firma de la función que el objeto al que se hace referencia debe existir en el momento en que se llama la función No se permiten nulos.

La persona que llama no puede esperar razonablemente una función que tome una referencia para verificar la validez de esa referencia.

Los punteros, por otro lado, pueden ser válidamente nulos. Si escribo una función que acepta un puntero ...

void increment(int* val)
{ 
    (*val)++;
}

... y la persona que llama proporciona un valor nulo, mi programa probablemente se bloquee. Puedo escribir toda la documentación que quiero indicando que el puntero no debe ser nulo, pero el hecho es que es bastante fácil que alguien lo pase accidentalmente. Así que si quiero estar seguro, debo verificarlo.

Pero escribe esta función con una referencia y la intención es clara. No se permiten nulos.


Las referencias se introdujeron principalmente para soportar la sobrecarga del operador. El uso de punteros para "pasar por referencia" le daría una sintaxis inaceptable según Bjarne Stroustrup. También permiten aliasing.

Además, permiten la programación orientada a objetos con una sintaxis más agradable que el uso del puntero explícitamente. Si está utilizando clases, debe pasar referencias para evitar el corte de objetos .

En resumen, siempre debe preferir el uso de referencias sobre punteros vacíos.


Las variables de referencia son una alternativa más segura a los punteros. Por lo general, cuando se trata de punteros, realmente no le importa el puntero ( ptr ) sino lo que apunta a ( *ptr ); y, sin embargo, todo el tiempo los programadores estropean y manipulan ptr lugar de *ptr y así sucesivamente. Considere este código:

void zeroize_by_pointer(int* p)
{
    p = 0; // this compiles, but doesn't do what you want
}

Compare con la versión de referencia,

void zeroize_by_reference(int& p)
{
    p = 0; // works fine
}

Hay muchas otras razones por las que las referencias son una buena idea, pero para alguien que comienza en C ++, sugiero que se centre en esto: hace que sea un poco más difícil dispararse en el pie. Siempre que trate con punteros, tendrá que lidiar en algún nivel con el modelo de memoria de la máquina, y eso es bueno evitarlo cuando sea posible.


Los argumentos de referencia se utilizan más cuando pasas un objeto como argumento. De esa manera usted no copia toda la variable; Por lo general, vienen con un modificador const como:

void printDescription(const Person& person) { ... }

De esa manera usted no copia el objeto.

En algún momento el tipo de retorno también se establece como una referencia. De esa manera, está devolviendo el mismo objeto (y no una copia de él). Echa un vistazo al operador << de ostream. ostream& operator<< (streambuf* sb ); .

Con las variables puede pensar en el caso en el que puede intercambiar valores.

void swap(int & a, int & b) {
    int aux = a;
    int a = b;
    int b = aux;
}

Este caso en Java, por ejemplo, tiene que hacerse de una manera más compleja.


Una variable de referencia no es más que un nombre de alias de la variable. Lo usaría cuando quisiera simplemente pasar el valor en lugar de copiar la misma variable en la memoria en una ubicación diferente. Por lo tanto, utilizando la referencia, se puede evitar la copia que guarda la memoria.

Según las preguntas frecuentes de Bjarne Stroustrup :

C ++ heredó los punteros de C, por lo que no pude eliminarlos sin causar problemas de compatibilidad graves. Las referencias son útiles para varias cosas, pero la razón directa por la que las introduje en C ++ fue para apoyar la sobrecarga del operador. Por ejemplo:

void f1(const complex* x, const complex* y)    // without references
    {
        complex z = *x+*y;    // ugly
        // ...
    }

    void f2(const complex& x, const complex& y)    // with references
    {
        complex z = x+y;    // better
        // ...
    }

De manera más general, si desea tener tanto la funcionalidad de los punteros como la funcionalidad de las referencias, necesita dos tipos diferentes (como en C ++) o dos conjuntos diferentes de operaciones en un solo tipo. Por ejemplo, con un solo tipo necesita tanto una operación para asignar al objeto al que se hace referencia como una operación para asignar a la referencia / puntero. Esto se puede hacer usando operadores separados (como en Simula). Por ejemplo:

Ref<My_type> r :- new My_type;
r := 7;            // assign to object
r :- new My_type;    // assign to reference

Alternativamente, podría confiar en la verificación de tipo (sobrecarga). Por ejemplo:

Ref<My_type> r = new My_type;
r = 7;            // assign to object
r = new My_type;    // assign to reference

Además, lea esta pregunta de Desbordamiento de pila sobre las diferencias entre una variable de puntero y una variable de referencia.







reference