Wenn ich zwei statische Variablen in verschiedenen Kompilierungseinheiten habe, dann ist ihre Initialisierungsreihenfolge nicht definiert. Diese Lektion ist gut gelernt.C++ Initialisierung von statischen Variablen (noch einmal)
Die Frage, die ich habe: sind alle statischen Variablen bereits zugewiesen, wenn der erste initialisiert wird. Mit anderen Worten:
static A global_a; // in compilation unit 1
static B global_b; // in compilation unit 2
struct A {
A() { b_ptr = &global_b; }
B *b_ptr;
void f() { b_ptr->do_something(); }
}
int main() {
global_a.f();
}
Wird Punkt auf einen gültigen Teil des Speichers B_PTR, wobei B zugeordnet ist, und zum Zeitpunkt der Ausführung der Hauptfunktion initialisiert? Auf allen Plattformen?
Längere Geschichte:
Die Übersetzungseinheit 1 ist Qt-Bibliothek. Der andere ist meine Anwendung. Ich habe paar QObject abgeleitete Klassen, die ich in der Lage sein muss, durch den Klassennamen String zu instantiieren. Dazu kam ich mit einer Templat-Factory-Klasse bis:
class AbstractFactory {
public:
virtual QObject *create() = 0;
static QMap<const QMetaObject *, AbstractFactory *> m_Map;
}
QMap<const QMetaObject *, AbstractFactory *> AbstractFactory::m_Map; //in .cpp
template <class T>
class ConcreteFactory: public AbstractFactory {
public:
ConcreteFactory() { AbstractFactory::m_Map[&T::staticMetaObject] = this; }
QObject *create() { return new T(); }
}
#define FACTORY(TYPE) static ConcreteFactory <TYPE> m_Factory;
Dann füge ich dieses Makro auf jeder QObject Unterklasse Definition:
class Subclass : public QObject {
Q_OBJECT;
FACTORY(Subclass);
}
Endlich kann ich eine Klasse durch den Typnamen instanziiert:
QObject *create(const QString &type) {
foreach (const QMetaObect *meta, AbstractFactory::m_Map.keys() {
if (meta->className() == type) {
return AbstractFactory::m_Map[meta]->create();
}
}
return 0;
}
So erhält die Klasse eine statische QMetaObject
Instanz: Subclass::staticMetaObject
aus der Qt-Bibliothek - es wird automatisch generiert in Q_OBJECT
Makro denke ich. Und dann erstellt das FACTORY
Makro eine statische ConcreteFactory<Subclass>
Instanz. ConcreteFactory versucht in seinem Konstruktor, auf Subclass :: staticMetaObject zu verweisen.
Und ich war ziemlich glücklich mit dieser Implementierung auf Linux (gcc), bis ich es mit Visual Studio 2008 kompilierte. Aus irgendeinem Grund war AbstractFactory :: m_Map in der Laufzeit leer, und der Debugger würde nicht mit dem Factory-Konstruktor brechen .
Hier kommt der Geruch von statischen Variablen, die auf andere statische Variablen verweisen.
Wie kann ich diesen Code optimieren, um all diese Fallen zu vermeiden?
Nun, dieser Code unterscheidet sich ziemlich von Ihrer ursprünglichen Frage ... in Ihrer ursprünglichen Frage haben Sie die Adresse einer globalen Variable gespeichert, aber haben erst auf 'main' zugegriffen, zu welcher Zeit die globale Konfiguration vollständig war. Aber jetzt versuchen Sie, es aus dem Konstruktor eines globalen "ConcreteFactory" -Objekts zu verwenden, das viel früher als "main" ist. –
Im detaillierten Beispiel speichere ich die Adresse der statischen Variable 'staticMetaObject'. Aber es wird zur Laufzeit von 'main' über die' create'-Funktion aufgerufen - genau wie ich es im vereinfachten Beispiel beschrieben habe.Ich bin nicht besorgt über die 'statische QMap <...> m_Map' - in meinem echten Code ist tatsächlich in eine statische Funktion eingewickelt, genau wie @Martin York vorgeschlagen. Das detaillierte Beispiel bezieht sich also auf 'staticMetaObject' vom' ConcreteFactory'-Konstruktor. –