Ich hätte gerne eine private statische Konstante für eine Klasse (in diesem Fall eine Shape-Factory).
Ich hätte gerne so etwas.
class A {
private:
static const string RECTANGLE = "rectangle";
}
Leider bekomme ich alle möglichen Fehler vom C++ (g++) Compiler, wie zum Beispiel:
ISO C++ verbietet die Initialisierung des Members ‘RECTANGLE’
ungültige In-Class-Initialisierung des statischen Datenmembers des nicht ganzzahligen Typs „std::string“
Fehler: ‘RECTANGLE’ statisch machen
Dies sagt mir, dass diese Art von Mitgliedsdesign nicht mit dem Standard konform ist. 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 ist willkommen.
Kann mir bitte jemand sagen, was ein “integraler” Typ ist? Danke sehr.
Ein privater statischer String in Ihrer Factory ist keine gute Lösung – bedenken Sie, dass Ihre Factory-Clients wissen müssen, welche Formen unterstützt werden, also legen Sie sie in einem separaten Namespace als static const std::string RECTANGLE = “Rectangle “.
– LukeCodeBaker
9. Januar 2016 um 22:15 Uhr
Wenn Ihre Klasse eine Vorlagenklasse ist, siehe stackoverflow.com/q/3229883/52074
– Trevor Boyd Smith
9. Juli 2018 um 12:50 Uhr
Ameise
Sie müssen Ihr statisches Mitglied außerhalb der Klassendefinition definieren und dort den Initialisierer bereitstellen.
Zuerst
// In a header file (if it is in a header file in your case)
class A {
private:
static const string RECTANGLE;
};
und dann
// In one of the implementation files
const string A::RECTANGLE = "rectangle";
Die Syntax, die Sie ursprünglich verwenden wollten (Initialisierer innerhalb der Klassendefinition), ist nur mit Ganzzahl- und Aufzählungstypen zulässig.
Ab C++17 haben Sie eine weitere Möglichkeit, die Ihrer ursprünglichen Deklaration sehr ähnlich ist: Inline-Variablen
// In a header file (if it is in a header file in your case)
class A {
private:
inline static const string RECTANGLE = "rectangle";
};
Es ist keine zusätzliche Definition erforderlich.
Ab C++20 statt const du kannst es deklarieren constexpr bei dieser Variante. Explizit inline wäre nicht mehr nötig, da constexpr impliziert inline.
Wenn es nicht erforderlich ist, einen STL-String zu verwenden, können Sie auch einfach ein const char* definieren. (weniger Overhead)
– KSchmidt
14. Oktober 2009 um 2:23 Uhr
Ich bin mir nicht sicher, ob es immer weniger Aufwand ist – es hängt von der Nutzung ab. Wenn dieser Member als Argument an Funktionen übergeben werden soll, die die konstante Zeichenfolge & annehmen, wird während der Initialisierung für jeden Aufruf temporär erstellt, im Gegensatz zur Erstellung eines Zeichenfolgenobjekts. IMHO Overhead für die Erstellung eines statischen String-Objekts ist vernachlässigbar.
– Tadeusz Kopec für die Ukraine
14. Oktober 2009 um 12:49 Uhr
Ich würde auch lieber ständig std::string verwenden. Der Overhead ist vernachlässigbar, aber Sie haben viel mehr Optionen und es ist viel unwahrscheinlicher, dass Sie dumme Dinge wie “magic” == A::RECTANGLE schreiben, nur um ihre Adresse zu vergleichen …
– Matthias M.
14. Oktober 2009 um 14:24 Uhr
der char const* hat den Vorteil, dass es initialisiert wird, bevor die gesamte dynamische Initialisierung abgeschlossen ist. Sie können sich also auf den Konstruktor eines beliebigen Objekts verlassen RECTANGLE damals schon initialisiert worden sein.
– Johannes Schaub – litb
16. Oktober 2009 um 11:40 Uhr
@cirosantilli: Weil von Anfang an in C++ Initialisierer enthalten waren Definitionennicht Erklärungen. Und die Deklaration von Datenelementen innerhalb der Klasse ist genau das: eine Deklaration. (Andererseits wurde eine Ausnahme für const integrale und enum-Member gemacht, und in C++11 – für const-Member von wörtlich Typen.)
– Ant
10. Januar 2013 um 16:24 Uhr
In C++11 können Sie jetzt Folgendes tun:
class A {
private:
static constexpr const char* STRING = "some useful string constant";
};
Leider funktioniert diese Lösung nicht für std::string.
– Kleines Schach
27. Oktober 2015 um 5:51 Uhr
Beachten Sie, dass 1. dies nur mit Literalen funktioniert und 2. dies nicht standardkonform ist, obwohl Gnu/GCC Bußgelder kompiliert, andere Compiler einen Fehler werfen. Definition muss im Körper sein.
– ManuelSchneid3r
16. November 2015 um 11:40 Uhr
@ManuelSchneid3r Wie genau ist das “nicht normgerecht”? Es sieht aus wie Moor-Standard-C++11 Klammer-oder-Gleich-Initialisierung mir.
– Unterstrich_d
10. Februar 2016 um 11:24 Uhr
@rvighne, nein das ist falsch. constexpr impliziert const für var, nicht für type it points. Dh static constexpr const char* const ist das gleiche wie static constexpr const char*aber nicht dasselbe wie static constexpr char*.
– midenok
11. August 2016 um 14:46 Uhr
@abyss.7 – Danke für deine Antwort, und ich habe bitte noch eine: Warum muss es statisch sein?
– Guy Avraham
6. November 2017 um 6:55 Uhr
Innerhalb von Klassendefinitionen können Sie nur erklären statische Mitglieder. Sie müssen definiert außerhalb der Klasse. Für integrale Konstanten zur Kompilierzeit macht der Standard die Ausnahme, dass Sie Mitglieder “initialisieren” können. Es ist aber immer noch keine Definition. Ohne Definition würde zum Beispiel die Adressaufnahme nicht funktionieren.
Ich möchte erwähnen, dass ich keinen Vorteil darin sehe, std::string gegenüber const char zu verwenden[] für Konstanten. std::string ist nett und alles, aber es erfordert eine dynamische Initialisierung. Also, wenn du so etwas schreibst
const std::string foo = "hello";
Auf Namensraumebene wird der Konstruktor von foo direkt vor der Ausführung von main ausgeführt, und dieser Konstruktor erstellt eine Kopie der Konstante “hello” im Heap-Speicher. Sofern Sie nicht wirklich RECTANGLE als std::string benötigen, könnten Sie genauso gut schreiben
// class definition with incomplete static member could be in a header file
class A {
static const char RECTANGLE[];
};
// this needs to be placed in a single translation unit only
const char A::RECTANGLE[] = "rectangle";
Dort! Keine Heap-Zuordnung, kein Kopieren, keine dynamische Initialisierung.
Tschüss, s.
Dies ist eine Pre-C++ 11-Antwort. Verwenden Sie Standard-C++ und verwenden Sie std::string_view.
– Benutzer10133158
25. Oktober 2018 um 7:55 Uhr
C++11 hat kein std::string_view.
– Lukas Salich
8. April 2019 um 15:03 Uhr
In C++ 17 können Sie verwenden Inline-Variablen:
class A {
private:
static inline const std::string my_string = "some useful string constant";
};
Beachten Sie, dass sich dies von der Antwort von abyss.7 unterscheidet: Diese definiert eine tatsächliche std::string Objekt, nicht a const char*
Dies sind nur zusätzliche Informationen, aber wenn Sie die Zeichenfolge wirklich in einer Header-Datei haben möchten, versuchen Sie Folgendes:
Das sieht cool aus 🙂 – Ich vermute, Sie haben einen Hintergrund in anderen Sprachen als C++?
– Pfund.
16. Oktober 2009 um 13:41 Uhr
Ich würde es nicht empfehlen. Ich mache das häufig. Es funktioniert gut und ich finde es offensichtlicher, als die Zeichenfolge in die Implementierungsdatei einzufügen. Die eigentlichen Daten von std::string befinden sich jedoch immer noch auf dem Heap. Ich würde ein const char* zurückgeben, in diesem Fall müssen Sie die statische Variable nicht deklarieren, damit die Deklaration weniger Platz benötigt (codeweise). Allerdings nur Geschmackssache.
– Zoomulator
18. Juni 2012 um 18:27 Uhr
Die statischen Variablen der Klasse können sein erklärt in der Kopfzeile muss aber sein definiert in einer .cpp-Datei. 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 bei 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, aber die Werte befinden sich im Header.
class A
{
private:
static struct _Shapes {
const std::string RECTANGLE {"rectangle"};
const std::string CIRCLE {"circle"};
} shape;
};
Anstatt einzelne Mitglieder zu initialisieren, wird die gesamte statische Struktur in .cpp initialisiert:
A::_Shapes A::shape;
Der Zugriff auf die Werte erfolgt mit
A::shape.RECTANGLE;
oder – da die Mitglieder privat sind und nur von A verwendet werden sollen – mit
shape.RECTANGLE;
Beachten Sie, dass diese Lösung immer noch unter dem Problem der Initialisierungsreihenfolge der statischen Variablen leidet. Wenn ein statischer Wert verwendet wird, um eine andere statische Variable zu initialisieren, ist die erste möglicherweise noch nicht initialisiert.
In diesem Fall die statische Variable Kopfzeilen enthält entweder { “” } oder { “.h”, “.hpp” }, abhängig von der vom Linker erstellten Initialisierungsreihenfolge.
Wie von @abyss.7 erwähnt, könnten Sie auch verwenden constexpr wenn der Wert der Variablen zur Kompilierzeit berechnet werden kann. Aber wenn du deine Saiten mit deklarierst static constexpr const char* und Ihr Programm verwendet std::string sonst gibt es einen Mehraufwand, weil ein neues std::string -Objekt wird jedes Mal erstellt, wenn Sie eine solche Konstante verwenden:
class A {
public:
static constexpr const char* STRING = "some value";
};
void foo(const std::string& bar);
int main() {
foo(A::STRING); // a new std::string is constructed and destroyed.
}
Das sieht cool aus 🙂 – Ich vermute, Sie haben einen Hintergrund in anderen Sprachen als C++?
– Pfund.
16. Oktober 2009 um 13:41 Uhr
Ich würde es nicht empfehlen. Ich mache das häufig. Es funktioniert gut und ich finde es offensichtlicher, als die Zeichenfolge in die Implementierungsdatei einzufügen. Die eigentlichen Daten von std::string befinden sich jedoch immer noch auf dem Heap. Ich würde ein const char* zurückgeben, in diesem Fall müssen Sie die statische Variable nicht deklarieren, damit die Deklaration weniger Platz benötigt (codeweise). Allerdings nur Geschmackssache.
– Zoomulator
18. Juni 2012 um 18:27 Uhr
aJ.
Um diese In-Class-Initialisierungssyntax zu verwenden, muss die Konstante eine statische Konstante vom Typ Ganzzahl oder Aufzählung sein, die durch einen konstanten Ausdruck initialisiert wird.
Dies ist die Einschränkung. Daher müssen Sie in diesem Fall eine Variable außerhalb der Klasse definieren. siehe Antwort von @AndreyT
Kann mir bitte jemand sagen, was ein “integraler” Typ ist? Danke sehr.
– Pfund.
16. Oktober 2009 um 13:43 Uhr
Ganzzahlige Typen beziehen sich auf Typen, die ganze Zahlen darstellen. Sehen publib.boulder.ibm.com/infocenter/comphelp/v8v101/…
– Bläser
27. Juli 2012 um 4:37 Uhr
Ein privater statischer String in Ihrer Factory ist keine gute Lösung – bedenken Sie, dass Ihre Factory-Clients wissen müssen, welche Formen unterstützt werden, also legen Sie sie in einem separaten Namespace als static const std::string RECTANGLE = “Rectangle “.
– LukeCodeBaker
9. Januar 2016 um 22:15 Uhr
Wenn Ihre Klasse eine Vorlagenklasse ist, siehe stackoverflow.com/q/3229883/52074
– Trevor Boyd Smith
9. Juli 2018 um 12:50 Uhr