Domanda

Vorrei definire un carattere costante * nel mio file di intestazione da utilizzare per il mio file .cpp. Quindi ho provato questo:

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

Il che mi porta con il seguente errore del compilatore:

  

errore C2864: 'SomeClass :: SOMETHING':   solo dati integrali const statici   i membri possono essere inizializzati in a   Classe

Sono nuovo di C ++. Che cosa sta succedendo qui? Perché questo è illegale? E come puoi farlo in alternativa?

È stato utile?

Soluzione

Devi definire variabili statiche in un'unità di traduzione, a meno che non siano di tipo integrale.

Nella tua intestazione:

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

Nel file .cpp:

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

Standard C ++, 9.4.2 / 4:

  

Se un membro di dati statici è di const   tipo di enumerazione integrale o const,   la sua dichiarazione nella classe   la definizione può specificare a   inizializzatore costante che deve essere un   espressione costante integrale. In ciò   caso, il membro può apparire in   espressioni costanti integrali all'interno   la sua portata. Il membro deve essere ancora   definito in un ambito dello spazio dei nomi, se lo è   utilizzato nel programma e nello spazio dei nomi   la definizione dell'ambito di applicazione non deve contenere un   initializer.

Altri suggerimenti

Per rispondere alla domanda del PO sul perché è consentito solo con tipi integrali.

Quando un oggetto viene usato come un valore (cioè come qualcosa che ha un indirizzo in memoria), deve soddisfare la "regola di definizione" " (ODR), ovvero deve essere definito in un'unica e sola unità di traduzione. Il compilatore non può e non deciderà in quale unità di traduzione definire quell'oggetto. Questa è la tua responsabilità. definendo quell'oggetto da qualche parte non lo stai semplicemente definendo, stai in realtà dicendo al compilatore che vuoi definirlo qui , in questo specifico unità di traduzione.

Nel frattempo, nel linguaggio C ++ le costanti integrali hanno uno status speciale. Possono formare espressioni costanti integrali (ICE). Nelle ICE le costanti integrali sono utilizzate come valori ordinari, non come oggetti (ovvero non è rilevante se tale valore integrale abbia indirizzo nella memoria o meno). In effetti, gli ICE vengono valutati in fase di compilazione. Per facilitare tale uso di costanti integrali, i loro valori devono essere visibili a livello globale. E la costante stessa non ha davvero bisogno di un posto reale nella memoria. A causa di queste costanti integrali ha ricevuto un trattamento speciale: è stato permesso di includere i propri inizializzatori nel file di intestazione e il requisito di fornire una definizione è stato rilassato (prima di fatto, poi di diritto).

Altri tipi costanti non hanno tali proprietà. Altri tipi costanti sono praticamente sempre usati come valori (o almeno non possono partecipare a ICE o qualcosa di simile a ICE), nel senso che richiedono una definizione. Il resto segue.

L'errore è che non è possibile inizializzare un carattere const statico * all'interno della classe. Qui puoi inizializzare solo variabili intere.

Devi dichiarare la variabile membro nella classe, quindi inizializzarla al di fuori della classe:

// file header

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

// file cpp

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

Se questo sembra fastidioso, pensalo perché l'inizializzazione può apparire solo in un'unità di traduzione. Se fosse nella definizione della classe, di solito sarebbe incluso da più file. Gli interi costanti sono un caso speciale (il che significa che il messaggio di errore forse non è così chiaro come potrebbe essere), e i compilatori possono effettivamente sostituire gli usi della variabile con il valore intero.

Al contrario, una variabile char * punta a un oggetto reale in memoria, che deve esistere realmente, ed è la definizione (inclusa l'inizializzazione) che fa esistere l'oggetto. La "regola di una definizione" significa quindi che non vuoi metterlo in un'intestazione, perché tutte le unità di traduzione inclusa quell'intestazione conterrebbero la definizione. Non potevano essere collegati tra loro, anche se la stringa contiene gli stessi caratteri in entrambi, perché in base alle attuali regole C ++ hai definito due oggetti diversi con lo stesso nome, e questo non è legale. Il fatto che abbiano gli stessi personaggi non è legale.

Con C ++ 11 puoi usare la parola chiave constexpr e scrivere nella tua intestazione:

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


Note:

  • constexpr rende QUALCOSA un puntatore costante in modo da non poter scrivere

    SOMETHING = "something different";
    

    più tardi.

  • A seconda del compilatore, potresti anche dover scrivere una definizione esplicita nel file .cpp:

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

Lo faccio sempre, specialmente per costosi parametri di default const.

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

Se stai usando Visual C ++, puoi farlo in modo non portabile usando i suggerimenti per il linker ...

// 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) significa che anche se Foo :: Bar verrà dichiarato in più file oggetto, il linker ne prenderà solo uno.

Tieni presente che funzionerà solo con la toolchain di Microsoft. Non aspettarti che sia portatile.

C'è un trucco che puoi usare con i template per fornire solo costanti al file H.

(nota, questo è un brutto esempio, ma funziona alla lettera almeno in g ++ 4.6.1.)

(file valori.hpp)

#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);
}

Compila (es. g ++ -o main main.cpp other.cpp & amp; & amp; ./main) e vedi due unità di compilazione che fanno riferimento alla stessa costante dichiarata in un header:

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

In MSVC, potresti invece essere in grado di utilizzare __declspec (selectany)

Ad esempio:

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

Inizializzatore costante consentito dallo standard C ++ solo per tipi integrali o di enumerazione. Vedi 9.4.2 / 4 per dettagli:

  

Se un membro di dati statici è di tipo const integrale o di enumerazione const, la relativa dichiarazione nella classe   la definizione può specificare un inizializzatore di costante che deve essere un'espressione di costante integrale (5.19). In ciò   caso, il membro può apparire in espressioni costanti integrali. Il membro deve essere ancora definito in un nome-   spazio ambito se utilizzato nel programma e la definizione dell'ambito dello spazio dei nomi non deve contenere un inizializzatore.

E 9.4.2 / 7:

  

I membri statici dei dati vengono inizializzati e distrutti esattamente come oggetti non locali (3.6.2, 3.6.3).

Quindi dovresti scrivere da qualche parte nel file cpp:

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

Per rispondere alla domanda perché , i tipi integrali sono speciali in quanto non sono un riferimento a un oggetto allocato ma piuttosto valori che sono duplicati e copiati. È solo una decisione di implementazione presa quando il linguaggio è stato definito, che è stato quello di gestire i valori al di fuori del sistema di oggetti e in modo efficiente e "inline" una moda il più possibile.

Questo non spiega esattamente perché sono consentiti come inizializzatori in un tipo, ma pensalo essenzialmente come un #define e quindi avrà senso come parte del tipo e non parte di l'oggetto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top