2009-10-14 10 views
354

Ich hätte gerne eine private statische Konstante für eine Klasse (in diesem Fall eine Form-Factory). Ich hätte gerne etwas ähnliches.C++ statische Konstante Zeichenfolge (Klassenmitglied)

class A { 
    private: 
     static const string RECTANGLE = "rectangle"; 
} 

Leider bekomme ich alle möglichen Fehler aus dem C++ (g ++) Compiler, wie zum Beispiel:

ISO C++ forbids initialization of member ‘RECTANGLE’

invalid in-class initialization of static data member of non-integral type ‘std::string’

error: making ‘RECTANGLE’ static

Das sagt mir, dass diese Art von Element-Design mit dem Standard nicht kompatibel ist. Wie haben Sie eine private literale Konstante (oder vielleicht öffentliche), ohne eine # define-Direktive zu verwenden (Ich möchte die Hässlichkeit der Daten Globalität vermeiden!)

Jede Hilfe wird geschätzt. Vielen Dank.

+11

Danke für all Ihre tollen Antworten! Es lebe SO! –

+0

Kann mir bitte jemand sagen, was ein integraler Typ ist? Vielen Dank. –

+1

Integrale Typen beziehen sich auf Typen, die ganze Zahlen darstellen. Siehe http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fintvar.htm – bleater

Antwort

377

Sie müssen Ihr statisches Element außerhalb der Klassendefinition definieren und dort den Initialisierer angeben.

Erste

// 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 zu verwenden versuchten (initializer innerhalb Klassendefinition) ist nur mit Integral- und Aufzählungstypen erlaubt.

+5

Wenn Sie keine STL-Zeichenfolge benötigen, können Sie auch einfach const char * definieren. (weniger Aufwand) – KSchmidt

+42

Ich bin mir nicht sicher, dass es immer weniger Aufwand ist - es hängt von der Nutzung ab. Wenn dieses Member als ein Argument an Funktionen übergeben werden soll, die const String & übernehmen, wird für jeden Aufruf vs. eine Stringobjekt-Erstellung während der Initialisierung temporär erstellt. IMHO Overhead für das Erstellen eines statischen String-Objekts ist vernachlässigbar. –

+17

Ich würde lieber std :: string die ganze Zeit auch verwenden. Der Overhead ist vernachlässigbar, aber Sie haben viel mehr Optionen und sind viel weniger wahrscheinlich, einige dumme Dinge wie "Magie" zu schreiben == A :: RECTANGLE nur ihre Adresse zu vergleichen ... –

7

To use that in-class initialization syntax, the constant must be a static const of integral or enumeration type initialized by a constant expression.

Dies ist die Einschränkung. Daher müssen Sie in diesem Fall die Variable außerhalb der Klasse definieren. siehe answwer von @AndreyT

15

Dies ist nur zusätzliche Informationen, aber wenn Sie wirklich die Zeichenfolge in einer Header-Datei wollen, versuchen Sie so etwas wie:

class foo 
{ 
public: 
    static const std::string& RECTANGLE(void) 
    { 
     static const std::string str = "rectangle"; 

     return str; 
    } 
}; 

Obwohl ich, dass empfohlen zweifeln.

+0

Das sieht cool aus :) - ich rate, dass Sie einen Hintergrund in anderen Sprachen als C++ haben? –

+4

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 zu setzen. Die tatsächlichen Daten von std :: string befinden sich jedoch immer noch auf Heap. Ich würde ein const char * zurückgeben, in diesem Fall müssen Sie die statische Variable nicht deklarieren, so dass die Deklaration weniger Platz (Code-weise) benötigt. Nur eine Frage des Geschmacks. – Zoomulator

4

Der aktuelle Standard erlaubt eine solche Initialisierung nur für statische Konstanten. Also müssen Sie tun, wie AndreyT erklärte. Dies wird jedoch im nächsten Standard durch die new member initialization syntax verfügbar sein.

30

Innerhalb Klassendefinitionen können Sie nur deklarieren statische Mitglieder. Sie müssen definiert außerhalb der Klasse sein. Für Kompilierzeit-Integralkonstanten macht der Standard die Ausnahme, dass Sie Member "initialisieren" können. Es ist aber immer noch keine Definition. Die Annahme der Adresse würde beispielsweise ohne Definition nicht funktionieren.

Ich möchte erwähnen, dass ich den Vorteil der Verwendung von std :: string über const char [] für Konstanten nicht sehen. std :: string ist nett und alles, aber es erfordert dynamische Initialisierung. Also, wenn Sie schreiben, so etwas wie

const std::string foo = "hello"; 

im Namespace Umfang der Konstruktor von foo richtig ausgeführt werden soll, bevor die Ausführung des Haupt beginnt und dieser Konstruktor wird eine Kopie der Konstante „Hallo“ im Heap-Speicher erstellen. Wenn Sie RECTANGLE nicht wirklich als std :: string benötigen, können Sie auch

schreiben. Dort! Keine Heap-Zuweisung, kein Kopieren, keine dynamische Initialisierung.

Prost, s.

5

möglich, nur tun:

static const std::string RECTANGLE() const { 
    return "rectangle"; 
} 

oder

#define RECTANGLE "rectangle" 
+7

Die Verwendung von #define, wenn eine typisierte Konstante verwendet werden kann, ist einfach falsch. –

+0

Ihr erstes Beispiel ist grundsätzlich eine gute Lösung, wenn Sie nicht "constexpr" haben, aber Sie können keine statische Funktion "const" machen. –

+0

Diese Lösung sollte vermieden werden. Es erstellt bei jedem Aufruf eine neue Zeichenfolge. Das wäre besser: 'statisch const std :: string RECTANGLE() const {statisch const std :: string value (" rectangle "); Rückgabewert; } ' –

4

Sie entweder für die oben erwähnte const char* Lösung gehen kann, aber dann, wenn Sie Zeichenfolge die ganze Zeit brauchen, Sie gehen zu müssen, viel Aufwand.
Auf der anderen Seite, statische Zeichenfolge benötigt dynamische Initialisierung, also wenn Sie den Wert während einer anderen globalen/statischen Variable Initialisierung verwenden möchten, könnten Sie das Problem der Reihenfolge der Initialisierung treffen. Um dies zu vermeiden, greift das günstigste auf das statische String-Objekt durch einen Getter zu, der prüft, ob Ihr Objekt initialisiert ist oder nicht.

//in a header 
class A{ 
    static string s; 
public: 
    static string getS(); 
}; 
//in implementation 
string A::s; 
namespace{ 
    bool init_A_s(){ 
    A::s = string("foo"); 
    return true; 
    } 
    bool A_s_initialized = init_A_s(); 
} 
string A::getS(){  
    if (!A_s_initialized) 
    A_s_initialized = init_A_s(); 
    return s; 
} 

Denken Sie daran, nur A::getS() zu verwenden. Da alle Threads nur von main() gestartet werden können und A_s_initialized vor main() initialisiert wird, müssen Sie auch in einer Multithreadumgebung keine Sperren verwenden. A_s_initialized ist standardmäßig 0 (vor der dynamischen Initialisierung). Wenn Sie also getS() verwenden, bevor s initialisiert wird, rufen Sie die init-Funktion sicher auf.

Btw, in der Antwort oben: „static const std :: string VIERECKS() const“ können statische Funktionen nicht const sein, weil sie nicht den Zustand ändern kann, wenn jedes Objekt sowieso (es gibt keinen dieser Zeiger ist).

113

In C++ 11 können Sie jetzt tun:

class A { 
private: 
    static constexpr const char* STRING = "some useful string constant"; 
}; 
+0

Danke, das rettete den Tag. Ich brauchte ein Bitset der Größe eines statischen Arrays in meiner Klasse. Ohne constexpr (für das Array) nicht möglich, es sei denn, Sie möchten jedes Mal, wenn die Klasse erstellt wird, eine Heap-Zuweisung vornehmen. – rwst

+15

Leider funktioniert diese Lösung nicht für std :: string. – SmallChess

+0

Beachten Sie, dass 1. das nur mit Literalen funktioniert und 2. das nicht standardkonform ist, obwohl Gnu/GCC Feinheiten kompiliert, andere Compiler einen Fehler erzeugen. Definition muss im Körper sein. – ManuelSchneid3r

3

Die Klasse statische Variablen im Header deklariert sein kann, aber muss 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 welcher generierten Objektdatei er abgelegt werden soll. Stattdessen müssen Sie die Entscheidung treffen.

Um die Definition eines statischen Wertes mit der Deklaration in C++ 11 zu behalten, 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 sind in der Kopfzeile.

class A 
{ 
private: 
    static struct _Shapes { 
    const std::string RECTANGLE {"rectangle"}; 
    const std::string CIRCLE {"circle"}; 
    } shape; 
}; 

Statt einzelne Mitglieder die gesamte statische Struktur der Initialisierung in CPP initialisiert wird:

A::_Shapes A::shape; 

Die Werte werden zugegriffen mit

A::shape.RECTANGLE; 

oder - da die Mitglieder privat sind und sind nur für die Verwendung von A - mit

shape.RECTANGLE; 
bestimmt

Beachten Sie, dass diese Lösung immer noch das Problem in der Reihenfolge Initialisierung der statischen Variablen leidet. Wenn ein statischer Wert für verwendet wird, um eine andere statische Variable zu initialisieren, wird die erste möglicherweise noch nicht initialisiert, .

// file.h 
class File { 
public: 
    static struct _Extensions { 
    const std::string h{ ".h" }; 
    const std::string hpp{ ".hpp" }; 
    const std::string c{ ".c" }; 
    const std::string cpp{ ".cpp" }; 
    } extension; 
}; 

// file.cpp 
File::_Extensions File::extension; 

// module.cpp 
static std::set<std::string> headers{ File::extension.h, File::extension.hpp }; 

In diesem Fall werden die statischen Variablen headers enthält entweder { ""} oder { ".h", ".hpp"}, in Abhängigkeit von der Reihenfolge der Initialisierung durch den Linker erstellt.

Wie von @ abyss.7 erwähnt, können Sie auch constexpr verwenden, wenn der Wert der Variablen zur Kompilierzeit berechnet werden kann. Aber wenn Sie erklären, um Ihre Strings mit static constexpr const char* und Ihr Programm verwendet std::string sonst wird es ein Overhead, weil ein neues std::string Objekt wird jedes Mal, wenn Sie eine solche Konstante verwenden erstellt werden:

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. 
} 
1

In C++ 17 Sie Inline verwenden können Variablen:

class A { 
private: 
    static inline const std::string my_string = "some useful string constant"; 
}; 

Beachten Sie, dass diese von abyss.7's answer anders: Dieser definieren ein tatsächliches std::string Objekt, keine const char*