static + seltsames verhalten
-
Hi, mal wieder ein Problem. Bei mir hat jede Klasse eine statische Funktion
Info& GetInfo() { static Info info(...); return info; }mit der ich Information über die Klasse wie den Namen usw. abrufen kann. Zusätzlich haben die Klassen statische Member die über eine statische Funktion "Init" einer anderen Klasse initalisiert werden, z.B so:
Property MyClass::myProperty = Property::Init(L"MyClass");Das funktioniert prima. Allerdings möchte ich nun nicht nur den Klassennamen der Initfunktion übergeben sondern gleich den ganzen Infomember, also z.B. so:
Property MyClass::myProperty = Property::Init(GetInfo());Das funktioniert auch aber diesmal stimmt etwas nicht. Beim Laden der Anwendung werden diverse Dateien geladen, deren Namen ich mir mit den zugehörigen Fehlermeldungen in einer Console ausgeben lasse. Seltsamerweise werden jetzt plötzlich keine Fehlermeldungen mehr ausgegeben, jedoch werden die Namen der Dateien die geladen werden angezeigt. Die Dateien werden auch tatsächlich geladen.
Kann es sein dass der Compiler hier irgendwie rumoptimiert und schon Code in der "Initialisierungspahse" der statischen Member ausführt?
-
Was für Fehlermeldungen meinst du denn?
Zu deinem Code: Du hast dort eine statische (lokale) Variable. Diese wird beim ersten Mal, wenn die Funktion aufgerufen wird, angelegt und initialisiert. Bei weiteren Funktionsaufrufen existiert die Variable bereits und wird nur noch zurückgegeben.
-
Das sind nur unwichtige Warnungen die ich selber ausgebe da meine Implementierung noch nicht komplett ist. Diese haben auf das Verhalten des Programms aber keinen Einfluss. Mit der ersten Variante sieht die Console etwa so aus:
Lade Datei 'ErsteDatei.abc'... Error: ... ... Error: ... Lade Datei 'ZweiteDatei.abc'... Error: ... usw.Mit der zweiten Variante wird in der Console nur noch folgendes ausgegeben:
Lade Datei 'ErsteDatei.abc'... Lade Datei 'ZweiteDatei.abc'... Lade Datei 'DritteDatei.abc'... usw.Ich kann auch beliebige weitere Ausgaben in der Console machen aber sie erscheinen nicht, obwohl der Code nachweislich ausgeführt wird.
Ich habe auch gerade nochmal in Visual Studio nachgeschaut. Ich habe gar keine Optimierungen eingeschaltet. Das ist es also nicht.
Ich glaube ich schreibe mal ein Mini-Programm ansonsten dürfte es für euch schwierig werden was ich meine.
-
Kann es sein dass der Compiler hier irgendwie rumoptimiert und schon Code in der "Initialisierungspahse" der statischen Member ausführt?
Ja klar macht er das, wie soll es auch sonst gehen?
Grob vereinfacht geht das so:
- Alles was global/static ist mit Nullbytes anfüllen
- Konstante globale/static Variablen mit ihrem Wert anfüllen (*)
- Dynamisch initialisierte globale/static Variablen initialisieren (*)
- main() aufrufen
Da dein
MyClass::myPropertyoffensichtlich einstaticMember ist, und offensichtlich dynamisch initialisiert wird (Property::Initist ja eine Funktion, ebenso wieGetInfo()), läuft das also in (3). Und wenn du dabeiGetInfo()aufrufst, dann wird das eben genau an der Stelle ausgeführt, und genau an der Stelle läuft dann auch der Kunstruktor vonstatic Info info.----
*: 1 & 2 können vom Compiler zu einem Schritt zusammengefasst werden.
Was (3) angeht: der Compiler *dürfte* das so lange verzögern, bis die erste Funktion aus der Übersetzungseinheit aufgerufen wird, in der die globalen/static Variablen definiert sind. Bloss tut das kein mir bekannter Compiler. Das oben beschriebene Verhalten ist auch vollkommen standardkonform, der Standard erlaubt explizit dass alles vor dem Beginn von main() initialisiert wird.
Ausgenommen natürlich static Variablen in Funktionen, diese dürfen erst initialisiert werden wenn die Funktion zum 1. mal aufgerufen wird. In deinem Beispiel findet dieser 1. Aufruf der
GetInfo()Funktion allerdings in Phase (3) statt, und daher muss auch die Funktions-lokale static Variable "info" zu diesem Zeitpunkt initialisiert werden.----
Was lernen wir daraus: "static" in C++ ist eine ganz gemeine hinterhältige Sache

-
Ok, ich habe den Fehler gefunden. Ich habe in der Init-Funktion während der Initialisierungspahse eine Fehlermeldung mit std::wcerr ausgegeben was zu Folge hat, dass dann auch später im Programm keine Ausgaben per wcerr mehr funktionieren. Warum auch immer. Lediglich die Ausgabe per wcout hat noch funktioniert weshalb auch die Ausgabe der Dateinamen in der Console erschien, nicht aber die Fehlermeldungen.
-
Im grunde kannst du das komplette Programm bereits vor der main() laufen lassen, wenn du deren Parameter nicht brauchst:
int myRealMain() { /* Hauptprogramm..... */ } const int returnCode = myRealMain(); //wird in Phase 3 aufgerufen int main() { return returnCode; }