Frage Konstante C ++ - Zeichenfolge (Klassenmember)


Ich hätte gerne eine private statische Konstante für eine Klasse (in diesem Fall eine Formfabrik). Ich hätte gerne etwas ähnliches.

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 von   Mitglied 'RECHTECK'

Ungültige In-Class-Initialisierung des statischen Datenelements des nicht-integralen Typs 'std :: string'

Fehler: "RECHTECK" statisch machen

Dies sagt mir, dass diese Art von Elementdesign nicht dem Standard entspricht. Wie haben Sie eine private literale Konstante (oder vielleicht public) ohne eine # define-Direktive zu verwenden (Ich möchte die Hässlichkeit der Globalität von Daten vermeiden!)

Jede Hilfe wird geschätzt. Vielen Dank.


377
2017-10-14 02:00


Ursprung


Antworten:


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

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 in der Klassendefinition), ist nur mit Integral- und Enum-Typen zulässig.


401
2017-10-14 02:03



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

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

127
2018-06-21 11:27



Innerhalb von Klassendefinitionen können Sie nur erklären statische Mitglieder. Sie müssen definiert außerhalb der Klasse. 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 nicht den Vorteil der Verwendung von std :: string über const char [] sehen für Konstanten. std :: string ist nett und alles, aber es erfordert dynamische Initialisierung. Also, wenn du sowas schreibst

const std::string foo = "hello";

Im Namespacebereich wird der Konstruktor von foo direkt vor der Ausführung von main gestartet und dieser Konstruktor erstellt eine Kopie der Konstante "hallo" im Heapspeicher. Wenn Sie RECTANGLE nicht wirklich als std :: string brauchen, können 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-Zuweisung, kein Kopieren, keine dynamische Initialisierung.

Prost, s.


31
2017-10-14 08:30



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

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

        return str;
    }
};

Obwohl ich bezweifle, dass das empfohlen wird.


15
2017-10-14 02:08



Um diese In-Class-Initialisierung zu verwenden   Syntax muss die Konstante eine statische sein   const des Integral- oder Aufzählungstyps   initialisiert durch einen konstanten Ausdruck.

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


7
2017-10-14 02:07



möglich nur tun:

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

oder

#define RECTANGLE "rectangle"

5
2018-03-27 01:08



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 dies anders ist als abyss.7's Antwort: Dieser definiert eine tatsächliche std::string Objekt, nicht a const char*


5
2018-03-02 15:15



Der aktuelle Standard erlaubt nur eine solche Initialisierung für statische konstante Integraltypen. Also müssen Sie tun, wie AndreyT erklärte. Das wird aber im nächsten Standard über die Syntax für die Initialisierung neuer Mitglieder.


4
2017-10-14 02:11



Sie können entweder für die const char* Lösung, die oben erwähnt wird, aber dann, wenn Sie die ganze Zeit Zeichenfolge benötigen, werden Sie eine Menge Overhead haben.
Auf der anderen Seite benötigt die statische Zeichenfolge eine dynamische Initialisierung. Wenn Sie also ihren Wert während der Initialisierung einer anderen globalen / statischen Variablen verwenden möchten, treffen Sie möglicherweise das Problem der Initialisierungsreihenfolge. 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 zu verwenden A::getS(). Weil jedes Threading nur von gestartet werden kann main(), und A_s_initialized wird vorher initialisiert main(), Sie benötigen keine Sperren auch in einer Multithread-Umgebung. A_s_initialized ist standardmäßig 0 (vor der dynamischen Initialisierung), also wenn Sie verwenden getS() Bevor s initialisiert wird, rufen Sie die init-Funktion sicher auf.

Übrigens, in der obigen Antwort: "statisch const std :: string RECTANGLE () const", statische Funktionen können nicht sein const weil sie den Zustand sowieso nicht ändern können (es gibt keinen dieser Zeiger).


4
2017-09-23 11:13



Die statischen Variablen der Klasse können sein erklärt im Header 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 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 Eine verschachtelte statische Struktur kann verwendet werden. In diesem Fall das statische Element ist 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;
};

Anstatt die einzelnen Elemente zu initialisieren, wird die gesamte statische Struktur in .cpp initialisiert:

A::_Shapes A::shape;

Auf die Werte wird mit zugegriffen

A::shape.RECTANGLE;

oder - da die Mitglieder privat sind und nur von A - mit verwendet werden sollen

shape.RECTANGLE;

Beachten Sie, dass diese Lösung immer noch unter dem Problem der Ordnung leidet Initialisierung der statischen Variablen. Wenn ein statischer Wert verwendet wird eine andere statische Variable initialisieren, die erste möglicherweise nicht initialisiert wird, noch.

// 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 die statische Variable Kopfzeilen enthält entweder {""} oder {".h", ".hpp"}, abhängig von der vom Linker erzeugten Initialisierungsreihenfolge.

Wie von @ abyss.7 erwähnt, könnte man auch verwenden constexpr wenn der Wert der Variablen zur Kompilierzeit berechnet werden kann. Aber wenn Sie Ihre Zeichenfolgen mit angeben static constexpr const char* und dein Programm verwendet std::string sonst wird es einen Overhead geben, weil ein neuer 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.
}

4
2018-02-24 07:54