A veces se necesita un único espacio de almacenamiento para utilizado por todos los objetos de una clase. En C, podría usar una variable global pero eso no es muy seguro. Los datos globales pueden ser modificados por todo el mundo y su nombre puede chocar con otros idénticos si es un proyecto grande. Sería ideal si los datos pudiesen almacenarse como si fuesen globales pero ocultos dentro de una clase y claramente asociados con esa clase.
Esto es posible usando atributos static
. Existe una
única porción de espacio para los atributos static
,
independientemente del número de objetos de dicha clase que se
hayan creado. Todos los objetos comparten el mismo espacio de
almacenamiento static
para ese atributo, constituyendo
una forma de «comunicarse» entre ellos. Pero los
datos static
pertenecen a la clase; su nombre está
restringido al interior de la clase y puede ser public
,
private
o protected
.
Puesto que los datos static
tienen una única porción
de memoria donde almacenarse, independientemente del número de
objetos creados, esa porción debe ser definida en un único
sitio. El compilador no reservará espacio de almacenamiento
por usted. El enlazador reportará un error si un atributo
miembro es declarado pero no definido.
La definición debe realizarse fuera de la clase (no se permite
el uso de la sentencia inline
), y sólo está permitida
una definición. Es por ello que habitualmente se incluye en el
fichero de implementación de la clase. La sintaxis suele traer
problemas, pero en realidad es bastante lógica. Por ejemplo,
si crea un atributo estático dentro de una clase de la
siguiente forma:
class A { static int i; public: //... };
Deberá definir el almacenamiento para ese atributo estático en el fichero de definición de la siguiente manera:
int A::i = 1;
Si quisiera definir una variable global ordinaria, debería utilizar
int i = 1;
pero aquí, el operador de resolución de ámbito y el nombre de
la clase se utilizan para especificar A::i
.
Algunas personas tienen problemas con la idea que
A::i
es private
, y pese a ello parece
haber algo que lo está manipulando abiertamente. ¿No rompe
esto el mecanismo de protección? Ésta es una práctica
completamente segura por dos razones. Primera, el único sitio
donde esta inicialización es legal es en la
definición. Efectivamente, si el dato static
fuese un
objeto con un constructor, habría llamado al constructor en
lugar de utilizar el operador =
. Segundo,
una vez se ha realizado la definición, el usuario final no
puede hacer una segunda definición puesto que el enlazador
indicaría un error. Y el creador de la clase está forzado a
crear la definición o el código no enlazaría en las
pruebas. Esto asegura que la definición sólo sucede una vez y
que es el creador de la clase quien la lleva a cabo.
La expresión completa de inicialización para un atributo estático se realiza en el ámbito de la clase. Por ejemplo,
//: C10:Statinit.cpp // Scope of static initializer #include <iostream> using namespace std; int x = 100; class WithStatic { static int x; static int y; public: void print() const { cout << "WithStatic::x = " << x << endl; cout << "WithStatic::y = " << y << endl; } }; int WithStatic::x = 1; int WithStatic::y = x + 1; // WithStatic::x NOT ::x int main() { WithStatic ws; ws.print(); } ///:~
Listado 10.18. C10/Statinit.cpp
Aquí el calificador WithStatic::
extiende el
ámbito de WithStatic
a la definición
completa.
El capítulo 8 introdujo una
variable static const
que le permite definir un
valor constante dentro del cuerpo de una clase. También es
posible crear arrays de objetos estáticos, ya sean
constantes o no constantes. La sintaxis es razonablemente
consistente:
//: C10:StaticArray.cpp // Initializing static arrays in classes class Values { // static consts are initialized in-place: static const int scSize = 100; static const long scLong = 100; // Automatic counting works with static arrays. // Arrays, Non-integral and non-const statics // must be initialized externally: static const int scInts[]; static const long scLongs[]; static const float scTable[]; static const char scLetters[]; static int size; static const float scFloat; static float table[]; static char letters[]; }; int Values::size = 100; const float Values::scFloat = 1.1; const int Values::scInts[] = { 99, 47, 33, 11, 7 }; const long Values::scLongs[] = { 99, 47, 33, 11, 7 }; const float Values::scTable[] = { 1.1, 2.2, 3.3, 4.4 }; const char Values::scLetters[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; float Values::table[4] = { 1.1, 2.2, 3.3, 4.4 }; char Values::letters[10] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; int main() { Values v; } ///:~
Listado 10.19. C10/StaticArray.cpp
Usando static const
de tipos enteros puede
realizar las definiciones dentro de la clase, pero para
cualquier otro tipo (incluyendo listas de enteros, incluso
si estos son static const
) debe realizar una
única definición externa para el atributo. Estas
definiciones tienen enlazado interno, por lo que pueden
incluirse en ficheros de cabecera. La sintaxis para
inicializar listas estáticas es la misma que para cualquier
agregado, incluyendo el conteo automáticoautomatic counting
.
También puede crear objetos static const
de
tipos de clase y listas de dichos objetos. De todas formas,
no puede inicializarlos utilizando la sintaxis tipo
«inline» permitida para static
const
de tipos enteros básicos:
//: C10:StaticObjectArrays.cpp // Static arrays of class objects class X { int i; public: X(int ii) : i(ii) {} }; class Stat { // This doesn't work, although // you might want it to: //! static const X x(100); // Both const and non-const static class // objects must be initialized externally: static X x2; static X xTable2[]; static const X x3; static const X xTable3[]; }; X Stat::x2(100); X Stat::xTable2[] = { X(1), X(2), X(3), X(4) }; const X Stat::x3(100); const X Stat::xTable3[] = { X(1), X(2), X(3), X(4) }; int main() { Stat v; } ///:~
Listado 10.20. C10/StaticObjectArrays.cpp
La inicialización de listas estáticas de objetos tanto constantes como no constantes debe realizarse de la misma manera, siguiendo la típica sintaxis de definición estática.