Statische C++-Initialisierung vs. __attribute__((constructor))

Lesezeit: 4 Minuten

Benutzer-Avatar
Albert

Beispiel:

struct Foo { Foo() { printf("foo\n"); } };
static Foo foo;

__attribute__((constructor)) static void _bar() { printf("bar\n"); }

Ist es deterministisches Wetter? foo oder bar wird zuerst gedruckt?

(Ich hoffe und würde erwarten, dass Konstruktoren von statischen Objekten immer zuerst ausgeführt werden, bin mir aber nicht sicher, und das GCC-Dokument über das Konstruktorattribut sagt nichts darüber aus.)

  • Wo verwenden Sie solche Compiler-Features?

    – AlexTheo

    9. Dezember 2011 um 15:47 Uhr

  • @AlexTheo: Das ist durchaus üblich. Siehe zB <codesearch.google.com/#search/…>. Sie verwenden es normalerweise jedes Mal, wenn Sie etwas initialisieren möchten.

    – Albert

    9. Dezember 2011 um 15:51 Uhr


  • Eigentlich bevorzuge ich so etwas wie static const bool _isInitialized und das Erstellen einer privaten Initialisierungsfunktion, mit der ich mein Objekt wie const bool MyClass::_isInitialized = initFunction(); Aber das ist nur für Objekte, die ich zuerst initialisieren möchte. Andernfalls sollte der Konstruktor einen Job erledigen.

    – AlexTheo

    9. Dezember 2011 um 16:10 Uhr


  • @AlexTheo Dieser Ansatz funktioniert in C nicht. Sicher können Sie einen Konstruktor in C++ verwenden, aber dies hat den gleichen Effekt wie ein statischer Initialisierer in C.

    – Nevelis

    28. September 2012 um 6:08 Uhr

  • @nevelis wir reden über c++!!!

    – AlexTheo

    28. September 2012 um 10:24 Uhr

Benutzer-Avatar
Nawaz

foo wird zuerst gedruckt, da die Objekte in der Reihenfolge ihrer Deklarationen initialisiert werden. Laufen und sehen:

Übrigens, __attribute__((constructor)) ist kein Standard-C++. Es ist die Erweiterung von GCC. Das Verhalten Ihres Programms hängt also davon ab, wie GCC es definiert hat. Kurz gesagt, es ist demnach implementierungsdefiniert foo wird zuerst gedruckt.

Das Dok sagt,

Das Konstruktorattribut bewirkt, dass die Funktion automatisch aufgerufen wird, bevor die Ausführung in main() eintritt. In ähnlicher Weise bewirkt das Destruktor-Attribut, dass die Funktion automatisch aufgerufen wird, nachdem main() abgeschlossen oder exit() aufgerufen wurde. Funktionen mit diesen Attributen sind nützlich, um Daten zu initialisieren, die während der Ausführung des Programms implizit verwendet werden.

Sie können optional eine ganzzahlige Priorität angeben, um die Reihenfolge zu steuern, in der Konstruktor- und Destruktorfunktionen ausgeführt werden. Ein Konstruktor mit einer kleineren Prioritätszahl läuft vor einem Konstruktor mit einer größeren Prioritätszahl; die umgekehrte Beziehung gilt für Destruktoren. Wenn Sie also einen Konstruktor haben, der eine Ressource zuweist, und einen Destruktor, der dieselbe Ressource freigibt, haben beide Funktionen normalerweise dieselbe Priorität. Die Prioritäten für Konstruktor- und Destruktorfunktionen sind die gleichen wie die, die für C++-Objekte im Namespace-Bereich angegeben sind (siehe C++-Attribute).

Ich denke, der fettgedruckte Text impliziert, dass die Objekte in der Reihenfolge ihrer Deklarationen initialisiert werden, wie ich bereits sagte, was durch ziemlich genau bestätigt wird Online-Demo Auch.

Ich nehme an, Sie möchten das auch lesen:

Wenn Sie die Initialisierungsreihenfolge steuern/ändern möchten, können Sie verwenden init_priority Attribut, Bereitstellung Priorität. Genommen von die Seite:

Some_Class  A  __attribute__ ((init_priority (2000)));
Some_Class  B  __attribute__ ((init_priority (543)));

Hier, B vorher initialisiert wird A.

  • Hm, alle Kommentare wurden gelöscht? Wie ist das möglich. Also, hier noch einmal mein Kommentar: Das Interessante für mich war diese Information zusammen mit dem letzten Link, insb. Dies: In Standard-C++ wird garantiert, dass Objekte, die im Namespace-Bereich definiert sind, in einer Reihenfolge initialisiert werden, die strikt mit der ihrer Definitionen in einer bestimmten Übersetzungseinheit übereinstimmt.

    – Albert

    8. Dezember 2011 um 18:55 Uhr

  • Eigentlich habe ich es gerade an einem anderen Beispiel getestet, und es scheint dort nicht der Fall zu sein.

    – Albert

    9. Dezember 2011 um 14:01 Uhr

  • @Albert: Poste den Code. Der eigentliche Code, ohne ihn ein wenig zu ändern. Poste auch die Ausgabe, die du bekommst.

    – Nawaz

    9. Dezember 2011 um 14:04 Uhr

  • Diese Antwort ist falsch. Gemäß der Dokumente: “Die Prioritäten für Konstruktor- und Destruktorfunktionen sind die gleichen wie die für C++-Objekte im Namespace-Bereich angegebenen (siehe C++-Attribute). Derzeit ist jedoch die Reihenfolge, in der Konstruktoren für C++-Objekte mit statischer Speicherdauer und Funktionen, die mit Attributkonstruktoren ausgestattet sind, aufgerufen werden, nicht festgelegt.” Daher ist die Ausgabe, wie @Albert in seiner Antwort erwähnt, nicht deterministisch.

    – ghd

    17. Dezember 2019 um 3:20 Uhr


Es ist nicht durch den Standard definiert, aber hier ist mein Experiment https://github.com/SanSanch5/static-initialization-order-example

Die Ausgabe auf gcc-9:

CFoo constructed
dll constructor
CBaz constructed
CBar constructed
foo
dll destructor
CBar destructed
CBaz destructed
CFoo destructed

Die Ausgabe auf clang-12:

CFoo constructed
dll constructor
CBaz constructed
CBar constructed
foo
CBar destructed
dll destructor
CBaz destructed
CFoo destructed

Der DLL-Konstruktor wird also vor der statischen Initialisierung aufgerufen, aber wie Sie sehen können, ist die Zerstörung bei gcc und clang etwas anders. Aber die Initialisierung eines statischen Objekts innerhalb eines DLL-Konstruktors beweist experimentell seine Zerstörung nach den anderen statischen Objekten. Aber es ist kein Standard, also nicht zuverlässig.

1015800cookie-checkStatische C++-Initialisierung vs. __attribute__((constructor))

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy