Statische Variablen in der C ++ - Klasse initialisieren?

78

Ich habe festgestellt, dass einige meiner Funktionen in einer Klasse tatsächlich nicht auf das Objekt zugreifen, also habe ich sie erstellt static. Dann sagte mir der Compiler, dass alle Variablen, auf die sie zugreifen, auch statisch sein müssen - na ja, bisher ziemlich verständlich. Ich habe eine Reihe von String-Variablen wie

string RE_ANY = "([^\\n]*)";
string RE_ANY_RELUCTANT = "([^\\n]*?)";

und so weiter in der Klasse. Ich habe sie dann alle gemacht, static constweil sie sich nie ändern. Mein Programm wird jedoch nur kompiliert, wenn ich sie aus der Klasse verschiebe: Andernfalls beschwert sich MSVC ++ 2010 über "Innerhalb einer Klasse dürfen nur statische konstante Integralvariablen initialisiert werden".

Nun, das ist unglücklich. Gibt es eine Problemumgehung? Ich möchte sie in der Klasse lassen, zu der sie gehören.

Felix Dombek
quelle

Antworten:

127

Sie können nicht innerhalb der Klasse initialisiert werden, aber sie können außerhalb der Klasse in einer Quelldatei initialisiert werden:

// inside the class
class Thing {
    static string RE_ANY;
    static string RE_ANY_RELUCTANT;
};

// in the source file
string Thing::RE_ANY = "([^\\n]*)";
string Thing::RE_ANY_RELUCTANT = "([^\\n]*?)";

Aktualisieren

Ich habe gerade die erste Zeile Ihrer Frage bemerkt - Sie möchten diese Funktionen nicht erstellen static, Sie möchten sie erstellen const. Wenn Sie sie staticerstellen, sind sie keinem Objekt mehr zugeordnet (sodass sie nicht auf nicht statische Elemente zugreifen können). Wenn Sie die Daten statisch machen, werden sie für alle Objekte dieses Typs freigegeben. Dies ist möglicherweise nicht das, was Sie wollen. Sie zu consterstellen bedeutet einfach, dass sie keine Mitglieder ändern können, aber dennoch auf sie zugreifen können.

Mike Seymour
quelle
1
Sie greifen auf nichts im Objekt zu, sondern nur auf temporäre Variablen, die ihnen als Referenzargumente gegeben werden. Daher sollten sie wahrscheinlich beide constund sein static.
Felix Dombek
8
@Felix: es ist nicht möglich, constbedeutet , dass es nicht ändern this... und es gibt keine thisfür staticMethoden.
Matthieu M.
2
@ androidplusios.design: Ich folge nicht. Sie benötigen, stringweil es sich um eine Variablendefinition handelt, für die ein Typspezifizierer erforderlich ist, und der Typ ist string. Sie können ()einen Deklarator umsetzen, wenn Sie möchten, es ändert nichts an der Bedeutung.
Mike Seymour
4
@ androidplusios.design: Das sieht es. Sie müssen jedoch die Definitionssyntax verwenden, um sie zu definieren und zu initialisieren. Dazu gehört auch ein Typbezeichner, unabhängig davon, ob er zuvor deklariert wurde oder nicht. So wird die Sprache angegeben.
Mike Seymour
4
@ androidplusios.design: Oder wenn Sie sich fragen, warum die Syntax mit all ihren Inkonsistenzen, Redundanzen und Mehrdeutigkeiten so ist, wie sie ist: weil sie sich über viele Jahrzehnte aus viel einfacheren Sprachen entwickelt hat.
Mike Seymour
32

Mike Seymour hat Ihnen die richtige Antwort gegeben, aber um hinzuzufügen ... Mit
C ++ können Sie in Ihrem Klassenkörper nur statische const-Integraltypen deklarieren und definieren , wie der Compiler sagt. Sie können also tatsächlich Folgendes tun:

class Foo
{
    static const int someInt = 1;
    static const short someShort = 2;
    // etc.
};

Und das können Sie mit keinem anderen Typ tun. In diesem Fall sollten Sie sie in Ihrer CPP-Datei definieren.

Santiago V.
quelle
16

Statische Mitgliedsvariablen müssen in der Klasse deklariert und dann außerhalb definiert werden!

Es gibt keine Problemumgehung. Fügen Sie einfach die eigentliche Definition in eine Quelldatei ein.


Aus Ihrer Beschreibung geht hervor, dass Sie statische Variablen nicht richtig verwenden. Wenn sie sich nie ändern, sollten Sie stattdessen eine konstante Variable verwenden, aber Ihre Beschreibung ist zu allgemein, um etwas mehr zu sagen.

Statische Elementvariablen haben für jede Instanz Ihrer Klasse immer den gleichen Wert: Wenn Sie eine statische Variable eines Objekts ändern, ändert sich dies auch für alle anderen Objekte (und Sie können auch ohne Instanz der Klasse darauf zugreifen - dh: ein Objekt).

Peoro
quelle
3
Sie sind jetzt const - sie müssen nur auch statisch sein, damit ich sie in statischen Elementfunktionen verwenden kann. Was ist der Grund für diese Regel, dass sie innerhalb einer Klasse deklariert und definiert werden müssen? Das macht für mich nicht viel Sinn.
Felix Dombek
2
@ Felix Dombek: Ich denke, der Grund dafür ist, dass die Klasse für jede Quelldatei, die Sie kompilieren und verknüpfen, deklariert ist (/ sein könnte), aber die tatsächlichen Variablen müssen nur einmal definiert werden. Dies ist der gleiche Grund, warum Sie explizit externdie in anderen Quelldateien definierten Variablen deklarieren müssen .
Peoro
1
@peoro: Das scheint vernünftig! Aber warum sind dann integrale Datentypen zulässig? Das sollte auch nicht erlaubt sein ...
Felix Dombek
1
@ Felix Dombek: Es ist auch nicht für integrale Typen erlaubt. Wenn Sie diese Struktur definieren: struct A { static int x; };und versuchen, A::xohne Definition int A::x;in einer Quelldatei darauf zuzugreifen , wird Ihr Code nicht verknüpft.
Peoro
2
@ Felix Dombek: Es ist keine Standardbeschwerde. ISO C ++ verbietet die Initialisierung von nicht konstanten statischen Elementen in der Klasse. Sie können dies nur für statische Integral-Const-Elemente tun, da statische Const-Integralvariablen nicht tatsächlich im Speicher abgelegt werden, sondern zur Kompilierungszeit als Konstante verwendet werden.
Peoro
16

Seit C ++ 11 kann es innerhalb einer Klasse mit durchgeführt werden constexpr.

class stat {
    public:
        // init inside class
        static constexpr double inlineStaticVar = 22;
};

Auf die Variable kann jetzt zugegriffen werden mit:

stat::inlineStaticVar
0ax1
quelle
1
Auf jeden Fall gut für Ints, Double, Zeiger usw. Dies funktioniert jedoch nicht mit der Zeichenfolge aus der Frage, da die Zeichenfolge kein Literal oder eine Referenz ist.
Roi Danton
1
Guter Punkt. Aber du könntest constexpr char RE_ANY[] = "([^\\n]*)";oder constexpr std::string_view RE_ANY("([^\\n]*)", 9);.
0ax1
9

Ich halte es für erwähnenswert, dass eine statische Variable nicht mit einer konstanten Variablen identisch ist.

Verwenden einer konstanten Variablen in einer Klasse

struct Foo{
    const int a;
    Foo(int b) : a(b){}
}

und wir würden es so erklären

fooA = new Foo(5);
fooB = new Foo(10);
// fooA.a = 5;
// fooB.a = 10;

Für eine statische Variable

struct Bar{
    static int a;
    Foo(int b){
        a = b;
    }
}
Bar::a = 0; // set value for a

welches so benutzt wird

barA = new Bar(5);
barB = new Bar(10);
// barA.a = 10;
// barB.a = 10;
// Bar::a = 10;

Sie sehen, was hier passiert. Die konstante Variable, die zusammen mit jeder Instanz von Foo instanziiert wird, da Foo instanziiert wird, hat für jede Instanz von Foo einen eigenen Wert und kann von Foo überhaupt nicht geändert werden.

Wo wie bei Bar ist dies nur ein Wert für Bar :: a, egal wie viele Instanzen von Bar erstellt werden. Sie alle teilen diesen Wert. Sie können auch darauf zugreifen, wenn sie Instanzen von Bar sind. Die statische Variable hält sich auch an Regeln für öffentlich / privat, sodass Sie festlegen können, dass nur Instanzen von Bar den Wert von Bar :: a lesen können.

thecoshman
quelle
1
huh ... finde es lustig, niemand hat auf meine schreckliche unnötige Verwendung von "neu" hingewiesen. Diese Frage ist nicht wirklich von Belang, aber ja, vermeiden Sie die Verwendung von "neu", wenn Sie dies nicht benötigen, und Sie tun dies mehr oder weniger nie.
Thecoshman
Bitte entfernen Sie Ihre schreckliche, unnötige Verwendung von new. ;-)
Roi Danton
1
Ich denke, es ist jetzt ein historisches Beispiel dafür, wie schlecht alter Code war. An diesem Punkt denke ich, dass es ein schlechter Dienst wäre, es zu entfernen.
Thecoshman
8

Nur um die anderen Antworten zu ergänzen. Um ein komplexes statisches Element zu initialisieren , können Sie dies wie folgt tun:

Deklarieren Sie Ihr statisches Mitglied wie gewohnt.

// myClass.h
class myClass
{
static complexClass s_complex;
//...
};

Erstellen Sie eine kleine Funktion, um Ihre Klasse zu initialisieren, wenn dies nicht trivial ist. Dies wird nur einmal aufgerufen, wenn das statische Element initialisiert wird. (Beachten Sie, dass der Kopierkonstruktor von complexClass verwendet wird, daher sollte er gut definiert sein.)

//class.cpp    
#include myClass.h
complexClass initFunction()
{
    complexClass c;
    c.add(...);
    c.compute(...);
    c.sort(...);
    // Etc.
    return c;
}

complexClass myClass::s_complex = initFunction();
Sergio Basurco
quelle
5

Einige Antworten scheinen etwas irreführend zu sein .

Du musst nicht ...

  • Weisen Sie einem statischen Objekt beim Initialisieren einen Wert zu, da das Zuweisen eines Werts optional ist .
  • Erstellen Sie eine weitere .cppDatei zum Initialisieren, da dies in derselben Header- Datei erfolgen kann.

Sie können sogar ein statisches Objekt im selben Klassenbereich wie eine normale Variable mit dem inlineSchlüsselwort initialisieren .


Initialisieren Sie ohne Werte in derselben Datei

#include <string>
class A
{
    static std::string str;
    static int x;
};
std::string A::str;
int A::x;

Initialisieren Sie mit Werten in derselben Datei

#include <string>
class A
{
    static std::string str;
    static int x;
};
std::string A::str = "SO!";
int A::x = 900;

Initialisieren Sie im selben Klassenbereich mit dem inlineSchlüsselwort

#include <string>
class A
{
    static inline std::string str = "SO!";
    static inline int x = 900;
};
Beyondo
quelle
2
Beste Antwort von allen. Ich habe hinzugefügt inlineund alle Verknüpfungsfehler sind verschwunden.
Contango
'Inline-Variablen sind eine c ++ 17-Erweiterung'. Das ist die Nachricht, die ich bekomme
Ssenyonjo
@Ssenyonjo Sie müssen den C ++ - Sprachstandard auf C ++ 17 oder höher setzen. Gehen Sie in Visual Studio zu Projekt -> Eigenschaften -> C / C ++ -> Sprache -> C ++ - Sprachstandard -> ISO C ++ 17 oder ISO C ++ Neueste. Und in GCC, verwenden Sie die Flagge -std=c++17oder -std=c++20usw.
Beyondo
2

Wenn Sie die statische Variable in Ihrer Header-Datei initialisieren möchten (anstelle einer * .cpp-Datei, die Sie möglicherweise benötigen, wenn Sie sich an eine "nur Header" -Idiom halten), können Sie das Initialisierungsproblem mithilfe von a umgehen Vorlage. Statische Variablen mit Vorlagen können in einem Header initialisiert werden, ohne dass mehrere Symbole definiert werden müssen.

Ein Beispiel finden Sie hier:

Statische Elementinitialisierung in einer Klassenvorlage

Mark Lakata
quelle
1

Verschieben Sie optional alle Ihre Konstanten in die CPP-Datei ohne Deklaration in der H-Datei. Verwenden Sie einen anonymen Namespace, um sie über das cpp-Modul hinaus unsichtbar zu machen.

// MyClass.cpp

#include "MyClass.h"

// anonymous namespace
namespace
{
    string RE_ANY = "([^\\n]*)";
    string RE_ANY_RELUCTANT = "([^\\n]*?)";
}

// member function (static or not)
bool MyClass::foo()
{
    // logic that uses constants
    return RE_ANY_RELUCTANT.size() > 0;
}
RuslanK
quelle