3.5. Alcance

Las reglas de ámbitos dicen cuando es válida una variable, dónde se crea, y cuándo se destruye (es decir, sale de ámbito). El ámbito de una variable se extiende desde el punto donde se define hasta la primera llave que empareja con la llave de apertura antes de que la variable fuese definida. Eso quiere decir que un ámbito se define por su juego de llaves «más cercanas». Para ilustrarlo:

//: C03:Scope.cpp
// How variables are scoped
int main() {
  int scp1;
  // scp1 visible here
  {
    // scp1 still visible here
    //.....
    int scp2;
    // scp2 visible here
    //.....
    {
      // scp1 & scp2 still visible here
      //..
      int scp3;
      // scp1, scp2 & scp3 visible here
      // ...
    } // <-- scp3 destroyed here
    // scp3 not available here
    // scp1 & scp2 still visible here
    // ...
  } // <-- scp2 destroyed here
  // scp3 & scp2 not available here
  // scp1 still visible here
  //..
} // <-- scp1 destroyed here
///:~

Listado 3.21. C03/Scope.cpp


El ejemplo anterior muestra cuándo las variables son visibles y cuando dejan de estar disponibles (es decir, cuando salen del ámbito). Una variable se puede utilizar sólo cuando se está dentro de su ámbito. Los ámbitos pueden estar anidados, indicados por parejas de llaves dentro de otras parejas de llaves. El anidado significa que se puede acceder a una variable en un ámbito que incluye el ámbito en el que se está. En el ejemplo anterior, la variable scp1 está disponible dentro de todos los demás ámbitos, mientras que scp3 sólo está disponible en el ámbito más interno.

3.5.1. Definición de variables «al vuelo»

Como se ha mencionado antes en este capítulo, hay una diferencia importante entre C y C++ al definir variables. Ambos lenguajes requieren que las variables estén definidas antes de utilizarse, pero C (y muchos otros lenguajes procedurales tradicionales) fuerzan a que se definan todas las variables al principio del bloque, de modo que cuando el compilador crea un bloque puede crear espacio para esas variables.

Cuando uno lee código C, normalmente lo primero que encuentra cuando empieza un ámbito, es un bloque de definiciones de variables. Declarar todas las variables al comienzo de un bloque requiere que el programador escriba de un modo particular debido a los detalles de implementación del lenguaje. La mayoría de las personas no conocen todas las variables que van a utilizar antes de escribir el código, de modo que siempre están volviendo al principio del bloque para insertar nuevas variables, lo cual resulta pesado y causa errores. Normalmente estas definiciones de variables no significan demasiado para el lector, y de hecho tienden a ser confusas porque aparecen separadas del contexto en el cual se utilizan.

C++ (pero no C) permite definir variables en cualquier sitio dentro de un ámbito, de modo que se puede definir una variable justo antes de usarla. Además, se puede inicializar la variable en el momento de la definición, lo que previene cierto tipo de errores. Definir las variables de este modo hace el código más fácil de escribir y reduce los errores que provoca estar forzado a volver atrás y adelante dentro de un ámbito. Hace el código más fácil de entender porque es una variable definida en el contexto de su utilización. Esto es especialmente importante cuando se está definiendo e inicializando una variable al mismo tiempo - se puede ver el significado del valor de inicialización por el modo en el que se usa la variable.

También se pueden definir variables dentro de expresiones de control tales como los bucles for y while, dentro de las sentencias de condiciones if, y dentro de la sentencia de selección switch. A continuación hay un ejemplo que muestra la definición de variables al-vuelo:

//: C03:OnTheFly.cpp
// On-the-fly variable definitions
#include <iostream>
using namespace std;

int main() {
  //..
  { // Begin a new scope
    int q = 0; // C requires definitions here
    //..
    // Define at point of use:
    for(int i = 0; i < 100; i++) { 
      q++; // q comes from a larger scope
      // Definition at the end of the scope:
      int p = 12; 
    }
    int p = 1;  // A different p
  } // End scope containing q & outer p
  cout << "Type characters:" << endl;
  while(char c = cin.get() != 'q') {
    cout << c << " wasn't it" << endl;
    if(char x = c == 'a' || c == 'b')
      cout << "You typed a or b" << endl;
    else
      cout << "You typed " << x << endl;
  }
  cout << "Type A, B, or C" << endl;
  switch(int i = cin.get()) {
    case 'A': cout << "Snap" << endl; break;
    case 'B': cout << "Crackle" << endl; break;
    case 'C': cout << "Pop" << endl; break;
    default: cout << "Not A, B or C!" << endl;
  }
} ///:~

Listado 3.22. C03/OnTheFly.cpp


En el ámbito más interno, se define p antes de que acabe el ámbito, de modo que realmente es un gesto inútil (pero demuestra que se puede definir una variable en cualquier sitio). La variable p en el ámbito exterior está en la misma situación.

La definición de i en la expresión de control del bucle for es un ejemplo de que es posible definir una variable exactamente en el punto en el que se necesita (esto sólo se puede hacer en C++). El ámbito de i es el ámbito de la expresión controlada por el bucle for, de modo que se puede re-utilizar i en el siguiente bucle for. Se trata de un modismo conveniente y común en C++; i es el nombre habitual para el contador de un for y así no hay que inventar nombres nuevos.

A pesar de que el ejemplo también muestra variables definidas dentro de las sentencias while, if y switch, este tipo de definiciones es menos común que las de expresiones for, quizás debido a que la sintaxis es más restrictiva. Por ejemplo, no se puede tener ningún paréntesis. Es decir, que no se puede indicar:

while((char c = cin.get()) != 'q')

Añadir los paréntesis extra parecería una acción inocente y útil, y debido a que no se pueden utilizar, los resultados no son los esperados. El problema ocurre porque != tiene orden de precedencia mayor que =, de modo que el char c acaba conteniendo un bool convertido a char. Cuando se muestra, en muchos terminales se vería el carácter de la cara sonriente.

En general, se puede considerar la posibilidad de definir variables dentro de las sentencias while, if y switch por completitud, pero el único lugar donde se debería utilizar este tipo de definición de variables es en el bucle for (dónde usted las utilizará más a menudo).