El uso anterior de const
es interesante y
probablemente útil en muchos casos, pero no resuelve el
programa original de «cómo hacer una constante en tiempo
de compilación dentro de una clase». La respuesta
requiere del uso de un especificador adicional que se
explicará completamente en el [FIXME:capítulo 10]:
static
. El especificador static
, en esta
situación significa «hay sólo una instancia a pesar de
que se creen varios objetos de la clase» que es
precisamente lo que se necesita: un atributo de clase que es
constante, y que no cambia de un objeto a otro de la misma
clase. Por eso, una static const
de un tipo básico se
puede tratar como una constante en tiempo de compilación.
Hay un característica de static const
cuando se usa
dentro de clases que es un tanto inusual: se debe indicar el
valor inicial en el punto en que se define. Esto sólo ocurre
con static const
y no funciona en otras situaciones
porque todos lo otros atributos deben inicializarse en el
constructor o en otros métodos.
A continuación aparece un ejemplo que muestra la creación y
uso de una static const
llamada
size
en una clase que representa una pila
de punteros a cadenas[65].
//: C08:StringStack.cpp // Using static const to create a // compile-time constant inside a class #include <string> #include <iostream> using namespace std; class StringStack { static const int size = 100; const string* stack[size]; int index; public: StringStack(); void push(const string* s); const string* pop(); }; StringStack::StringStack() : index(0) { memset(stack, 0, size * sizeof(string*)); } void StringStack::push(const string* s) { if(index < size) stack[index++] = s; } const string* StringStack::pop() { if(index > 0) { const string* rv = stack[--index]; stack[index] = 0; return rv; } return 0; } string iceCream[] = { "pralines & cream", "fudge ripple", "jamocha almond fudge", "wild mountain blackberry", "raspberry sorbet", "lemon swirl", "rocky road", "deep chocolate fudge" }; const int iCsz = sizeof iceCream / sizeof *iceCream; int main() { StringStack ss; for(int i = 0; i < iCsz; i++) ss.push(&iceCream[i]); const string* cp; while((cp = ss.pop()) != 0) cout << *cp << endl; } ///:~
Listado 8.12. C08/StringStack.cpp
Como size
se usa para determinar el tamaño
del vector stack
, es adecuado usar una
constante en tiempo de compilación, pero que queda oculta
dentro de la clase.
Fíjese en que push()
toma un const
string*
como argumento, pop()
retorna un const string*
y
StringStack
contiene const
string*
. Si no fuera así, no podría usar una
StringStack
para contener los punteros
de icecream
. En cualquier caso, también
impide hacer algo que cambie los objetos contenidos en
StringStack
. Por supuesto, no todos los
contenedores están diseñados con esta restricción.
En versiones antiguas de C++ el tipo static const
no se permitía dentro de las clases. Esto hacía que
const
no pudiese usarse para expresiones constantes
dentro de clases. Pero muchos programadores lo conseguían
con una solución típica (normalmente conocida como
«enum hack») que
consiste en usar un enum
sin etiqueta y sin
instancias. Una enumeración debe tener establecidos sus
valores en tiempo de compilación, es local a una clase y sus
valores están disponibles para expresiones constantes. Por
eso, es habitual ver código como:
//: C08:EnumHack.cpp #include <iostream> using namespace std; class Bunch { enum { size = 1000 }; int i[size]; }; int main() { cout << "sizeof(Bunch) = " << sizeof(Bunch) << ", sizeof(i[1000]) = " << sizeof(int[1000]) << endl; } ///:~
Listado 8.13. C08/EnumHack.cpp
Este uso de enum
garantiza que no se ocupa
almacenamiento en el objeto, y que todos los símbolos
definidos en la enumeración se evalúan en tiempo de
compilación. Además se puede establecer explícitamente el
valor de los símbolos:
enum { one = 1, two = 2, three };
utilizando tipos enum
enteros, el compilador
continuará contando a partir del último valor, así que el
símbolo three
tendrá un valor 3.
En el ejemplo StringStack anterior, la línea:
static const int size = 100;
podriá sustituirse por:
enum { size = 100 };
Aunque es fácil ver esta técnica en código correcto, el uso
de static const
fue añadido al lenguaje
precisamente para resolver este problema. En todo caso, no
existe ninguna razón abrumadora por la que deba usar
static const
en lugar de enum
, y en
este libro se utiliza enum
porque hay más
compiladores que le dan soporte en el momento en el momento
en que se escribió este libro.