Existen ciertos modificadores de variables que se nos estaban quedando en el tintero y que no habíamos visto todavía. Estos modificadores afectan al modo en que se almacenan las variables y a su ámbito temporal, es decir, la zona de programa desde donde las variables son accesibles.
Sintaxis:
[auto] <tipo> <nombre_variable>;
El modificador auto se usa para definir el ámbito temporal de una variable local. Es el modificador por defecto para las variables locales, y se usa muy raramente.
Sintaxis:
register <tipo> <nombre_variable>;
Indica al compilador una preferencia para que la variable se almacene en un registro de la CPU, si es posible, con el fin de optimizar su acceso y reducir el código.
Los datos declarados con el modificador register tienen un ámbito temporal global.
El compilador puede ignorar la petición de almacenamiento en registro, éste está basado en el análisis que realice el compilador sobre cómo se usa la variable.
Sintaxis:
static <tipo> <nombre_variable>; static <tipo> <nombre_de_función>(<lista_parámetros>);
Se usa con el fin de que las variables locales de una función conserven su valor entre distintas llamadas sucesivas a la misma. Las variables estáticas tienen un ámbito local con respecto a su accesibilidad, pero temporalmente son como las variables externas.
Sintaxis:
extern <tipo> <nombre_variable>; [extern] <tipo> <nombre_de_función>(<lista_parámetros>);
Este modificador se usa para indicar que el almacenamiento y valor de una variable o la definición de una función están definidos en otro módulo o fichero fuente. Las funciones declaradas con extern son visibles por todos los ficheros fuente del programa, salvo que se redefina la función como static.
El modificador extern es opcional para las funciones prototipo.
Se puede usar extern "c" con el fin de prevenir que algún nombre de función pueda ser ocultado por funciones de programas C++.
Sintaxis:
const <tipo> <variable> = <inicialización>; const <tipo> <variable_agregada> = {<lista_inicialización>}; const <tipo> <nombre_de_función>(<lista_parámetros>); <tipo> <nombre_de_función>(<lista_parámetros>) const;
Cuando se aplica a una variable, indica que su valor no puede ser modificado, cualquier intento de hacerlo durante el programa generará un error. Precisamente por eso, es imprescindible inicializar las variables constantes cuando se declaran.
Cuando se trata de un objeto de un tipo agregado: array, estructura o unión, se usa la segunda forma.
En C++ es preferible usar este tipo de constantes en lugar de constantes simbólicas (macros definidas con #define). El motivo es que estas constantes tienen un tipo declarado, y el compilador puede encontrar errores por el uso inapropiado de constantes que no podría encontrar si se usan constantes simbólicas.
Cuando se aplica al valor de retorno de una variable el significado es análogo. Evidentemente, si el valor de retorno no es una referencia, no tiene sentido declararlo como constante, ya que lo es siempre. Pero cuando se trate de referencias, este modificador impide que la variable referenciada sea modificada.
#include <iostream>
using namespace std;
int y;
const int &funcion();
int main() {
// funcion()++; // Ilegal (1)
cout << ", " << y << endl;
cin.get();
return 0;
}
const int &funcion() {
return y;
}
Como vemos en (1) no nos es posible modificar el valor de la referencia devuelta por "funcion".
Cuando se añade al final de un prototipo de función indica que la función no modifica el valor de ninguna variable. Veremos que esto se aplica casi exclusivamente en clases, y en ese contexto tiene gran utilidad.
En este último caso se trata más bien de una especie de promesa, que estaremos, en cualquier caso, obligados a cumplir.
Sintaxis:
class{ ... mutable <tipo> <nombre_variable>; ... }; struct { ... mutable <tipo> <nombre_variable>; ... };
Sirve para que determinados miembros de un objeto de una estructura o clase declarado como constante, puedan ser modificados.
using namespace std; struct stA { int y; int x; }; struct stB { int a; mutable int b; }; int main() { const stA A = {1, 2}; // Obligatorio inicializar const stB B = {3, 4}; // Obligatorio inicializar // A.x = 0; // Ilegal (1) // A.y = 0; // B.a = 0; B.b = 0; // Legal (2) cin.get(); return 0; }
Como se ve en (2), es posible modificar el miembro "b" del objeto "B", a pesar de haber sido declarado como constante. Ninguno de los otros campos, ni en "A", ni en "B", puede ser modificado.
Sintaxis:
volatile <tipo> <nombre_variable>;
Este modificador se usa con variables que pueden ser modificadas desde el exterior del programa, por procesos externos.
El compilador usa este modificador para omitir optimizaciones de la variable, por ejemplo, si se declara una variable sin usar el modificador "volatile", el compilador o el sistema operativo puede almacenar el valor leído la primera vez que se accede a ella, bien en un registro o en la memoria caché. O incluso, si el compilador sabe que no ha modificado su valor, no actualizarla en la memoria normal. Si su valor se modifica externamente, sin que el programa sea notificado, se pueden producir errores, ya que estaremos trabajando con un valor no válido.
Usando el modificador "volatile" obligamos al compilador a consultar el valor de la variable en memoria cada vez que se deba acceder a ella.
Por esta misma razón es frecuente encontrar los modificadores "volatile" y "const": si la variable se modifica por un proceso externo, no tiene mucho sentido que el programa la modifique.
auto, const, extern, mutable, register, static y volatile.
© Septiembre de 2000 Salvador Pozo, salvador@conclase.net