2012-07-02 25 views
15

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

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

int X::i = 0; 

¿Por qué el miembro de datos estáticos (aquí "i") solo es una declaración, no una definición?

+9

Because. ;) Así es como se define el idioma. ;) – jalf

+0

Tiene permitido 'static const int i = 10'. –

+2

@CharlesBeattie, eso todavía no es una definición, y algunas veces se necesita una definición, vea http://gcc.gnu.org/wiki/VerboseDiagnostics#missing_static_const_definition –

Respuesta

35

Es importante distinguir el inicializador que dice lo que su valor inicial es, y la definición. Este código modificado es válida, 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, ¿Qué debe estar fuera de la clase es una definición, no la inicialización.

Esto se debe a una variable debe tener una dirección en la memoria (a menos que sólo se usa en situaciones limitadas, como en expresiones constantes de tiempo de compilación.)

Existe una variable miembro no estática dentro del objeto que es una miembro de, por lo que su dirección depende de la dirección del objeto que lo contiene. Cada vez que crea un nuevo X, también crea 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 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 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.

Dado que una variable estática necesita exactamente una definición en exactamente un archivo de objeto, no tiene sentido permitir esa definición en la clase, dado que las definiciones de clase suelen existir en 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 mire como declarar una variable extern:

namespace X { 
    extern int i; 
} 

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

int X::i = 0; 
+1

¿'i' no tiene que ser' const' o 'constexpr' para que funcione si es un miembro de datos estático? –

+0

¡Uy, sí! arreglado, gracias –

+0

Duda - Cada vez que creas una nueva X también creas una nueva variable 'X :: i'. No debería ser así: "Cada vez que creas una nueva X también creas una nueva variable' X :: normalValue' ". – Rndp13

3

miembro de clase "estático" es como una variable globalmente asignada (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 símbolo como cualquier variable global.

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

1

Cuando el compilador 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.

+2

No, está confundiendo el inicializador con la definición. El inicializador se puede dar en la declaración que se repite en cada TU. La definición (con o sin inicializador) solo debe existir en un lugar. –

+0

En lugar de pensar en la inicialización, creo que es más útil pensar en términos de direcciones. La variable estática debe tener una dirección y solo una dirección, por lo que debe estar definida en exactamente una unidad de traducción, de modo que el vinculador pueda darle una dirección. –

6

Debe proporcionar una definición separada para un miembro de datos estáticos (si es odr-used, como se define en C++ 11) simplemente porque esa definición residirá en alguna parte, 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 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 debe decidir a qué unidad de traducción colocar el objeto real.

+0

pensé que es la inicialización de miembros de DATOS NO ESTÁTICOS con C++ 11. – CyberGuy

+0

Quité el incorrecto "en C++ 11 todos los miembros estáticos pueden tener inicializadores también" reclamar de mi respuesta. – AnT

2

La razón simple es porque las clases generalmente se declaran en los archivos header, que a menudo se incluyen en varios 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 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 usar la dirección i en alguna parte. 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 
1

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

Desde el C++ 03 estándar, §9.4.2

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

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

Pero incluso entonces todavía necesita una definición si la variable se usa odr. –

0

datos estáticos miembro

#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_function : : 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(); 
} 
Cuestiones relacionadas