2012-07-02 22 views
15

Perché l'inizializzazione dei membri dei dati statici deve essere al di fuori della classe?Dati statici Inizializzazione dei membri

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

int X::i = 0; 

Perché il membro di dati static (qui "i"), solo una dichiarazione, non una definizione?

+9

Perché. ;) Ecco come viene definita la lingua. ;) – jalf

+0

È consentito 'static const int i = 10'. –

+2

@CharlesBeattie, non è ancora una definizione e talvolta è necessaria una definizione, vedere http://gcc.gnu.org/wiki/VerboseDiagnostics#missing_static_const_definition –

risposta

35

E 'importante distinguere il initializer che dice ciò che il suo valore iniziale è, e la definizione di . Questo codice modificato è valido, con l'inizializzatore nella definizione della classe:

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

const int X::i;     // definition 

cioè cosa deve essere al di fuori della classe è una definizione, non l'inizializzazione.

Questo perché una variabile deve avere un indirizzo nella memoria (meno che non sia utilizzato solo in casi limitati, ad esempio in fase di compilazione espressioni costanti.)

presente una variabile membro static all'interno dell'oggetto che è un membro di, quindi il suo indirizzo dipende dall'indirizzo dell'oggetto che lo contiene. Ogni volta che crei un nuovo X, crei anche una nuova variabile X::normalValue. La durata della vita del membro dati non statici inizia con il costruttore della classe. La sintassi NSDMI non ha nulla a che fare con l'indirizzo della variabile in memoria, ma consente semplicemente di fornire un valore iniziale in un posto, invece di ripeterlo in ogni costruttore con un elenco di inizializzatore costruttore esplicito.

D'altra parte, una variabile membro statica non è contenuta all'interno di un'istanza della classe, esiste indipendentemente da una singola istanza ed esiste dall'inizio del programma, ad un indirizzo fisso. Affinché una variabile membro statica (o qualsiasi altro oggetto globale) ottenga un indirizzo univoco, il linker deve vedere esattamente una definizione della variabile statica, esattamente in un file oggetto, e assegnargli un indirizzo.

Poiché una variabile statica richiede esattamente una definizione in esattamente un file oggetto, non ha senso consentire che tale definizione venga fornita nella classe, poiché le definizioni di classe in genere esistono nei file di intestazione e sono incluse in più file oggetto . Quindi, sebbene sia possibile fornire un inizializzatore nella classe, è comunque necessario definire il membro dei dati statici da qualche parte.

si può anche guardare a esso come dichiara un extern variabile:

namespace X { 
    extern int i; 
} 

Questo dichiara la variabile, ma ci deve essere una definizione da qualche parte nel programma:

int X::i = 0; 
+1

Non è necessario che 'i' sia' const' o 'constexpr' affinché funzioni se è un membro dati statico? –

+0

Oops, si! risolto, grazie –

+0

Dubbio - Ogni volta che crei una nuova X, crei anche una nuova variabile 'X :: i'. Non dovrebbe essere così "Ogni volta che crei una nuova X, crei anche una nuova variabile' X :: normalValue' ". – Rndp13

3

Il membro di classe "statico" è come una variabile allocata globalmente (non è correlato alla singola istanza di classe), quindi deve risiedere in un file oggetto (e deve essere dichiarato nel file "cpp") come simbolo proprio come qualsiasi variabile globale.

Un membro di classe semplice (non statico) risiede nel blocco di memoria allocato per l'istanza di classe.

1

Quando il compilatore genera codice binario da un'unità (estrema semplificazione: un file cpp e tutte le intestazioni incluse) emetterà un simbolo per la variabile statica e, eventualmente, il codice di inizializzazione per quella variabile.

Va bene che un simbolo di variabile statica venga dichiarato in più unità, ma non è corretto inizializzarlo più volte.

Pertanto, è necessario assicurarsi che il codice di inizializzazione venga emesso solo per una singola unità. Ciò significa che la variabile statica deve essere definita esattamente in una unità.

+2

No, si confonde l'inizializzatore con la definizione. L'inizializzatore può essere fornito sulla dichiarazione che viene ripetuta in ogni TU. La definizione (con o senza inizializzatore) deve esistere solo in un posto. –

+0

Invece di pensare all'inizializzazione, trovo che sia più utile pensare in termini di indirizzi. La variabile statica deve avere un indirizzo e solo un indirizzo _one, quindi deve essere definito in una sola unità di traduzione, quindi il linker può dargli un indirizzo. –

6

è necessario fornire una definizione separata per un membro di dati statici (se ODR-utilizzato, come definito in C++ 11), semplicemente perché tale definizione deve risiedere da qualche parte - in una e una sola unità di traduzione. I membri di dati di classe statici sono fondamentalmente oggetti globali (variabili globali) dichiarati nell'ambito della classe. Il compilatore vuole che tu scelga una specifica unità di traduzione che conservi il "corpo" reale di ogni oggetto globale. Sei tu che devi decidere su quale unità di traduzione posizionare l'oggetto reale.

+0

ho pensato che fosse l'inizializzazione dei membri di DATI NON STATICI con C++ 11. – CyberGuy

+0

Ho rimosso l'errato "in C++ 11 anche tutti i membri statici possono avere inizializzatori" reclamano dalla mia risposta. – AnT

2

La semplice ragione è che le classi sono in genere dichiarati in intestazione file, che spesso sono inclusi in più file cpp. I membri dei dati statici hanno un collegamento esterno e devono essere dichiarati esattamente in una unità di traduzione che li rende inadatti per essere definiti all'interno di una classe.

Come juanchopanza sottolinea il seguente è consentito:

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

Tuttavia, questa è solo una dichiarazione non una definizione. Devi ancora definirlo se hai intenzione di usare l'indirizzo di i da qualche parte. Ad esempio:

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

tenga presente che è possibile inizializzare il membro di dati static nel punto di dichiarazione se è di tipo const integrale di tipo const censimento:

Dalla C++ 03 standard §9.4.2

Se un membro dati statico è di tipo const censimento integrale o const, la dichiarazione nella definizione della classe può specificare un costante inizializzatore che forma costante integrale espressione (5.19)

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

Ma anche in questo caso è ancora necessaria una definizione se la variabile è odr-used. –

0

statici Dati Utente

#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(); 
} 
Problemi correlati