Ich möchte eine Klasse mit einem privaten statischen Datenelement haben (einem Vektor, der alle Zeichen az enthält). In Java oder C # kann ich einfach einen "statischen Konstruktor" erstellen, der ausgeführt wird, bevor ich Instanzen der Klasse erstelle, und die statischen Datenelemente der Klasse einrichten. Es wird nur einmal ausgeführt (da die Variablen schreibgeschützt sind und nur einmal festgelegt werden müssen) und da es eine Funktion der Klasse ist, kann es auf seine privaten Mitglieder zugreifen. Ich könnte dem Konstruktor Code hinzufügen, der prüft, ob der Vektor initialisiert ist, und ihn initialisiert, wenn dies nicht der Fall ist, aber dies führt viele notwendige Prüfungen ein und scheint nicht die optimale Lösung für das Problem zu sein.
Mir fällt der Gedanke ein, dass die Variablen, da sie schreibgeschützt sind, nur öffentliche statische Konstanten sein können, sodass ich sie einmal außerhalb der Klasse festlegen kann, aber es scheint wieder einmal ein hässlicher Hack zu sein.
Ist es möglich, private statische Datenelemente in einer Klasse zu haben, wenn ich sie nicht im Instanzkonstruktor initialisieren möchte?
quelle
Antworten:
Um das Äquivalent eines statischen Konstruktors zu erhalten, müssen Sie eine separate gewöhnliche Klasse schreiben, die die statischen Daten enthält, und dann eine statische Instanz dieser gewöhnlichen Klasse erstellen.
quelle
friend
es sehr sinnvoll ist, dass die KlasseElsewhere
leicht aufStaticStuff
die Interna zugreifen kann (ohne die Kapselung auf gefährliche Weise zu unterbrechen, möchte ich hinzufügen).Nun, du kannst haben
Vergessen Sie nicht (in der .cpp):
Das Programm wird weiterhin ohne die zweite Zeile verknüpft, aber der Initialisierer wird nicht ausgeführt.
quelle
MyClass::a.push_back(i)
statt seina.push_back(i)
?_initializer
ist ein Unterobjekt vonMyClass
. Unterobjekte werden in dieser Reihenfolge initialisiert: Unterobjekte der virtuellen Basisklasse in der Reihenfolge der Tiefe von links nach rechts (wobei jedes einzelne Unterobjekt nur einmal initialisiert wird); dann einfache Basisobjekt-Unterobjekte in der Reihenfolge Tiefe zuerst von links nach rechts; dann Mitgliedsunterobjekte in der Reihenfolge der Deklaration. Daher ist es sicher, die Strategie von EFraim zu verwenden, vorausgesetzt, der Code_initialiser
bezieht sich nur auf Mitglieder, die zuvor deklariert wurden.C ++ 11 Lösung
Seit C ++ 11 können Sie einfach verwenden Lambda-Ausdrücke verwenden , um statische Klassenmitglieder zu initialisieren. Dies funktioniert sogar, wenn Sie eine Konstruktionsreihenfolge zwischen den verschiedenen statischen Elementen festlegen müssen oder wenn Sie statische Elemente haben
const
.Header-Datei:
Quelldatei:
quelle
try catch
Block einschließen, wenn Ausnahmen ausgelöst werden können.In der .h-Datei:
In der CPP-Datei:
quelle
Hier ist ein anderer Ansatz ähnlich dem von Daniel Earwicker, der auch Konrad Rudolphs Vorschlag für eine Freundschaftsklasse verwendet. Hier verwenden wir eine innere private Freund-Dienstprogrammklasse, um die statischen Mitglieder Ihrer Hauptklasse zu initialisieren. Beispielsweise:
Header-Datei:
Implementierungsdatei:
Dieser Ansatz hat den Vorteil, dass die Initializer-Klasse vollständig vor der Außenwelt verborgen bleibt und alles, was in der zu initialisierenden Klasse enthalten ist, erhalten bleibt.
quelle
ToBeInitialized::Initializer::Initializer()
aufgerufen wird, und Sie müssenToBeInitialized::Initializer ToBeInitialized::initializer;
es der Implementierungsdatei hinzufügen . Ich habe einige Dinge von Ihrer Idee und von EFraims Idee übernommen, und es funktioniert genau so, wie ich es brauche und sieht sauber aus. Danke, Mann.Test::StaticTest()
wird während der globalen statischen Initialisierung genau einmal aufgerufen.Der Aufrufer muss der Funktion, die sein statischer Konstruktor sein soll, nur eine Zeile hinzufügen.
static_constructor<&Test::StaticTest>::c;
Erzwingt die Initialisierungc
während der globalen statischen Initialisierung.quelle
Keine
init()
Funktion erforderlich ,std::vector
kann aus einem Bereich erstellt werden:Beachten Sie jedoch, dass Statiken vom Klassentyp Probleme in Bibliotheken verursachen, sodass sie dort vermieden werden sollten.
C ++ 11 Update
Ab C ++ 11 können Sie dies stattdessen tun:
Es entspricht semantisch der C ++ 98-Lösung in der ursprünglichen Antwort, aber Sie können kein String-Literal auf der rechten Seite verwenden, sodass es nicht völlig überlegen ist. Wenn Sie jedoch einen Vektor von jeder anderen Art haben als
char
,wchar_t
,char16_t
oderchar32_t
(Arrays von denen eine als Stringliterale geschrieben werden kann), die C ++ 11 - Version wird ausschließlich Standardcode entfernen , ohne andere Syntax Einführung, im Vergleich zu dem C ++ 98 Ausführung.quelle
Das Konzept der statischen Konstruktoren wurde in Java eingeführt, nachdem sie aus den Problemen in C ++ gelernt hatten. Wir haben also kein direktes Äquivalent.
Die beste Lösung ist die Verwendung von POD-Typen, die explizit initialisiert werden können.
Oder machen Sie Ihre statischen Elemente zu einem bestimmten Typ, der über einen eigenen Konstruktor verfügt, der ihn korrekt initialisiert.
quelle
Beim Versuch, eine Klasse zu kompilieren und zu verwenden
Elsewhere
(aus Earwickers Antwort ), erhalte ich:Es scheint nicht möglich zu sein, statische Attribute von nicht ganzzahligen Typen zu initialisieren, ohne Code außerhalb der Klassendefinition (CPP) einzufügen.
Um diese Kompilierung durchzuführen, können Sie stattdessen " eine statische Methode mit einer statischen lokalen Variablen " verwenden. Etwas wie das:
Sie können dem Konstruktor auch Argumente übergeben oder ihn mit bestimmten Werten initialisieren. Er ist sehr flexibel, leistungsstark und einfach zu implementieren. Sie verfügen lediglich über eine statische Methode, die eine statische Variable und kein statisches Attribut enthält. Die Syntax ändert sich ein wenig, ist aber dennoch nützlich. Hoffe das ist nützlich für jemanden,
Hugo González Castro.
quelle
Ich denke, eine einfache Lösung dafür wird sein:
quelle
Habe gerade den gleichen Trick gelöst. Ich musste die Definition eines einzelnen statischen Elements für Singleton angeben. Aber machen Sie die Dinge komplizierter - ich habe beschlossen, ctor von RandClass () nicht aufzurufen, es sei denn, ich werde es verwenden ... deshalb wollte ich Singleton in meinem Code nicht global initialisieren. Außerdem habe ich in meinem Fall eine einfache Schnittstelle hinzugefügt.
Hier ist der endgültige Code:
Ich habe den Code vereinfacht und verwende die Funktion rand () und ihren Single Seed Initialisierer srand ()
quelle
Hier ist meine Variante der EFraim-Lösung. Der Unterschied besteht darin, dass der statische Konstruktor dank der impliziten Vorlageninstanziierung nur aufgerufen wird, wenn Instanzen der Klasse erstellt werden, und dass keine Definition in der
.cpp
Datei erforderlich ist (dank der Magie der Vorlageninstanziierung).In der
.h
Datei haben Sie:In der
.cpp
Datei können Sie haben:Beachten Sie, dass dies
MyClass::a
nur initialisiert wird, wenn Zeile [1] vorhanden ist, da dies den Konstruktor aufruft (und dessen Instanziierung erfordert), der dann die Instanziierung von erfordert_initializer
.quelle
Hier ist eine andere Methode, bei der der Vektor für die Datei, die die Implementierung enthält, privat ist, indem ein anonymer Namespace verwendet wird. Dies ist nützlich für Dinge wie Nachschlagetabellen, die für die Implementierung privat sind:
quelle
I
undi
etwas Dunkleres benennen, damit Sie sie nicht versehentlich irgendwo weiter unten in der Datei verwenden.Es muss sicherlich nicht so kompliziert sein wie die derzeit akzeptierte Antwort (von Daniel Earwicker). Die Klasse ist überflüssig. In diesem Fall ist kein Sprachkrieg erforderlich.
.hpp Datei:
CPP-Datei:
quelle
GCC bietet
https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
Kennzeichnen Sie eine statische Methode mit diesem Attribut und sie wird beim Laden des Moduls vor main () ausgeführt.
quelle
Sie definieren statische Elementvariablen ähnlich wie Sie Elementmethoden definieren.
foo.h
foo.cpp
quelle
Um eine statische Variable zu initialisieren, tun Sie dies einfach innerhalb einer Quelldatei. Beispielsweise:
quelle
Wie wäre es mit einer Vorlage, die das Verhalten von C # nachahmt?
quelle
Für einfache Fälle wie hier ist eine statische Variable, die in eine statische Elementfunktion eingeschlossen ist, fast genauso gut. Es ist einfach und wird normalerweise von Compilern optimiert. Dies löst jedoch nicht das Problem der Initialisierungsreihenfolge für komplexe Objekte.
quelle
Ist das eine Lösung?
quelle
Ein statischer Konstruktor kann mithilfe einer Friend-Klasse oder einer verschachtelten Klasse wie folgt emuliert werden.
Ausgabe:
quelle
new
ein char-Array nur, um den Zeiger sofort zu verlieren und zu überschreiben?Wow, ich kann nicht glauben, dass niemand die offensichtlichste Antwort erwähnt hat und eine, die das Verhalten des statischen Konstruktors von C # am besten nachahmt, dh sie wird erst aufgerufen, wenn das erste Objekt dieses Typs erstellt wird.
std::call_once()
ist in C ++ 11 verfügbar; Wenn Sie das nicht verwenden können, können Sie dies mit einer statischen booleschen Klassenvariablen und einer atomaren Operation zum Vergleichen und Austauschen durchführen. In Ihrem Konstruktor finden Sie, wenn Sie atomar die Klasse statische Flagge von ändern könnenfalse
zutrue
, und wenn ja , können Sie die statische Konstruktion Code ausführen.Machen Sie es für zusätzliche Gutschrift zu einem 3-Wege-Flag anstelle eines Booleschen, dh nicht ausgeführt, ausgeführt und ausgeführt. Dann können alle anderen Instanzen dieser Klasse durch Sperren gesperrt werden, bis die Instanz, auf der der statische Konstruktor ausgeführt wird, beendet ist (dh einen Speicherzaun ausgeben und dann den Status auf "Fertig ausführen" setzen). Ihr Spin-Lock sollte die "Pause" -Anweisung des Prozessors ausführen, die Wartezeit jedes Mal bis zu einem Schwellenwert verdoppeln usw. - eine ziemlich übliche Spin-Locking-Technik.
In Abwesenheit von C ++ 11, diese sollten Sie loslegen.
Hier ist ein Pseudocode, der Sie führt. Fügen Sie dies in Ihre Klassendefinition ein:
Und das in Ihrem Konstruktor:
quelle