Frage

Ich möchte in meiner Header-Datei eine Konstante char* definieren, die meine CPP-Datei verwenden soll.Also ich habe Folgendes versucht:

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

Was mir den folgenden Compilerfehler beschert:

Fehler C2864:'SomeClass::SOMETHING':Nur statische konstantintegrale Datenmitglieder können in einer Klasse initialisiert werden

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

War es hilfreich?

Lösung

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:

  

Wenn ein statisches Datenelement ist von konst   Integral- oder const Aufzählungstyp,   seine Erklärung in der Klasse   Definition kann ein angeben   Konstant initializer die eine sein soll   Integralkonstante Ausdruck. dass   Fall kann das Element erscheint in   integrale Konstante Ausdrücke innerhalb   ihr Anwendungsbereich. Das Mitglied wird noch sein   in einem Namespace Bereich definiert, wenn es   im Programm und der Namespace   Anwendungsbereich Definition enthält nicht ein   initializer.

Andere Tipps

Um die Frage des OP zu beantworten, warum dies nur mit ganzzahligen Typen zulässig ist.

Wenn ein Objekt als L-Wert verwendet wird (d. h.als etwas, das eine Adresse im Speicher hat), muss es die „One Definition Rule“ (ODR) erfüllen, d. h. es muss in einer und nur einer Übersetzungseinheit definiert sein.Der Compiler kann und wird nicht entscheiden, in welcher Übersetzungseinheit dieses Objekt definiert werden soll.Dies liegt in Ihrer Verantwortung.Von definieren Wenn Sie dieses Objekt irgendwo platzieren, definieren Sie es nicht nur, Sie teilen dem Compiler tatsächlich mit, dass Sie es definieren möchten Hier, In Das spezifische Übersetzungseinheit.

Mittlerweile haben Integralkonstanten in der C++-Sprache einen Sonderstatus.Sie können integrale konstante Ausdrücke (ICEs) bilden.In ICEs werden wie üblich Integralkonstanten verwendet Werte, nicht so wie Objekte (d. h.Dabei spielt es keine Rolle, ob ein solcher Integralwert im Speicher gespeichert ist oder nicht.Tatsächlich werden ICEs zur Kompilierungszeit ausgewertet.Um eine solche Verwendung von Integralkonstanten zu ermöglichen, müssen ihre Werte global sichtbar sein.Und die Konstante selbst benötigt nicht wirklich einen tatsächlichen Platz im Speicher.Aus diesem Grund wurden Integralkonstanten besonders behandelt:Es war erlaubt, ihre Initialisierer in die Header-Datei aufzunehmen, und die Anforderung, eine Definition bereitzustellen, wurde gelockert (zuerst de facto, dann de jure).

Andere Konstantentypen haben keine solchen Eigenschaften.Andere Konstantentypen werden praktisch immer als L-Werte verwendet (oder können zumindest nicht an ICEs oder ICE-ähnlichen Elementen teilnehmen), was bedeutet, dass sie einer Definition bedürfen.Der Rest folgt.

Der Fehler ist, dass Sie keine static const char* innerhalb der Klasse initialisieren. Sie können nur Integer-Variablen gibt initialisieren.

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

// Header-Datei

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

// CPP-Datei

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

Wenn dies ärgerlich scheint, daran zu denken als zu sein, weil die Initialisierung nur in einer Übersetzungseinheit angezeigt werden kann. Wenn es in der Klassendefinition ist, wäre das in der Regel durch mehrere Dateien enthalten sein. Konstante Zahlen sind ein Sonderfall (was bedeutet, dass die Fehlermeldung vielleicht nicht so klar ist, wie es sein könnte), und Compiler können effektiv Verwendungen der Variable mit dem Integer-Wert ersetzen.

Im Gegensatz dazu ein char* Variable auf ein tatsächliches Objekt im Speicher, die erforderlich ist, um wirklich existiert, und es ist die Definition (einschließlich der Initialisierung), die das Objekt macht existiert. Die „eine Definitionsregel“ bedeutet, dass Sie deshalb wollen sie nicht in einem Header setzen, weil dann alle Übersetzungseinheiten, die Header einschließlich der Definition enthalten würde. Sie können nicht miteinander verbunden werden, auch wenn die Zeichenfolge die gleichen Zeichen in beiden enthalten, weil unter dem aktuellen C ++ Regeln Sie zwei verschiedene Objekte mit demselben Namen definiert haben, und das ist nicht legal. Die Tatsache, dass sie die gleichen Zeichen haben, passieren in ihnen nicht legal macht.

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

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


Hinweis:

  • constexpr macht SOMETHING ein konstanter Zeiger, so dass Sie nicht schreiben kann

    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;
    
class A{
public:
   static const char* SOMETHING() { return "something"; }
};

Ich mache es die ganze Zeit - vor allem für teuren const Standardparameter

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

Wenn Sie Visual C ++ verwenden, können Sie nicht portabel machen diese Hinweise an den Linker mit ...

// 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 eine abholen.

Beachten Sie, dies nur mit dem Microsoft-Toolchain arbeiten. Erwarten Sie nicht, dies tragbar sein.

Es gibt einen Trick, den Sie mit Vorlagen verwenden, können H-Datei nur Konstanten zur Verfügung zu stellen.

(beachten Sie, dies ist 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);
}

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

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

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

Zum Beispiel:

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

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

  

Wenn ein statisches Datenelement von const integral oder const Aufzählungstyp ist, ihre Erklärung in der Klasse   Definition kann einen Konstant Initialisierer spezifiziert, die einen integrierenden konstanten Ausdruck sein (5.19). dass   Fall kann das Element in integraler konstanter Ausdrücke erscheinen. Das Mitglied wird nach wie vor in einem Namen- definiert werden   space-Bereich, wenn es in dem Programm und die Namespace Umfang Definition verwendet wird, nicht einen Initialisierer enthalten.

Und 9.4.2 / 7:

  

Statische Datenelemente initialisiert und zerstören genau wie nicht-lokale Objekte (3.6.2, 3.6.3).

So sollten Sie irgendwo in CPP-Datei schreiben:

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

die zu beantworten, warum Frage, sind integrale Typen speziell, da sie nicht ein Verweis auf ein Objekt zugewiesen sind, sondern Werte, die dupliziert werden und kopiert. Es ist nur eine Implementierung Entscheidung getroffen, wenn die Sprache definiert wurde, die Werte außerhalb des Objektsystems handhaben war und in so effizient und „inline“ eine Art und Weise, wie möglich.

Dies ist nicht genau zu erklären, warum sie als initializors in einer Art erlaubt sind, aber im wesentlichen einen #define daran denkt und dann wird es Sinn als Teil der Art und nicht Teil des Objekts machen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top