2011-01-26 10 views
12

Dopo aver letto molte delle domande relative all'inizializzazione delle variabili statiche, non sono ancora sicuro di come si applica alle variabili const a livello di spazio dei nomi.const variabili nel file di intestazione e fiasco di inizializzazione statica

devo tipo del seguente codice in un file di intestazioneconfig.h generato dallo script di build:

static const std::string path1 = "/xyz/abc"; 
static const std::string path2 = "/etc"; 

Secondo quanto ho letto la parola static non è necessario, anche deprecato qui.

La mia domanda: Il codice di cui sopra è soggetto al fiasco dell'inizializzazione statica?

Se ho quanto segue in una intestazione del filemyclass.h:

class MyClass 
{ 
public: 
    MyClass(const std::string& str) : m_str(str) {} 
    std::string Get() const { return m_str; } 

private: 
    std::string m_str; 
} 

const MyClass myclass1("test"); 

sarà questo pone problemi di inizializzazione statico?

Se ho capito bene, a causa delle variabili const con collegamento interno non ci dovrebbero essere problemi in entrambi i casi?

Edit: (a causa di dribeas risposta)

Forse devo dire che Sono interessato a casi d'uso come:

In main.cpp:

#include <config.h> 
#include <myclass.h> 

std::string anotherString(path1 + myclass1.Get()); 

int main() 
{ 
    ... 
} 

Un'altra domanda per quanto riguarda questo uso caso: il compilatore ottimizzerà lo path2 in questo caso?

risposta

9

Ho cercato di ottenere le informazioni necessarie direttamente dal documento C++ 03 Standard. Ecco cosa ho trovato:

Per quanto riguarda le dichiarazioni const static:

Secondo la sezione 3.5.3 oggetti definiti a livello di spazio dei nomi e dichiarato const hanno collegamento interno per impostazione predefinita. static dichiara inoltre che un oggetto a livello di spazio dei nomi dispone di un collegamento interno, pertanto non è necessario dichiarare un oggetto static const.

Sempre secondo l'allegato D.2

The use of the static keyword is deprecated when declaring objects in namespace scope (see 3.3.5).

Per quanto riguarda l'inizializzazione fiasco statica:

Dal momento che le variabili sono definite in un file di intestazione sono sempre definiti prima di qualsiasi altro oggetti statici che li utilizzano .

Dalla sezione 3.6.2.1:

Objects with static storage duration defined in namespace scope in the same translation unit and dynamically initialized shall be initialized in the order in which their definition appears in the translation unit.

Risposta 1: Ciò significa passando variabili ad un constuctor oggetto statico dovrebbe andare bene.

Risposta 2: Tuttavia, un problema potrebbe verificarsi se le variabili contengono da un costruttore non in linea di un oggetto statico:

né nella sezione 3.6.2.1 né 3.6.2.3 è vero specificato in quale ordine gli oggetti statici in diverse unità di compilazione vengono inizializzati se l'inizializzazione dinamica viene eseguita prima della prima istruzione di main.

Si consideri il seguente:

// consts.h 
#include <string> 

const std::string string1 = "ham"; 
const std::string string2 = "cheese"; 

// myclass.h 
#include <string> 

class MyClass 
{ 
public: 
    MyClass(); 
    MyClass(std::string str); 
    std::string Get() { return memberString; } 
private: 
    std::string memberString; 
} 

// myclass.cpp 
#include "consts.h" 
#include "myclass.h" 

MyClass::MyClass() : memberString(string1) {} 

MyClass::MyClass(std::string str) : memberString(str) {} 

// main.cpp 
#include <iostream> 
#include "consts.h" 
#include "myclass.h" 

MyClass myObject1; 
MyClass myObject2(string2); 

using namespace std; 

int main() 
{ 
    cout << myObject1.Get(); // might not print "ham" 
    cout << myObject2.Get(); // will always print "cheese" 
} 

Dal myclass.cpp ha la propria copia delle variabili const, questi non possono essere inizializzate quando si chiama.

Quindi sì, const variabili definite nel file di intestazione può essere utilizzato in un modo che è incline al fiasco di inizializzazione statico

Per quanto posso vedere questo si applica solo alle variabili che non richiedono inizializzazione statico:

Da C++ 03 di serie, sezione 3.6.2.1:

Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.

12

La prima definizione inserisce path1 in ciascuna unità di compilazione che include config.h. Per evitare ciò, non definire variabili nei file di intestazione. Di solito ci si dichiarano le variabili nell'intestazione come extern:

extern const std::string path1; 
extern const MyClass myclass1; 

e definirli in un file di implementazione, per esempio config.cpp:

const std::string path1 = "/xyz/abc"; 
const MyClass myclass1("test"); 

A volte hai bisogno di una variabile costante che è utilizzabile solo da un file di implementazione. Quindi è possibile dichiarare tale variabile nell'ambito di file come static.

static const std::string path1 = "/xyz/abc"; 

static non è più deprecato. static e extern sono a volte impliciti, ma dimentico sempre dove e come, quindi di solito li specificano esplicitamente per tutte le variabili a livello di spazio dei nomi.

+1

non è vero che il 'static' può essere utilizzato solo nei file di implementazione. –

+0

ovviamente, non esiste alcun concetto di file di implementazione in C++, proverò a cambiare il testo – Philipp

+2

Penso che la frase che stai cercando sia "unità di traduzione" invece di file di implementazione. I valori dovrebbero essere dichiarati nelle intestazioni e definiti al massimo in una unità di traduzione. In questo caso, poiché sono costanti e hanno implicito il collegamento interno, non si ottiene un errore di simbolo definito in modo multiplo al momento del collegamento, ma si ha lo stesso simbolo definito in ogni unità di traduzione che include questa intestazione. Poiché hanno un collegamento interno, non interrompono il collegamento, ma il compilatore non può eliminare i duplicati. Una volta ho rasato 8 MB da un file eseguibile rimuovendo le definizioni di stringhe dalle intestazioni. – legalize

8

Ciò che viene definito come fiasco di inizializzazione statico è un problema quando una variabile di livello del namespace dipende dal valore assegnato a una variabile di livello dello spazio dei nomi diversa che potrebbe essere inizializzata o meno in precedenza. Nei tuoi due esempi non esiste una tale dipendenza e non dovrebbero esserci problemi.

Questo, d'altra parte, è soggetta a tale tipo di errore:

// header.h 
extern const std::string foo; 

// constant.cpp 
const std::string foo("foo"); 

// main.cpp 
#include "header.h" 
const std::string foobar(foo+"bar"); 
int main() { 
    std::cout << foobar << std::endl; 
} 

Non v'è alcuna garanzia che foo viene inizializzata prima foobar, anche se entrambi sono costanti. Ciò significa che il comportamento del programma non è definito e potrebbe stampare "foobar", "bar" o morire.

+0

Ho aggiornato il mio caso d'uso nella domanda originale. Questo è esattamente lo scenario che voglio sapere. –

+0

Non oserei commentare in merito. Il mio primo pensiero è che dovrebbe andare bene (la costante è locale all'unità di traduzione ed è definita prima della seconda costante), ma se possibile eviterei quel costrutto. Il modo più semplice che conosco per essere sicuro sarebbe utilizzare internamente una funzione con una variabile statica invece della var globale: 'inline myclass & global_object() {istanza myclass; restituire istanza; } 'Il linguaggio garantisce che' instance' sarà completamente inizializzato nella prima chiamata alla funzione, quindi 'std :: string other (global_object(). Get());' funzionerà sicuramente ... –

+2

In ogni caso, Vorrei * veramente * cercare di evitarlo del tutto ed eliminare le variabili a livello di spazio dei nomi con la loro inizializzazione. L'inizializzazione delle variabili a livello di spazio dei nomi non è banale, avviene in due passaggi, dove tutti i * globals * che sono istanziati da costanti ottengono i loro valori per primi, e quindi in un secondo passaggio tutto ciò che dipende da una non costante verrà inizializzato secondo l'ordine di definizione (all'interno della stessa unità di traduzione), in un ordine non definito quando più di un'unità di traduzione è collegata a un programma. –

2

Il fiasco di inizializzazione statico fa riferimento a variabili statiche che dipendono dall'uno all'altro. La semplice definizione di alcune variabili static const non sarà una fonte di problemi.

Problemi correlati