Ich hätte gerne eine private statische Konstante für eine Klasse (in diesem Fall eine Formfabrik).
Ich hätte gerne so etwas.
class A {
private:
static const string RECTANGLE = "rectangle";
}
Leider bekomme ich vom C ++ (g ++) Compiler alle möglichen Fehler, wie zum Beispiel:
ISO C ++ verbietet die Initialisierung des Mitglieds 'RECTANGLE'
Ungültige In-Class-Initialisierung eines statischen Datenelements vom nicht integralen Typ 'std :: string'
Fehler: 'RECTANGLE' wird statisch
Dies sagt mir, dass diese Art von Elementdesign nicht dem Standard entspricht. Wie haben Sie eine private Literalkonstante (oder vielleicht eine öffentliche), ohne eine # define-Direktive verwenden zu müssen (ich möchte die Hässlichkeit der Datenglobalität vermeiden!)
Jede Hilfe wird geschätzt.
Antworten:
Sie müssen Ihr statisches Element außerhalb der Klassendefinition definieren und dort den Initialisierer bereitstellen.
Zuerst
und dann
Die Syntax, die Sie ursprünglich verwenden wollten (Initialisierer innerhalb der Klassendefinition), ist nur mit Integral- und Aufzählungstypen zulässig.
Ab C ++ 17 haben Sie eine weitere Option, die Ihrer ursprünglichen Deklaration sehr ähnlich ist: Inline-Variablen
Es ist keine zusätzliche Definition erforderlich.
Oder stattdessen
const
können Sie esconstexpr
in dieser Variante deklarieren . Explizitinline
wäre nicht mehr notwendig, daconstexpr
impliziertinline
.quelle
char const*
hat die Güte, dass es initialisiert wird, bevor alle dynamischen Initialisierungen durchgeführt werden. Im Konstruktor eines Objekts können Sie sich also darauf verlassenRECTANGLE
, dass es bereits initialisiert wurde.In C ++ 11 können Sie jetzt Folgendes tun:
quelle
constexpr
impliziertconst
für var, nicht für type it points. Dhstatic constexpr const char* const
ist das gleiche wiestatic constexpr const char*
, aber nicht das gleiche wiestatic constexpr char*
.Innerhalb von Klassendefinitionen können Sie nur statische Mitglieder deklarieren . Diese müssen definiert außerhalb der Klasse. Für Integralkonstanten zur Kompilierungszeit macht der Standard die Ausnahme, dass Sie Mitglieder "initialisieren" können. Es ist jedoch immer noch keine Definition. Das Übernehmen der Adresse würde beispielsweise ohne Definition nicht funktionieren.
Ich möchte erwähnen, dass ich den Vorteil der Verwendung von std :: string gegenüber const char [] für Konstanten nicht sehe . std :: string ist nett und erfordert eine dynamische Initialisierung. Also, wenn du so etwas schreibst
Im Namespace-Bereich wird der Konstruktor von foo unmittelbar vor der Ausführung von main gestartet und dieser Konstruktor erstellt eine Kopie der Konstanten "hello" im Heapspeicher. Wenn Sie RECTANGLE nicht wirklich als std :: string benötigen, können Sie genauso gut schreiben
Dort! Keine Heap-Zuordnung, kein Kopieren, keine dynamische Initialisierung.
Prost, s.
quelle
Dies sind nur zusätzliche Informationen. Wenn Sie die Zeichenfolge jedoch wirklich in einer Header-Datei haben möchten, versuchen Sie Folgendes:
Obwohl ich bezweifle, dass dies empfohlen wird.
quelle
In C ++ 17 können Sie Inline-Variablen verwenden :
Beachten Sie, dass dies anders ist als die Antwort von abyss.7 : Diese definiert ein tatsächliches
std::string
Objekt, nicht einconst char*
quelle
inline
viele Duplikate erzeugt?Dies ist die Einschränkung. In diesem Fall müssen Sie daher eine Variable außerhalb der Klasse definieren. Antwort von @AndreyT
quelle
Die statischen Klassenvariablen können im Header deklariert werden , müssen jedoch in einer CPP-Datei definiert werden. Dies liegt daran, dass es nur eine Instanz einer statischen Variablen geben kann und der Compiler nicht entscheiden kann, in welche generierte Objektdatei sie eingefügt werden soll, sodass Sie stattdessen die Entscheidung treffen müssen.
Um die Definition eines statischen Werts mit der Deklaration in C ++ 11 beizubehalten, kann eine verschachtelte statische Struktur verwendet werden. In diesem Fall ist das statische Element eine Struktur und muss in einer CPP-Datei definiert werden, die Werte befinden sich jedoch im Header.
Anstatt einzelne Elemente zu initialisieren, wird die gesamte statische Struktur in .cpp initialisiert:
Auf die Werte wird mit zugegriffen
oder - da die Mitglieder privat sind und nur von A verwendet werden sollen - mit
Beachten Sie, dass diese Lösung immer noch unter dem Problem der Reihenfolge der Initialisierung der statischen Variablen leidet. Wenn ein statischer Wert zum Initialisieren einer anderen statischen Variablen verwendet wird, wird die erste möglicherweise noch nicht initialisiert.
In diesem Fall enthalten die statischen Variablen- Header je nach der vom Linker erstellten Initialisierungsreihenfolge entweder {""} oder {".h", ".hpp"}.
Wie in @ abyss.7 erwähnt, können Sie auch verwenden,
constexpr
wenn der Wert der Variablen zur Kompilierungszeit berechnet werden kann. Wenn Sie jedoch Ihre Zeichenfolgen mit deklarierenstatic constexpr const char*
und Ihr Programmstd::string
anderweitig verwendet , entsteht ein Overhead, dastd::string
jedes Mal, wenn Sie eine solche Konstante verwenden, ein neues Objekt erstellt wird:quelle
Der aktuelle Standard erlaubt eine solche Initialisierung nur für statische konstante Integraltypen. Sie müssen also tun, was AndreyT erklärt hat. Dies wird jedoch im nächsten Standard über die neue Member-Initialisierungssyntax verfügbar sein .
quelle
möglich einfach machen:
oder
quelle
constexpr
aber keine statische Funktion erstellen könnenconst
.static const std::string RECTANGLE() const { static const std::string value("rectangle"); return value; }
Sie können sich entweder für die
const char*
oben genannte Lösung entscheiden, aber wenn Sie ständig Zeichenfolgen benötigen, werden Sie viel Overhead haben.Auf der anderen Seite benötigt eine statische Zeichenfolge eine dynamische Initialisierung. Wenn Sie also ihren Wert während der Initialisierung einer anderen globalen / statischen Variablen verwenden möchten, kann das Problem der Initialisierungsreihenfolge auftreten. Um dies zu vermeiden, ist es am billigsten, über einen Getter auf das statische Zeichenfolgenobjekt zuzugreifen, der prüft, ob Ihr Objekt initialisiert ist oder nicht.
Denken Sie daran, nur zu verwenden
A::getS()
. Da Threading nur von gestartet werdenmain()
kann undA_s_initialized
zuvor initialisiert wurdemain()
, benötigen Sie auch in einer Multithread-Umgebung keine Sperren.A_s_initialized
ist standardmäßig 0 (vor der dynamischen Initialisierung). Wenn Sie also vor der Initialisierung vongetS()
s verwenden, rufen Sie die Init-Funktion sicher auf.Übrigens können in der obigen Antwort: " static const std :: string RECTANGLE () const " statische Funktionen nicht sein,
const
weil sie den Status eines Objekts ohnehin nicht ändern können (es gibt keinen Zeiger).quelle
Schneller Vorlauf bis 2018 und C ++ 17.
static_assert 'funktioniert' nur zur Kompilierungszeit
};
Oben ist ein richtiger und legaler Standard C ++ Bürger. Es kann leicht in alle std :: -Algorithmen, Container, Dienstprogramme und dergleichen eingebunden werden. Zum Beispiel:
Genießen Sie das Standard-C ++
quelle
std::string_view
für Konstanten nur , wenn Sie verwendenstring_view
Parameter in allen Ihren Funktionen. Wenn eine Ihrer Funktionen einenconst std::string&
Parameter verwendet, wird eine Kopie einer Zeichenfolge erstellt, wenn Sie einestring_view
Konstante durch diesen Parameter übergeben. Wenn Ihre Konstanten vom Typ sind, werdenstd::string
die Kopien weder fürconst std::string&
Parameter noch fürstd::string_view
Parameter erstellt.inline
Variablen mit ihrer ODR-Semantik in C ++ 17 eintrafen. Aber string_view ist C ++ 17 zu, so dass nurconstexpr auto some_str = "compile time"sv;
macht die Arbeit (und tatsächlich, es ist nicht eine Variable, dann ist esconstexpr
, soinline
ist implizit, wenn Sie eine Variable haben - also nichtconstexpr
- danninline auto some_str = "compile time"sv;
wird es tun, obwohl natürlich ein Namespace-scope Variable, die im Wesentlichen eine globale Variable ist, wäre selten eine gute Idee.