ProgrammierungC++ Systemarchitekt / Senior Software Engineer

Wie ist die Reihenfolge der Initialisierung von globalen und statischen Objekten in C++? Wie kann man die korrekte Initialisierung zwischen verschiedenen Übersetzungseinheiten garantieren?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

In C++ werden globale und statische Objekte vor dem Betreten der Funktion main() initialisiert. Der Standard garantiert jedoch nicht die Reihenfolge der Initialisierung von Objekten in verschiedenen Übersetzungseinheiten (Kompilierungsdateien, translation units). Dies kann zu dem sogenannten "static initialization order fiasco" führen – einem Bug, bei dem ein globales Objekt auf ein anderes verweist, das noch nicht initialisiert wurde.

Um eine korrekte Initialisierung zu garantieren und das Problem zu vermeiden, wird das Muster "konstruiere bei erstem Gebrauch" verwendet: Eine Funktion wird deklariert, die einen Verweis auf ein statisches lokales Objekt zurückgibt (es wird beim ersten Aufruf thread-sicher mit C++11 erstellt).

Beispielcode (Muster konstruiere bei erstem Gebrauch):

// foo.h class Config { public: int value; }; Config& getConfig() { static Config config; config.value = 42; // Initialisierung garantiert! return config; }

Fangfrage.

Was passiert, wenn in zwei verschiedenen .cpp-Dateien globale Objekte einander erzeugen?

Antwort: Das Ergebnis ist unbestimmt, es kann zu einem Zugriff auf ein nicht-initialisiertes Objekt kommen. Lösung – die direkte globale Initialisierung durch eine späte Initialisierung mittels eines statischen lokalen Objekts innerhalb einer Funktion ersetzen (s. oben).


Geschichte

-In einem CRM-System haben zwei Module globale Logger deklariert, die bei der Initialisierung aufeinander verwiesen. In verschiedenen Builds der Anwendung änderte sich die Reihenfolge der Initialisierung, was zu schwer nachverfolgbare Bugs führte: Abstürze und Protokollierung auf einen Nullzeiger.


Geschichte

-In einer Grafik-Engine verwies ein globales Containerobjekt für Ressourcen auf den Ressourcenmanager, der selbst global war. Auf Linux und Windows implementierten Compiler die Lade-Reihenfolge unterschiedlich, was zu einer Desynchronisation und verschiedenen Fehlern führte.


Geschichte

-Ein Entwickler führte eine globale Initialisierung von Einstellungen ein, und später fügte ein Kollege eine weitere globale Variable hinzu, die von den Einstellungen abhing. Das funktionierte lokal, brach jedoch im CI zusammen, wo eine andere Modulzusammenstellung stattfand.