Comment initialiser les membres statiques privés EN C++?

Quelle est la meilleure façon d'initialiser un membre de données statique privé en C++? J'ai essayé cela dans mon fichier d'en-tête, mais cela me donne des erreurs de linker bizarres:

class foo
{
    private:
        static int i;
};

int foo::i = 0;

je suppose que c'est parce que je ne peux pas initialiser un membre privé de l'extérieur de la classe. Quelle est donc la meilleure façon de le faire?

412

17 réponses

la déclaration de classe doit être dans le fichier d'en-tête (ou dans le fichier source s'il n'est pas partagé).

Fichier: foo.h

class foo
{
    private:
        static int i;
};

Mais l'initialisation doit être dans le fichier source.

Fichier: foo.cpp

int foo::i = 0;

Si l'initialisation est dans le fichier d'en-tête, puis chaque fichier qui contient le fichier d'en-tête aura une définition du membre statique. Ainsi pendant la phase de lien vous obtiendrez linker les erreurs comme le code pour initialiser la variable seront définies dans les fichiers source multiples.

Note: Matt Curtis: souligne que C++ permet la simplification de ce qui précède si la variable membre statique est de type constant (par ex. int , bool , char ). Vous pouvez alors déclarer et initialiser la variable member directement à l'intérieur de la déclaration de classe dans le fichier d'en-tête:

class foo
{
    private:
        static int const i = 42;
};
456
répondu Martin York 2015-07-19 16:00:27

pour une variable :

foo.h:

class foo
{
private:
    static int i;
};

foo.cpp:

int foo::i = 0;

C'est parce qu'il ne peut être une instance de foo::i dans votre programme. C'est un peu l'équivalent de extern int i dans un fichier d'en-tête et int i dans un fichier source.

Pour un constante vous pouvez mettre la valeur directement dans la déclaration de classe:

class foo
{
private:
    static int i;
    const static int a = 42;
};
76
répondu Matt Curtis 2008-10-09 03:41:40

pour les futurs téléspectateurs de cette question, je tiens à souligner que vous devriez éviter ce que monkey0506 suggère .

Les fichiers D'en-tête

sont pour les déclarations.

Les fichiers D'en-tête

sont compilés une fois pour chaque fichier .cpp qui les #includes directement ou indirectement, et le code en dehors de toute fonction est lancé à l'initialisation du programme, avant main() .

en mettant: foo::i = VALUE; dans l'en-tête, foo:i se verra attribuer la valeur VALUE (quelle qu'elle soit) pour chaque fichier .cpp , et ces assignations se feront dans un ordre indéterminé (déterminé par le linker) avant que main() ne soit lancé.

et si #define VALUE était un autre numéro dans l'un de nos fichiers .cpp ? Il compilera très bien et nous n'aurons aucun moyen de savoir qui gagne jusqu'à ce que nous exécutions le programme.

jamais mis exécuté code dans un en-tête pour la même raison que vous jamais #include un .cpp fichier.

inclure les gardes (qui je suis d'accord que vous devriez toujours utiliser) de vous protéger de quelque chose de différent: le même en-tête étant indirectement #include d plusieurs fois lors de la compilation d'un seul .cpp fichier

25
répondu Joshua Clayton 2017-05-23 11:47:29

avec un compilateur Microsoft[1], Les variables statiques qui ne sont pas int - comme peuvent également être définies dans un fichier d'en-tête, mais en dehors de la déclaration de classe, en utilisant le Microsoft spécifique __declspec(selectany) .

class A
{
    static B b;
}

__declspec(selectany) A::b;

notez que je ne dis pas que c'est bon, je dis juste que c'est faisable.

[1] de nos jours, plus de compilateurs que MSC prennent en charge __declspec(selectany) - au moins gcc et clang. Peut-être même plus.

19
répondu Johann Gerell 2015-03-24 08:59:51
int foo::i = 0; 

est la syntaxe correcte pour initialiser la variable, mais elle doit aller dans le fichier source (.rpc) plutôt que dans l'en-tête.

Parce que c'est une variable statique le compilateur a besoin de créer qu'une seule copie. Vous devez avoir une ligne "int foo:j'ai" quelque part dans votre code pour indiquer au compilateur où le mettre sinon vous aurez une erreur de lien. Si c'est dans un en-tête, vous obtiendrez une copie de chaque fichier qui contient l'en-tête, afin de vous multipliez défini les erreurs de symboles du linker.

16
répondu David Dibben 2008-10-09 03:42:18

Je n'ai pas assez de rapport ici pour ajouter ceci comme commentaire, mais IMO c'est du bon style d'écrire vos en-têtes avec #include guards de toute façon, ce qui comme noté par Paranaix il y a quelques heures empêcherait une erreur de définition multiple. À moins que vous n'utilisiez déjà un fichier distinct pour le RPC, il n'est pas nécessaire d'en utiliser un pour initialiser les participants non intégraux.

#ifndef FOO_H
#define FOO_H
#include "bar.h"

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

Je ne vois pas la nécessité d'utiliser un fichier distinct pour le RPC. Bien sûr, vous pouvez, mais il n'y a pas de raison technique pour laquelle vous devriez avoir.

10
répondu monkey0506 2012-01-07 19:42:54

si vous voulez initialiser un type de composé (F. E. string) vous pouvez faire quelque chose comme ça:

class SomeClass {
  static std::list<string> _list;

  public:
    static const std::list<string>& getList() {
      struct Initializer {
         Initializer() {
           // Here you may want to put mutex
           _list.push_back("FIRST");
           _list.push_back("SECOND");
           ....
         }
      }
      static Initializer ListInitializationGuard;
      return _list;
    }
};

comme le ListInitializationGuard est une variable statique à l'intérieur de SomeClass::getList() méthode, il sera construit qu'une seule fois, ce qui signifie que le constructeur est appelé une fois. Cela va initialize _list variable à la valeur que vous avez besoin. Tout appel ultérieur à getList retournera simplement l'objet _list déjà initialisé.

bien sûr, vous devez accédez à _list object toujours en appelant la méthode getList() .

10
répondu Henry Dorsett Case 2015-10-06 21:09:18

depuis C++17, les membres statiques peuvent être définis dans l'en-tête avec le mot-clé inline .

http://en.cppreference.com/w/cpp/language/static

" un élément de données statique peut être déclaré en ligne. Un membre de données statiques en ligne peut être défini dans la définition de classe et peut spécifier un initialiseur de membre par défaut. Il n'a pas besoin d'une définition de classe:"

struct X
{
    inline static int n = 1;
};
10
répondu Die in Sente 2017-09-08 01:29:56

vous pouvez également inclure l'affectation dans le fichier d'en-tête si vous utilisez des en-têtes de garde. J'ai utilisé cette technique pour une bibliothèque C++ que j'ai créé. Une autre façon d'obtenir le même résultat consiste à utiliser des méthodes statiques. Exemple...

class Foo
   {
   public:
     int GetMyStatic() const
     {
       return *MyStatic();
     }

   private:
     static int* MyStatic()
     {
       static int mStatic = 0;
       return &mStatic;
     }
   }

le code ci-dessus a le" bonus " de ne pas exiger un fichier RPC/source. Encore une fois, une méthode que j'utilise pour mes bibliothèques C++.

5
répondu 2013-04-11 21:50:25

Je suis L'idée de Karl. Je l'aime et maintenant je l'utilise ainsi. J'ai un peu modifié la notation et ajouté quelques fonctionnalités

#include <stdio.h>

class Foo
{
   public:

     int   GetMyStaticValue () const {  return MyStatic();  }
     int & GetMyStaticVar ()         {  return MyStatic();  }
     static bool isMyStatic (int & num) {  return & num == & MyStatic(); }

   private:

      static int & MyStatic ()
      {
         static int mStatic = 7;
         return mStatic;
      }
};

int main (int, char **)
{
   Foo obj;

   printf ("mystatic value %d\n", obj.GetMyStaticValue());
   obj.GetMyStaticVar () = 3;
   printf ("mystatic value %d\n", obj.GetMyStaticValue());

   int valMyS = obj.GetMyStaticVar ();
   int & iPtr1 = obj.GetMyStaticVar ();
   int & iPtr2 = valMyS;

   printf ("is my static %d %d\n", Foo::isMyStatic(iPtr1), Foo::isMyStatic(iPtr2));
}

ce sorties

mystatic value 7
mystatic value 3
is my static 1 0
5
répondu Alejadro Xalabarder 2013-12-31 14:45:25

travaille aussi dans privateStatic.fichier cpp :

#include <iostream>

using namespace std;

class A
{
private:
  static int v;
};

int A::v = 10; // possible initializing

int main()
{
A a;
//cout << A::v << endl; // no access because of private scope
return 0;
}

// g++ privateStatic.cpp -o privateStatic && ./privateStatic
3
répondu andrew 2014-01-26 11:21:05

Qu'en est-il de la méthode set_default() ?

class foo
{
    public:
        static void set_default(int);
    private:
        static int i;
};

void foo::set_default(int x) {
    i = x;
}

nous n'aurions qu'à utiliser la méthode set_default(int x) et notre variable static serait initialisée.

ce ne serait pas en désaccord avec le reste des commentaires, en fait, il suit le même principe d'initialisation de la variable dans une portée globale, mais en utilisant cette méthode, nous le rendons explicite (et facile à voir-comprendre) au lieu d'avoir la définition de la variable accroché là.

3
répondu Arturo Ruiz Mañas 2015-10-06 20:12:33

modèle de constructeur statique qui fonctionne pour des objets multiples

un idiome a été proposé à: https://stackoverflow.com/a/27088552/895245 Mais voici une version plus propre qui ne nécessite pas de créer une nouvelle méthode par membre, et un exemple exécutable:

#include <cassert>
#include <vector>

// Normally on the .hpp file.
class MyClass {
public:
    static std::vector<int> v, v2;
    static struct _StaticConstructor {
        _StaticConstructor() {
            v.push_back(1);
            v.push_back(2);
            v2.push_back(3);
            v2.push_back(4);
        }
    } _staticConstructor;
};

// Normally on the .cpp file.
std::vector<int> MyClass::v;
std::vector<int> MyClass::v2;
// Must come after every static member.
MyClass::_StaticConstructor MyClass::_staticConstructor;

int main() {
    assert(MyClass::v[0] == 1);
    assert(MyClass::v[1] == 2);
    assert(MyClass::v2[0] == 3);
    assert(MyClass::v2[1] == 4);
}

Voir aussi: constructeurs statiques en C++? J'ai besoin d'initialiser privé objets statiques

Testé avec g++ -std=c++11 -Wall -Wextra , GCC 7.2, Ubuntu 17.10.

3

je voulais juste mentionner quelque chose d'un peu étrange pour moi quand je l'ai rencontré pour la première fois.

j'avais besoin d'initialiser un privé donnée membre statique dans une classe de modèle.

dans le .h ou .hpp, il ressemble à quelque chose comme ceci pour initialiser un membre de données statiques d'une classe de modèle:

template<typename T>
Type ClassName<T>::dataMemberName = initialValue;
1
répondu Tyler Heers 2016-10-01 01:27:02

le problème de connexion que vous avez rencontré est probablement causé par:

  • Offrant à la fois classe et membre statique définition dans le fichier d'en-tête,
  • incluant cet en-tête dans deux fichiers source ou plus.

C'est un problème courant pour ceux qui commencent avec C++. Le membre de classe statique doit être initialisé dans une seule unité de traduction, c'est-à-dire dans un fichier source unique.

malheureusement, la statique des membres du groupe doit être initialisé à l'extérieur du corps de la classe. Cela complique l'écriture du code d'en-tête seulement, et, par conséquent, j'utilise une approche tout à fait différente. Vous pouvez fournir votre objet statique par la fonction de classe statique ou non-statique par exemple:

class Foo
{
    // int& getObjectInstance() const {
    static int& getObjectInstance() {
        static int object;
        return object;
    }

    void func() {
        int &object = getValueInstance();
        object += 5;
    }
};
1
répondu no one special 2017-09-10 10:20:32

est-ce que cela sert votre but?

//header file

struct MyStruct {
public:
    const std::unordered_map<std::string, uint32_t> str_to_int{
        { "a", 1 },
        { "b", 2 },
        ...
        { "z", 26 }
    };
    const std::unordered_map<int , std::string> int_to_str{
        { 1, "a" },
        { 2, "b" },
        ...
        { 26, "z" }
    };
    std::string some_string = "justanotherstring";  
    uint32_t some_int = 42;

    static MyStruct & Singleton() {
        static MyStruct instance;
        return instance;
    }
private:
    MyStruct() {};
};

//Usage in cpp file
int main(){
    std::cout<<MyStruct::Singleton().some_string<<std::endl;
    std::cout<<MyStruct::Singleton().some_int<<std::endl;
    return 0;
}
0
répondu David Nogueira 2016-09-02 11:50:33

une façon" old-school "de définir les constantes est de les remplacer par un enum :

class foo
{
    private:
        enum {i = 0}; // default type = int
        enum: int64_t {HUGE = 1000000000000}; // may specify another type
};

de cette façon ne nécessite pas de fournir une définition, et évite de faire la constante lvalue , ce qui peut vous épargner quelques maux de tête, par exemple lorsque vous accidentellement ODR-utilisation it.

0
répondu anatolyg 2018-06-13 21:27:06