Inicialización de miembros de datos estáticos

¿Por qué la inicialización del miembro de datos estáticos debe estar fuera de la clase?

class X { public: int normalValue = 5; //NSDMI static int i; }; int X::i = 0; 

¿Por qué el miembro de datos estáticos (aquí “yo”) es solo una statement, no una definición?

Es importante distinguir el inicializador que dice cuál es su valor inicial y la definición . Este código modificado es válido, con el inicializador en la definición de la clase:

 class X { public: int normalValue = 5; static const int i = 0; // declaration, with initializer }; const int X::i; // definition 

es decir, lo que debe estar fuera de la clase es una definición, no la inicialización.

Esto se debe a que una variable debe tener una dirección en la memoria (a menos que solo se use en situaciones limitadas, como en expresiones constantes en tiempo de comstackción).

Existe una variable miembro no estática dentro del objeto al que pertenece, por lo que su dirección depende de la dirección del objeto que la contiene. Cada vez que creas una nueva X , también creas una nueva variable X::normalValue . La vida útil del miembro de datos no estáticos comienza con el constructor de la clase. La syntax de NSDMI no tiene nada que ver con la dirección de la variable en la memoria, solo le permite proporcionar un valor inicial en un lugar, en lugar de repetirlo en cada constructor con una lista de inicializadores de constructor explícita.

Por otro lado, una variable miembro estática no está contenida dentro de una instancia de la clase, existe independientemente de cualquier instancia única y existe desde el inicio del progtwig, en una dirección fija. Para que una variable miembro estática (o cualquier otro objeto global) obtenga una dirección única, el vinculador debe ver exactamente una definición de la variable estática, en exactamente un archivo de objeto, y asignarle una dirección.

Debido a que una variable estática necesita exactamente una definición en exactamente un archivo de objeto, no tiene sentido permitir que se proporcione esa definición en la clase, dado que las definiciones de clase suelen existir en los archivos de encabezado y se incluyen en varios archivos de objeto. Entonces, aunque puede proporcionar un inicializador en la clase, aún necesita definir el miembro de datos estáticos en alguna parte.

También puede verlo como declarar una variable extern :

 namespace X { extern int i; } 

Esto declara la variable, pero debe haber una definición en algún lugar del progtwig:

 int X::i = 0; 

Debe proporcionar una definición separada para un miembro de datos estáticos (si se usa odr , como se define en C ++ 11) simplemente porque esa definición residirá en algún lugar, en una y solo una unidad de traducción. Los miembros de datos de clase estáticos son básicamente objetos globales (variables globales) declarados en el scope de clase. El comstackdor quiere que elijas una unidad de traducción específica que contendrá el “cuerpo” real de cada objeto global. Es usted quien debe decidir a qué unidad de traducción colocar el objeto real.

El miembro de clase “estático” es como una variable asignada globalmente (no está relacionada con la instancia de clase única), por lo que debe residir en algún archivo de objeto (y declararse en el archivo “.cpp”) como un símbolo como cualquier variable global.

El miembro de clase simple (no estático) reside en el bloque de memoria asignado para la instancia de clase.

La razón simple es porque las clases generalmente se declaran en archivos de encabezado , que a menudo se incluyen en múltiples archivos cpp. Los miembros de datos estáticos tienen enlaces externos y deben declararse en exactamente una unidad de traducción que los hace no aptos para ser definidos dentro de una clase.

Como señala juanchopanza, lo siguiente está permitido:

 struct A { const static int i = 1; }; 

Sin embargo, esto es solo una statement, no una definición. Aún necesita definirlo si va a usar la dirección de i algún lugar. Por ejemplo:

 f(int); g(int&); X x; // Okay without definition for template arguments char a[A::i]; // Okay without definition, just using value as constant expression &A::i; // Need a definition because I'm taking the address f(A::i); // Okay without definition for pass by value g(A::i); // Need a definition with pass by reference 

Tenga en cuenta que es posible inicializar el miembro de datos estáticos en el punto de statement si es de tipo integral const tipo de enumeración:

Del estándar C ++ 03, §9.4.2

Si un miembro de datos estático es de tipo const integral o const enumeration, su statement en la definición de clase puede especificar un constante-inicializador que será una expresión de constante integral (5.19)

 struct Foo { static const int j = 42; // OK }; 

Cuando el comstackdor genera código binario de una unidad (simplificación extrema: un archivo cpp y todos sus encabezados incluidos) emitirá un símbolo para la variable estática y eventualmente el código de inicialización para esa variable.

Está bien que un símbolo de variable estática se declare en unidades múltiples, pero no está bien que se inicialice varias veces.

Por lo tanto, debe asegurarse de que el código de inicialización solo se emita para una sola unidad. Esto significa que la variable estática debe definirse en exactamente una unidad.

Miembro de datos estáticos

 #include #include class static_var { static int count; //static member of class public : void incr_staticvar() { count++; } void outputc() { cout<<"Value of Static variable Count :- "<