2009-10-28 11 views
47

Ich möchte eine Konstante Char * in meiner Header-Datei für meine .cpp-Datei zu verwenden. Also habe ich versucht, diesen:Wie deklariere ich ein statisches const char * in Ihrer Header-Datei?

private: 
    static const char *SOMETHING = "sommething"; 

Was mich mit dem folgenden Compiler-Fehler bringt:

error C2864: 'SomeClass::SOMETHING' : only static const integral data members can be initialized within a class

Ich bin neu in C++. Was geht hier vor sich? Warum ist das illegal? Und wie kannst du es alternativ tun?

+1

Sie sollten "static const char * const ETWAS" stattdessen verwenden, es sei denn, Sie wirklich Ich möchte SOMETHING neu zuordnen können, um zur Laufzeit auf etwas anderes zu zeigen. – bk1e

Antwort

57

Sie müssen statische Variablen in einer Übersetzungseinheit definieren, es sei denn, sie integralen Typen sind.

In der Kopfzeile:

private: 
    static const char *SOMETHING; 
    static const int MyInt = 8; // would be ok 

In der CPP-Datei:

const char *YourClass::SOMETHING = "something"; 

C++ Standard 9.4.2/4:

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression. In that case, the member can appear in integral constant expressions within its scope. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.

+0

so ist es mir unmöglich, dies in der Header-Datei zuzuweisen? Ich kann dies mit anderen Datentypen wie DWORD tun. Warum ist das? – Mark

+3

Weil ein Zeiger kein integraler Typ ist. Integral umfasst nur die eingebauten Komponenten. –

+0

Warum ist das der Fall? Welche Einschränkung hat diese Einschränkung in C++ begrenzt? – Mark

15

Der Fehler ist, dass man nicht initialisiere einen static const char* innerhalb der Klasse. Sie können dort nur ganzzahlige Variablen initialisieren.

Sie müssen die Membervariable in der Klasse deklarieren, und es dann außerhalb der Klasse initialisieren:

// Header-Datei

class Foo { 
    static const char *SOMETHING; 
    // rest of class 
}; 

// CPP-Datei

const char *Foo::SOMETHING = "sommething"; 

Wenn Das scheint ärgerlich, weil es so aussieht, als könnte die Initialisierung nur in einer Übersetzungseinheit erscheinen. Wenn dies in der Klassendefinition der Fall wäre, würde dies normalerweise in mehreren Dateien enthalten sein. Konstante Ganzzahlen sind ein Sonderfall (was bedeutet, dass die Fehlermeldung möglicherweise nicht so klar ist, wie es sein könnte), und Compiler können die Verwendung der Variablen durch den Ganzzahlwert effektiv ersetzen.

Im Gegensatz dazu zeigt eine char* Variable auf ein tatsächliches Objekt im Speicher, das wirklich existieren muss, und es ist die Definition (einschließlich Initialisierung), die das Objekt existiert. Die "eine Definitionsregel" bedeutet, dass Sie sie daher nicht in eine Kopfzeile einfügen möchten, da dann alle Übersetzungseinheiten einschließlich dieser Kopfzeile die Definition enthalten würden. Sie konnten nicht miteinander verknüpft werden, obwohl die Zeichenfolge in beiden die gleichen Zeichen enthält, da Sie unter den aktuellen C++ - Regeln zwei verschiedene Objekte mit demselben Namen definiert haben, was nicht zulässig ist. Die Tatsache, dass sie die gleichen Charaktere in sich tragen, macht es nicht legal.

+0

Sie benötigen kein weiteres "statisch" in der cpp-Datei. – VictorCreator

1

Constant initializer erlaubt durch C++ Standard-nur für Integral- oder Aufzählungstypen. Siehe 9.4.2/4 für Details:

If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a name- space scope if it is used in the program and the namespace scope definition shall not contain an initializer.

Und 9.4.2/7:

Static data members are initialized and destroyed exactly like non-local objects (3.6.2, 3.6.3).

So sollten Sie schreiben irgendwo in CPP-Datei:

const char* SomeClass::SOMETHING = "sommething"; 
3

Wenn Sie Mit Visual C++ können Sie das nicht portabel machen, indem Sie Hinweise zum Linker verwenden ...

// In foo.h... 

class Foo 
{ 
public: 
    static const char *Bar; 
}; 

// Still in foo.h; doesn't need to be in a .cpp file... 

__declspec(selectany) 
const char *Foo::Bar = "Blah"; 

__declspec(selectany) bedeutet, dass, obwohl Foo::Bar wird in mehreren Objektdateien erklärt bekommen, wird der Linker nur einen abholen.

Beachten Sie, dass dies nur mit der Microsoft Toolchain funktioniert. Erwarten Sie nicht, dass dies tragbar ist.

3

Es gibt einen Trick, den Sie mit Vorlagen verwenden können, um nur H-Datei-Konstanten bereitzustellen.

(beachten Sie, ist dies ein hässliches Beispiel, funktioniert aber wörtlich in zumindest in g ++ 4.6.1.)

(values.hpp Datei)

#include <string> 

template<int dummy> 
class tValues 
{ 
public: 
    static const char* myValue; 
}; 

template <int dummy> const char* tValues<dummy>::myValue = "This is a value"; 

typedef tValues<0> Values; 

std::string otherCompUnit(); // test from other compilation unit 

(main.cpp)

#include <iostream> 
#include "values.hpp" 

int main() 
{ 
    std::cout << "from main: " << Values::myValue << std::endl; 
    std::cout << "from other: " << otherCompUnit() << std::endl; 
} 

(other.cpp)

#include "values.hpp" 

std::string otherCompUnit() { 
    return std::string(Values::myValue); 
} 

Kompilieren (z. g ++ -o Haupt main.cpp other.cpp & & ./main) und zwei Kompilierungseinheiten sich auf die gleiche Konstante in einem Header deklariert sehen:

from main: This is a value 
from other: This is a value 

In MSVC, können Sie stattdessen der Lage sein, zu verwenden, __declspec (selectany)

Zum Beispiel:

__declspec(selectany) const char* data = "My data"; 
+0

Der Vorlagen-Trick ist falsch. Es ist nicht erlaubt, mehrere Definitionen einer expliziten Spezialisierung anzugeben. Dies könnte funktionieren, wenn der Compiler es nicht abfangen kann, aber das ist trotzdem eine Verletzung von ODR. – AnT

+0

Tatsächlich würde die korrekte Implementierung des Vorlagentricks eine nicht spezialisierte Deklaration verwenden. – AnT

+1

Nur ein weiterer Fall, in dem der MS-Compiler nicht allen Standards folgt? Ich muss zugeben, dass ich es noch nie in GCC probiert habe. – Torlack

24

der OP Frage zu beantworten, warum es nur mit ganzzahligen Typen erlaubt.

Wenn ein Objekt als lvalue verwendet wird (dh als ein Objekt, das eine Adresse im Speicher hat), muss es die "one definition rule" (ODR) erfüllen, dh es muss in genau einer Übersetzungseinheit definiert werden . Der Compiler kann und wird nicht entscheiden, in welcher Übersetzungseinheit dieses Objekt definiert wird. Dies liegt in Ihrer Verantwortung. Durch definierenden dieses Objekt irgendwo Sie nicht nur definieren, sagen Sie tatsächlich den Compiler, dass Sie es hier definieren möchten, in diese spezifische Übersetzungseinheit.

In C++ haben Sprachintegralkonstanten einen speziellen Status. Sie können Integral Constant Expressions (ICEs) bilden. In ICEs werden Integralkonstanten als gewöhnliche Werte, nicht als Objekte verwendet (d. H. Es ist nicht relevant, ob ein solcher Integralwert eine Adresse im Speicher hat oder nicht). Tatsächlich werden ICEs zur Kompilierzeit ausgewertet. Um eine solche Verwendung von Integralkonstanten zu ermöglichen, müssen ihre Werte global sichtbar sein. Und die Konstante selbst braucht keinen wirklichen Platz im Speicher. Aufgrund dieser integralen Konstanten erhielt besondere Behandlung: Es wurde erlaubt, ihre Initialisierer in die Header-Datei aufzunehmen, und die Anforderung, eine Definition zu liefern, wurde gelockert (zuerst de facto, dann de jure).

Andere konstante Typen haben keine solchen Eigenschaften. Andere konstante Typen werden virtuell immer als lvalues ​​verwendet (oder können zumindest nicht an ICEs oder ähnlichen ICEs teilnehmen), was bedeutet, dass sie eine Definition erfordern. Der Rest folgt.

+0

außer Vorlagen –

+1

@Mooing Ente: Der vollständige Text von ODR ist ziemlich umfangreich. Es hat viele "Ausnahmen" und "Nicht-Ausnahmen", die nicht einmal im Entferntesten auf "Vorlagen" beschränkt sind. Meine Antwort beschränkt sich auf einen kleinen relevanten Teil von ODR, der sich mit Integralkonstanten beschäftigt. Ich sehe nicht, wie Ihre Bemerkung "außer Vorlagen" hier relevant sein kann (ich verstehe nicht einmal, was genau Sie mit dieser Bemerkung meinen.) – AnT

+1

Das ist alles wahr, und ich gab ein +1, weil das eine großartige Antwort ist, aber Template-Klassen sind (meines Wissens) Ausnahmen von allen ODR-Regeln. Ich dachte nur, ich würde das da hineinwerfen. –

0

Um die Frage warum zu beantworten, sind ganzzahlige Typen insofern speziell, als sie keine Referenz auf ein zugeordnetes Objekt sind, sondern Werte, die dupliziert und kopiert werden. Es ist nur eine Implementierungsentscheidung, die getroffen wurde, als die Sprache definiert wurde, nämlich mit Werten außerhalb des Objektsystems und so effizient und "inline" wie möglich umzugehen.

Dies erklärt nicht genau, warum sie als Initializers in einem Typ zulässig sind, aber denken Sie an es als im Wesentlichen ein #define und dann wird es Sinn als Teil des Typs und nicht Teil des Objekts.

9
class A{ 
public: 
    static const char* SOMETHING() { return "something"; } 
}; 

Ich mache es die ganze Zeit - vor allem für teure Konst-Standard-Parameter.

class A{ 
    static 
    const expensive_to_construct& 
    default_expensive_to_construct(){ 
     static const expensive_to_construct xp2c(whatever is needed); 
     return xp2c; 
    } 
}; 
6

Mit 11 C++ können Sie das constexpr Schlüsselwort verwenden und in der Kopfzeile schreiben:

private: 
    static constexpr const char* SOMETHING = "something"; 


Hinweise:

  • constexpr macht SOMETHING ein konstanter Zeiger Sie können also nicht

    schreiben
    SOMETHING = "something different"; 
    

    später.

  • Abhängig von Ihrem Compiler, müssen Sie möglicherweise auch eine explizite Definition in der CPP-Datei schreiben:

    constexpr const char* MyClass::SOMETHING;