c++ c++11 g++

c++ - Inicialización de miembros de datos estáticos



c++11 g++ (7)

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

Está bien que un símbolo de variable estática se declare en varias unidades, 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 exactamente en una unidad.

¿Por qué la inicialización de miembros 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í "i") es solo una declaración, no una definición?


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

Como señala juanchopanza, se permite lo siguiente:

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

Sin embargo, esto es sólo una declaración, no una definición. Aún necesita definirlo si va a utilizar la dirección de i algún lugar. Por ejemplo:

f(int); g(int&); X<A::i> 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


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 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 de tiempo de compilación).

Existe una variable miembro no estática dentro del objeto del que es miembro, 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 sintaxis 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 explícita de inicializadores de constructores.

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 programa, 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 esa definición se proporcione en la clase, ya que las definiciones de clase generalmente existen en los archivos de encabezado y se incluyen en varios archivos de objeto. Entonces, aunque puede proporcionar un inicializador en la clase, aún debe definir el miembro de datos estáticos en algún lugar.

También puedes 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 programa:

int X::i = 0;


Miembro de Datos Estáticos

#include<iostream.h> #include<conio.h> class static_var { static int count; //static member of class public : void incr_staticvar() { count++; } void outputc() { cout<<"Value of Static variable Count :- "<<count<<endl; } }; int static_var::count; void main() { clrscr(); static_var obj1,obj2,obj3,obj4; obj1.incr_staticvar(); obj2.incr_staticvar(); obj3.incr_staticvar(); obj4.incr_staticvar(); cout<<"/nAfter Increment of static variable by Four Different objects is :-/n"; obj1.outputc ( ); obj2.outputc ( ); obj3.outputc ( ); obj4.outputc ( ); getch(); }


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

De la norma C ++ 03, §9.4.2

Si un miembro de datos estáticos es de tipo de enumeración constante o constante, su declaración en la definición de clase puede especificar un inicializador de constantes que será una expresión constante constante (5.19)

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


el miembro de la clase "estática" es como una variable asignada globalmente (no está relacionada con la instancia de una sola clase), por lo que debe residir en algún archivo de objeto (y para ser declarado en el archivo ".cpp") como un símbolo como cualquier otro variable global.

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


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 debe 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 alcance de la clase. El compilador quiere que elijas una unidad de traducción específica que contendrá el "cuerpo" real de cada objeto global. Es usted quien tiene que decidir a qué unidad de traducción colocar el objeto real.