Frage

C++ garantiert, dass Variablen in einer kompilationseinheit (.cpp-Datei) initialisiert werden in der Reihenfolge der Deklaration.Für die Anzahl der Zusammenstellung Einheiten, die diese Regel funktioniert für jedes separat (ich meine static-Variablen außerhalb von Klassen).

Aber, die Reihenfolge der Initialisierung der Variablen, die nicht definiert ist über unterschiedliche Zusammenstellung Einheiten.

Wo kann ich sehen, einige Erklärungen über diesen Auftrag für gcc und MSVC (ich weiß, dass das Vertrauen auf, das ist eine sehr schlechte Idee ist - es ist einfach zu verstehen, die Probleme, die wir haben können mit legacy-code, wenn die Umstellung auf die neuen GCC-Dur und verschiedene OS)?

War es hilfreich?

Lösung

Wie Sie sagen, dass die Bestellung über verschiedene Übersetzungseinheiten nicht definiert ist.

Innerhalb derselben Übersetzungseinheit der Reihenfolge gut definiert ist. Die gleiche Reihenfolge wie Definition

Das ist, weil diese nicht in der Sprache Ebene gelöst wird, sondern auf der Ebene des Linkers. Sie müssen also wirklich die Linker Dokumentation überprüfen. Obwohl ich bezweifle wirklich diese in irgendeiner Weise nützlich helfen.

Für gcc: Check out ld

Ich habe festgestellt, dass auch die Reihenfolge der Objekte zu ändern Dateien verknüpft werden, kann die Initialisierung Reihenfolge ändern. So ist es nicht nur Ihr Linker, die Sie darum kümmern müssen, aber wie der Linker durch Ihr Build-System aufgerufen wird. Auch versuchen, das Problem ist praktisch ein nicht-Starter zu lösen.

Dies ist im Allgemeinen nur ein Problem, wenn Globals Initialisierung, die sich während ihrer eigenen Initialisierung Referenz (so wirkt sich nur auf Objekte mit Konstruktoren).

Es gibt Techniken, um das Problem zu bekommen.

  • Verzögerte Initialisierung.
  • Schwarz Zähler
  • Legen Sie alle komplexen globalen Variablen innerhalb derselben Übersetzungseinheit.

  • Anmerkung 1: Globals:
    Gebrauchte lose auf statische Speicherdauer Variablen zu verweisen, die möglicherweise vor main() initialisiert werden.
  • Anmerkung 2: Potentiell
    Im allgemeinen Fall erwarten wir statische Speicherdauer Variablen vor Haupt initialisiert werden, aber der Compiler erlaubt Initialisierung in einigen Situationen zu verschieben (die Regeln sind komplex siehe Standard für Details).

Andere Tipps

Ich erwarte, dass der Konstruktor, um zwischen den Modulen ist in erster Linie eine Funktion in welcher Reihenfolge Sie die Objekte an den Linker übergeben.

Allerdings ist GCC können Sie verwenden, um explizit die Reihenfolge angeben für globalen ctors:

class Thingy
{
public:
    Thingy(char*p) {printf(p);}
};

Thingy a("A");
Thingy b("B");
Thingy c("C");

Ausgänge 'ABC', wie man erwarten würde, aber

Thingy a __attribute__((init_priority(300))) ("A");
Thingy b __attribute__((init_priority(200))) ("B");
Thingy c __attribute__((init_priority(400))) ("C");

Ausgänge 'BAC'.

Da Sie bereits wissen, dass Sie sollten nicht auf diese Angaben verlassen, wenn es unbedingt nötig, hier kommt es.Meine Allgemeine Beobachtung über verschiedene toolchains (MSVC, gcc/ld, clang/llvm, etc) ist, dass die Reihenfolge, in der die Objekt-Dateien sind an den linker übergeben wird, die Reihenfolge, in der Sie initialisiert werden.

Es gibt Ausnahmen von dieser, und ich will nicht behaupten alle, aber hier sind die, die ich lief in mich hinein:

1) GCC-Versionen vor Version 4.7 tatsächlich initialisieren in der umgekehrten Reihenfolge der link-Zeile. Dieses ticket in GCC ist, wenn die änderung ist passiert, und es brach eine Menge von Programmen, die hing bei der Initialisierung, um (einschließlich mir!).

2) In GCC und Clang die Verwendung von Konstruktor-Funktion Priorität ändern können Sie die Initialisierung Bestellung.Beachten Sie, dass dies nur für die Funktionen, die erklärt werden "Konstrukteure" (D. H.Sie sollte ausgeführt werden, wie einer globalen Objekt-Konstruktors sein würde).Ich habe versucht, mit Hilfe von Prioritäten, wie und gefunden, dass auch mit der höchsten Priorität auf eine Konstruktor-Funktion, alle Konstruktoren ohne Priorität (z.B.normale Globale Objekte, Konstruktor-Funktionen ohne Priorität) initialisiert wird erste.In anderen Worten, die Priorität ist nur relativ zu anderen Funktionen mit Prioritäten, aber der eigentliche erste-Klasse-Bürger sind diejenigen, ohne Priorität.Um es noch schlimmer, diese Regel ist effektiv das Gegenteil in GCC vor 4.7 aufgrund von Punkt (1) oben.

3) unter Windows, es ist eine sehr nette und nützliche shared-library (DLL) - entry-point-Funktion genannt DllMain(), wenn definiert, wird mit dem parameter "fdwReason" gleich DLL_PROCESS_ATTACH direkt nach der alle globalen Daten wurden initialisiert und bevor die verwendeten Anwendung eine chance hat, rufen Sie keine Funktionen auf, die auf die DLL.Dies ist äußerst nützlich sein, in einigen Fällen, und es gibt absolut nicht Analog Verhalten, um diese auf anderen Plattformen mit GCC oder Clang mit C oder C++.Die nächsten, die Sie finden, ist ein Konstruktor-Funktion mit Priorität (siehe oben Punkt (2)), was absolut nicht die gleiche Sache und funktioniert nicht für viele Anwendungsfälle, die DllMain() funktioniert für.

4) Wenn Sie mit CMake zu generieren build-Systeme, die ich oft zu tun habe, habe ich festgestellt, dass die Reihenfolge der input source-Dateien werden in der Reihenfolge des daraus resultierenden Objekt-Dateien gegeben, um den linker.Jedoch, Häufig Ihre Anwendung/DLL ist auch die Anbindung in anderen Bibliotheken, in welchem Fall diese Bibliotheken werden auf die link-Zeile nach Ihre Eingabe source-Dateien.Wenn Sie schauen, um eine Ihrer globalen Objekte werden die sehr erste zu initialisieren, dann haben Sie Glück und Ihr können setzen die source-Datei, die mit diesem Objekt werden die erste in der Liste der Quelldateien.Allerdings, wenn Sie schauen, um eine der Letzte initialisiert werden (die kann effektiv replizieren DllMain () - Verhalten!) dann können Sie machen einen Anruf zu add_library() mit einer source-Datei zu erzeugen eine statische Bibliothek, und fügen Sie die resultierende statische Bibliothek als letzter link-Abhängigkeit in Ihrem target_link_libraries () - Aufruf für Ihre Anwendung/DLL.Seien Sie vorsichtig, dass Ihre Globale Objekt kann optimiert werden, in diesem Fall, und Sie können die --whole-archive erzwingen der linker nicht zu entfernen unbenutzte Symbole für spezielle tiny-Archiv-Datei.

Tipp Schließen

Zu wissen unbedingt die resultierende Initialisierung der Reihenfolge der verknüpften Anwendung/shared-library, pass --print-Karte ld-linker und Suche nach .init_array (oder in GCC vor 4.7, grep für .ctors).Jede Globale Konstruktor wird gedruckt werden in der Reihenfolge, wird es initialisiert, und denken Sie daran, dass die Reihenfolge ist im Gegenteil in der GCC vor, 4,7 (siehe Punkt (1) oben).

Der Faktor der Motivation für das schreiben dieser Antwort ist, dass ich brauchte, um zu wissen, diese Informationen, die keine andere Wahl hatten, zu verlassen sich auf die Initialisierung, um, und fand nur spärliche bits dieser Informationen in anderen SO-posts und internet-Foren.Die meisten von es wurde gelernt, die durch viel Experimentieren, und ich hoffe, dass dies erspart einige Menschen die Zeit, es zu tun!

http://www.parashift.com/c++ -FAQ-lite / ctors.html # faq-10.12 - Link bewegt sich. diese ein stabiler ist, aber Sie werden danach suchen müssen um.

edit: osgx lieferte eine bessere link .

Neben Martin Kommentare von einem C Hintergrund kommen, denke ich immer an statischen Variablen als Teil des Programms ausführbar, integriert und Raum im Datensegment zugeordnet. So können statische Variablen wie bei der Initialisierung als das Programm geladen gedacht werden, vor jedem Code ausgeführt wird. Die genaue Reihenfolge, in der dies geschieht, kann durch einen Blick auf das Datensegment der Map-Datei ausgegeben vom Linker ermittelt werden, aber für die meisten Absichten und Zwecke der Initialisierung ist simultaeneous.

Edit:. Je nach Konstruktion, um von statischen Objekten haftet nicht tragbar zu sein und sollte wahrscheinlich vermieden werden

Wenn Sie wirklich die endgültige Reihenfolge wollen wissen, ich Sie würde empfehlen, eine Klasse, deren Konstruktor zu erstellen meldet den aktuellen Zeitstempel und erstellen mehrere statische Instanzen der Klasse in jedem Ihrer CPP-Dateien, so dass Sie die endgültige Reihenfolge der Initialisierung wissen konnte, . Achten Sie darauf, einige wenig Zeit setzen in den Konstruktor aufwendige Operation nur damit Sie nicht den gleichen Zeitstempel für jede Datei erhalten Sie.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top