Was ist der beste Weg, um ein privates statisches Datenelement in C ++ zu initialisieren? Ich habe dies in meiner Header-Datei versucht, aber es gibt mir seltsame Linker-Fehler:
class foo
{
private:
static int i;
};
int foo::i = 0;
Ich vermute, das liegt daran, dass ich ein privates Mitglied von außerhalb der Klasse nicht initialisieren kann. Was ist der beste Weg, dies zu tun?
c++
initialization
static-members
Jason Baker
quelle
quelle
inline static int x[] = {1, 2, 3};
. Siehe en.cppreference.com/w/cpp/language/static#Static_data_membersAntworten:
Die Klassendeklaration sollte sich in der Header-Datei befinden (oder in der Quelldatei, wenn sie nicht freigegeben ist).
Datei: foo.h.
Die Initialisierung sollte sich jedoch in der Quelldatei befinden.
Datei: foo.cpp
Wenn sich die Initialisierung in der Header-Datei befindet, enthält jede Datei, die die Header-Datei enthält, eine Definition des statischen Elements. Während der Link-Phase werden daher Linker-Fehler angezeigt, da der Code zum Initialisieren der Variablen in mehreren Quelldateien definiert wird. Die Initialisierung von
static int i
muss außerhalb einer Funktion erfolgen.Anmerkung: Matt Curtis: weist darauf hin , dass C ++ ermöglicht die Vereinfachung des oben , wenn die statische Elementvariable von const int - Typ (z
int
,bool
,char
). Anschließend können Sie die Mitgliedsvariable direkt in der Klassendeklaration in der Header-Datei deklarieren und initialisieren:quelle
Für eine Variable :
foo.h:
foo.cpp:
Dies liegt daran, dass
foo::i
Ihr Programm nur eine Instanz enthalten kann. Dies entsprichtextern int i
in etwa einer Header-Datei undint i
einer Quelldatei.Für eine Konstante können Sie den Wert direkt in die Klassendeklaration einfügen:
quelle
private
Variablen hier außerhalb von Class initialisiert werden können. Kann dies auch für nicht statische Variablen durchgeführt werden?Class
macht in Cpp keinen Sinn.Seit C ++ 17 können statische Elemente im Header mit dem Schlüsselwort inline definiert werden .
http://en.cppreference.com/w/cpp/language/static
"Ein statisches Datenelement kann als Inline deklariert werden. Ein statisches Inline-Datenelement kann in der Klassendefinition definiert werden und einen Standardelementinitialisierer angeben. Es ist keine Definition außerhalb der Klasse erforderlich:"
quelle
Für zukünftige Betrachter dieser Frage möchte ich darauf hinweisen, dass Sie vermeiden sollten, was monkey0506 vorschlägt .
Header-Dateien sind für Deklarationen.
Header-Dateien werden einmal für jede
.cpp
Datei kompiliert , die sie direkt oder indirekt enthält#includes
, und Code außerhalb einer Funktion wird zuvor bei der Programminitialisierung ausgeführtmain()
.Durch Einfügen von:
foo::i = VALUE;
in den Headerfoo:i
wird der WertVALUE
(was auch immer das ist) für jede.cpp
Datei zugewiesen , und diese Zuweisungen erfolgen in einer unbestimmten Reihenfolge (vom Linker festgelegt), bevor siemain()
ausgeführt werden.Was ist, wenn wir
#define VALUE
in einer unserer.cpp
Dateien eine andere Nummer haben ? Es wird gut kompiliert und wir werden keine Möglichkeit haben zu wissen, welches gewinnt, bis wir das Programm ausführen.Fügen Sie niemals ausgeführten Code in einen Header ein, aus dem gleichen Grund, aus dem Sie niemals
#include
eine.cpp
Datei haben.Include-Wachen (die Sie meiner Meinung nach immer verwenden sollten) schützen Sie vor etwas anderem: Der gleiche Header wird
#include
beim Kompilieren einer einzelnen.cpp
Datei indirekt mehrmals dquelle
Mit einem Microsoft-Compiler [1] können statische Variablen, die nicht
int
ähnlich sind, auch in einer Header-Datei definiert werden, jedoch außerhalb der Klassendeklaration unter Verwendung der Microsoft-spezifischen__declspec(selectany)
.Beachten Sie, dass ich nicht sage, dass dies gut ist, sondern nur, dass dies möglich ist.
[1] Heutzutage unterstützen mehr Compiler als MSC
__declspec(selectany)
- zumindest gcc und clang. Vielleicht sogar noch mehr.quelle
Ist die richtige Syntax zum Initialisieren der Variablen, muss jedoch in der Quelldatei (.cpp) und nicht im Header enthalten sein.
Da es sich um eine statische Variable handelt, muss der Compiler nur eine Kopie davon erstellen. Sie müssen eine Zeile "int foo: i" in Ihrem Code haben, um dem Compiler mitzuteilen, wo er sie ablegen soll. Andernfalls wird ein Linkfehler angezeigt. Wenn sich das in einem Header befindet, erhalten Sie in jeder Datei, die den Header enthält, eine Kopie. Erhalten Sie also mehrfach definierte Symbolfehler vom Linker.
quelle
Ich habe hier nicht genug Repräsentanten, um dies als Kommentar hinzuzufügen, aber IMO ist es ein guter Stil, Ihre Header trotzdem mit # include-Wachen zu schreiben , was, wie Paranaix vor einigen Stunden feststellte, einen Fehler mit mehreren Definitionen verhindern würde. Sofern Sie nicht bereits eine separate CPP-Datei verwenden, ist es nicht erforderlich, nur eine zu verwenden, um statische nicht integrale Elemente zu initialisieren.
Ich sehe keine Notwendigkeit, dafür eine separate CPP-Datei zu verwenden. Sicher können Sie, aber es gibt keinen technischen Grund, warum Sie müssen sollten.
quelle
#endif // FOO_H
#pragma once
Wenn Sie einen zusammengesetzten Typ (z. B. einen String) initialisieren möchten, können Sie Folgendes tun:
Da es sich bei der Methode
ListInitializationGuard
um eine statische Variable innerhalb derSomeClass::getList()
Methode handelt, wird sie nur einmal erstellt, was bedeutet, dass der Konstruktor einmal aufgerufen wird. Diesinitialize _list
variiert je nach dem Wert, den Sie benötigen. Bei jedem nachfolgenden Aufruf vongetList
wird einfach das bereits initialisierte_list
Objekt zurückgegeben.Natürlich muss man darauf zugreifen
_list
immer durch Aufrufen dergetList()
Methode Objekt .quelle
Statisches C ++ 11-Konstruktormuster, das für mehrere Objekte funktioniert
Eine Redewendung wurde vorgeschlagen unter: https://stackoverflow.com/a/27088552/895245, aber hier ist eine sauberere Version, bei der keine neue Methode pro Mitglied erstellt werden muss.
main.cpp
GitHub stromaufwärts .
Kompilieren und ausführen:
Siehe auch: statische Konstruktoren in C ++? Ich muss private statische Objekte initialisieren
Getestet unter Ubuntu 19.04.
C ++ 17 Inline-Variable
Erwähnt unter: https://stackoverflow.com/a/45062055/895245, aber hier ist ein ausführbares Beispiel für mehrere Dateien, um es noch klarer zu machen: Wie funktionieren Inline-Variablen?
quelle
Sie können die Zuweisung auch in die Header-Datei aufnehmen, wenn Sie Header-Guards verwenden. Ich habe diese Technik für eine von mir erstellte C ++ - Bibliothek verwendet. Eine andere Möglichkeit, das gleiche Ergebnis zu erzielen, ist die Verwendung statischer Methoden. Zum Beispiel...
Der obige Code hat den "Bonus", dass keine CPP / Quelldatei erforderlich ist. Wieder eine Methode, die ich für meine C ++ - Bibliotheken verwende.
quelle
Ich folge der Idee von Karl. Ich mag es und jetzt benutze ich es auch. Ich habe die Notation ein wenig geändert und einige Funktionen hinzugefügt
dies gibt aus
quelle
Funktioniert auch in der Datei privateStatic.cpp:
quelle
Was ist mit einer
set_default()
Methode?Wir müssten nur die
set_default(int x)
Methode verwenden und unserestatic
Variable würde initialisiert.Dies würde nicht im Widerspruch zu den übrigen Kommentaren stehen, sondern folgt dem gleichen Prinzip der Initialisierung der Variablen in einem globalen Bereich. Mit dieser Methode machen wir sie jedoch explizit (und leicht verständlich), anstatt die Definition zu haben der dort hängenden Variablen.
quelle
Das Linker-Problem, auf das Sie gestoßen sind, wird wahrscheinlich verursacht durch:
Dies ist ein häufiges Problem für diejenigen, die mit C ++ beginnen. Das statische Klassenmitglied muss in einer einzelnen Übersetzungseinheit, dh in einer einzelnen Quelldatei, initialisiert werden.
Leider muss das statische Klassenmitglied außerhalb des Klassenkörpers initialisiert werden. Dies erschwert das Schreiben von Nur-Header-Code, und daher verwende ich einen ganz anderen Ansatz. Sie können Ihr statisches Objekt über eine statische oder nicht statische Klassenfunktion bereitstellen, zum Beispiel:
quelle
Eine "alte" Art, Konstanten zu definieren, besteht darin, sie durch Folgendes zu ersetzen
enum
:Auf diese Weise muss keine Definition angegeben werden, und es wird vermieden, dass der konstante Wert l festgelegt wird , wodurch Sie einige Kopfschmerzen sparen können, z. B. wenn Sie ihn versehentlich mit ODR verwenden .
quelle
Ich wollte nur etwas erwähnen, das mir etwas fremd ist, als ich das zum ersten Mal sah.
Ich musste ein privates statisches Datenelement in einer Vorlagenklasse initialisieren.
In der Datei .h oder .hpp sieht es ungefähr so aus, als würde ein statisches Datenelement einer Vorlagenklasse initialisiert:
quelle
Dient dies Ihrem Zweck?
quelle