c++ meta - Comment déclarer un caractère * statique dans votre fichier d'en-tête?



description exemple (9)

L'erreur est que vous ne pouvez pas initialiser un static const char* dans la classe. Vous ne pouvez y initialiser que des variables entières.

Vous devez déclarer la variable membre dans la classe, puis l'initialiser en dehors de la classe:

// En tête de fichier

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

// fichier cpp

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

Si cela semble ennuyeux, considérez-le comme étant dû au fait que l'initialisation ne peut apparaître que dans une seule unité de traduction. Si c'était dans la définition de la classe, cela serait généralement inclus dans plusieurs fichiers. Les entiers constants constituent un cas particulier (ce qui signifie que le message d'erreur n'est peut-être pas aussi clair qu'il pourrait l'être), et les compilateurs peuvent effectivement remplacer les utilisations de la variable par la valeur entière.

En revanche, une variable char* pointe sur un objet réel en mémoire, qui est nécessaire pour exister réellement, et c'est la définition (y compris l'initialisation) qui rend l'objet existant. La "règle de définition unique" signifie que vous ne souhaitez donc pas la placer dans un en-tête, car toutes les unités de traduction incluant cet en-tête contiendraient alors la définition. Ils ne peuvent pas être liés ensemble, même si la chaîne contient les mêmes caractères dans les deux cas, car, dans les règles C ++ actuelles, vous avez défini deux objets différents portant le même nom, ce qui n'est pas légal. Le fait qu'ils aient les mêmes personnages ne le rend pas légal.

Je voudrais définir un caractère constant * dans mon fichier d'en-tête pour mon fichier .cpp à utiliser. Alors j'ai essayé ceci:

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

Ce qui m'amène avec l'erreur suivante du compilateur:

erreur C2864: 'SomeClass :: SOMETHING': seuls les membres de données intégrales const statique peuvent être initialisés dans une classe

Je suis nouveau en C ++. Qu'est-ce qui se passe ici? Pourquoi est-ce illégal? Et comment pouvez-vous le faire alternativement?


Initialisateur constant autorisé par la norme C ++ uniquement pour les types intégraux ou d'énumération. Voir 9.4.2 / 4 pour plus de détails:

Si un membre de données statique est de type énum intégral ou const constant, sa déclaration dans la définition de classe peut spécifier un initialisateur de constante qui doit être une expression constante intégrale (5.19). Dans ce cas, le membre peut apparaître dans des expressions constantes intégrales. Le membre doit toujours être défini dans une étendue nom-espace s'il est utilisé dans le programme et la définition de la portée d'espace-noms ne doit pas contenir d'initialiseur.

Et 9.4.2 / 7:

Les membres de données statiques sont initialisés et détruits exactement comme des objets non locaux (3.6.2, 3.6.3).

Donc, vous devriez écrire quelque part dans le fichier cpp:

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

Si vous utilisez Visual C ++, vous pouvez le faire de manière non portable en utilisant des astuces sur l'éditeur de liens ...

// 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) signifie que même si Foo::Bar sera déclaré dans plusieurs fichiers objet, l'éditeur de liens n'en prendra qu'un.

Gardez à l'esprit que cela ne fonctionnera qu'avec la chaîne d'outils Microsoft. Ne vous attendez pas à ce que cela soit portable.


Vous devez définir des variables statiques dans une unité de traduction, sauf si elles sont de type intégral.

Dans votre entête:

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

Dans le fichier .cpp:

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

C ++ standard, 9.4.2 / 4:

Si un membre de données statique est de type énum intégral ou const constant, sa déclaration dans la définition de classe peut spécifier un initialisateur de constante qui doit être une expression constante intégrale. Dans ce cas, le membre peut apparaître dans des expressions constantes intégrales dans sa portée. Le membre doit toujours être défini dans une portée d'espace de noms s'il est utilisé dans le programme et la définition de la portée d'espace de noms ne doit pas contenir d'initialiseur.


Pour répondre à la question « pourquoi» , les types intégraux ont ceci de particulier qu'ils ne font pas référence à un objet alloué, mais plutôt à des valeurs dupliquées et copiées. Il s’agit simplement d’une décision d’implémentation prise lors de la définition du langage, qui consistait à gérer les valeurs en dehors du système d’objets et de la manière la plus efficace et la plus intégrée possible.

Cela n'explique pas exactement pourquoi ils sont autorisés en tant qu'initialiseurs dans un type, mais considérez-le comme étant essentiellement une #define et cela aura alors un sens comme faisant partie du type et non de l'objet.


Avec C ++ 11, vous pouvez utiliser le mot clé constexpr et écrire dans votre en-tête:

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


Remarques:

  • constexpr fait de constexpr un pointeur constant pour vous constexpr écrire

    SOMETHING = "something different";

    plus tard.

  • Selon votre compilateur, vous aurez peut-être également besoin d'écrire une définition explicite dans le fichier .cpp:

    constexpr const char* MyClass::SOMETHING;

Il existe une astuce que vous pouvez utiliser avec des modèles pour fournir des constantes de fichier H uniquement.

(notez que ceci est un exemple laid, mais fonctionne textuellement au moins en g ++ 4.6.1.)

(fichier values.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);
}

Compilez (par exemple, g ++ -o main main.cpp other.cpp && ./main) et voyez deux unités de compilation référençant la même constante déclarée dans un en-tête:

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

En MSVC, vous pourrez peut-être utiliser __declspec (selectany)

Par exemple:

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

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

Je le fais tout le temps - en particulier pour les paramètres coûteux par défaut de const.

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

Const: Const n'est rien d'autre que "constant", une variable dont la valeur est constante mais au moment de la compilation. Et il est obligatoire de lui attribuer une valeur. Par défaut, un const est statique et nous ne pouvons pas changer la valeur d'une variable const tout au long du programme.

Static ReadOnly: une valeur de variable de type Static Readonly peut être affectée lors de l'exécution ou affectée lors de la compilation et modifiée lors de l'exécution. Mais la valeur de cette variable ne peut être modifiée que dans le constructeur statique. Et ne peut pas être changé plus loin. Il ne peut changer qu'une seule fois à l'exécution

Référence: c-sharpcorner





c++ constants