constructeurs statiques en C ++? J'ai besoin d'initialiser des objets statiques privés

176

Je veux avoir une classe avec un membre de données statique privé (un vecteur qui contient tous les caractères az). En java ou C #, je peux simplement créer un "constructeur statique" qui fonctionnera avant de créer des instances de la classe, et configure les données statiques membres de la classe. Il n'est exécuté qu'une seule fois (car les variables sont en lecture seule et ne doivent être définies qu'une seule fois) et comme il s'agit d'une fonction de la classe, il peut accéder à ses membres privés. Je pourrais ajouter du code dans le constructeur qui vérifie si le vecteur est initialisé, et l'initialiser si ce n'est pas le cas, mais cela introduit de nombreuses vérifications nécessaires et ne semble pas être la solution optimale au problème.

L'idée me vient à l'esprit que puisque les variables seront en lecture seule, elles peuvent simplement être des const statiques publiques, donc je peux les définir une fois en dehors de la classe, mais encore une fois, cela ressemble à un horrible hack.

Est-il possible d'avoir des membres de données statiques privés dans une classe si je ne veux pas les initialiser dans le constructeur d'instance?

Gordon Gustafson
la source
3
Double possible de Comment initialiser les membres statiques privés en C ++?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功 Cette question se concentre sur l'exécution de code pour initialiser des objets statiques privés , et non sur la définition de valeurs constantes de types primitifs statiques privés. Les solutions sont différentes.
Gordon Gustafson
ah, je pense que vous avez raison, vous rétractant.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

180

Pour obtenir l'équivalent d'un constructeur statique, vous devez écrire une classe ordinaire distincte pour contenir les données statiques, puis créer une instance statique de cette classe ordinaire.

class StaticStuff
{
     std::vector<char> letters_;

public:
     StaticStuff()
     {
         for (char c = 'a'; c <= 'z'; c++)
             letters_.push_back(c);
     }

     // provide some way to get at letters_
};

class Elsewhere
{
    static StaticStuff staticStuff; // constructor runs once, single instance

};
Daniel Earwicker
la source
12
Merci! bien que ce soit très ennuyeux de devoir faire tout cela. L'une des nombreuses "erreurs" dont C # et java ont appris.
Gordon Gustafson
109
Oui. Je fais toujours remarquer aux gens que si le C ++ n'avait pas commis toutes ces "erreurs", d'autres langages devraient les faire. C ++ couvrant tellement de terrain, même en faisant des erreurs, a été formidable pour les langages qui l'ont suivi.
quark
11
Juste une petite nuance, car les constructeurs entrent en jeu, personne ne garantit quand le constructeur de l'objet statique s'exécute. Une approche bien connue et beaucoup plus sûre est la classe Elsewhere {StaticStuff & get_staticStuff () {static StaticStuff staticStuff; // le constructeur s'exécute une fois, quand quelqu'un en a besoin pour la première fois return staticStuff; }}; Je me demande si les constructeurs statiques en C # et Java peuvent fournir la même garantie que le code ci-dessus ...
Oleg Zhylin
13
@Oleg: Oui, ils le font. La norme garantit que les constructeurs de toutes les variables non locales sont exécutés avant la saisie de main. Il garantit également qu'au sein d'une unité de compilation, l'ordre de construction est bien défini et le même ordre que la déclaration au sein de l'unité de compilation. Malheureusement, ils ne définissent pas l'ordre entre plusieurs unités de compilation.
Martin York
13
C'est en fait un cas où friendcela a beaucoup de sens pour que la classe Elsewherepuisse facilement accéder StaticStuffaux composants internes de (sans interrompre l'encapsulation de manière dangereuse, pourrais-je ajouter).
Konrad Rudolph le
81

Eh bien, vous pouvez avoir

class MyClass
{
    public:
        static vector<char> a;

        static class _init
        {
          public:
            _init() { for(char i='a'; i<='z'; i++) a.push_back(i); }
        } _initializer;
};

N'oubliez pas (dans le .cpp) ceci:

vector<char> MyClass::a;
MyClass::_init MyClass::_initializer;

Le programme sera toujours lié sans la deuxième ligne, mais l'initialiseur ne sera pas exécuté.

EFraim
la source
+1 (je n'ai pas essayé) Mais: Quand est appelé ctor _init._init ()? Avant ou après le cteur de MyClass lorsque j'ai un objet MyClass statique? Je suppose que vous ne pouvez pas dire ...
ur.
2
bonjour, où puis-je en savoir plus sur cette magie "d'initialisation"?
Karel Bílek
Ne devrait-il pas être à la MyClass::a.push_back(i)place de a.push_back(i)?
Neel Basu
4
@ur .: _initializerest un sous-objet de MyClass. Les sous-objets sont initialisés dans cet ordre: sous-objets de classe de base virtuelle, dans l'ordre de la profondeur d'abord, de gauche à droite (mais en initialisant chaque sous-objet distinct une seule fois); puis des sous-objets de classe de base simples, dans l'ordre de la profondeur d'abord, de gauche à droite; puis les sous-objets membres dans l'ordre de déclaration. Il est donc sûr d'utiliser la stratégie d'EFraim, à condition que le code _initialiserne fasse référence qu'aux membres déclarés avant lui.
j_random_hacker
2
Cette réponse est meilleure que celle acceptée car l'auteur a mentionné l'indispensable initialisation sur le deuxième clip de code.
Jeff T.
33

Solution C ++ 11

Depuis C ++ 11, vous pouvez simplement utiliser des expressions lambda pour initialiser les membres de classe statiques. Cela fonctionne même si vous devez imposer un ordre de construction entre les différents membres statiques, ou si vous avez des membres statiques qui le sont const.

En tête de fichier:

class MyClass {
    static const vector<char> letters;
    static const size_t letterCount;
};

Fichier source:

// Initialize MyClass::letters by using a lambda expression.
const vector<char> MyClass::letters = [] {
    vector<char> letters;
    for (char c = 'a'; c <= 'z'; c++)
        letters.push_back(c);
    return letters;
}();

// The initialization order of static members is defined by the order of
// definition within the source file, so we can access MyClass::letters here.
const size_t MyClass::letterCount = letters.size();
emkey08
la source
solution intéressante. dans ce cas, si je lance une exception, qui peut l'attraper?
rafi wiener
5
Le code d'initialisation du programme statique ne doit jamais lever d'exceptions, sinon le programme plantera. Vous devez encapsuler la logique d'initialisation dans un try catchbloc si des exceptions peuvent être levées.
emkey08
19

Dans le fichier .h:

class MyClass {
private:
    static int myValue;
};

Dans le fichier .cpp:

#include "myclass.h"

int MyClass::myValue = 0;
Fourmi
la source
5
Cela fonctionne bien pour les membres statiques individuels (quel que soit leur type). Le défaut par rapport aux constructeurs statiques est que vous ne pouvez pas imposer un ordre entre les différents membres statiques. Si vous devez faire cela, consultez la réponse d'Earwicker.
quark
Je fais exactement cela, mais il ne compile toujours pas. Et il dit que c'est la zone à problème (dans le constructeur, pas l'en-tête)
Flotolk
14

Voici une autre approche similaire à celle de Daniel Earwicker, utilisant également la suggestion de classe d'amis de Konrad Rudolph. Ici, nous utilisons une classe utilitaire d'ami privé interne pour initialiser les membres statiques de votre classe principale. Par exemple:

En tête de fichier:

class ToBeInitialized
{
    // Inner friend utility class to initialize whatever you need

    class Initializer
    {
    public:
        Initializer();
    };

    friend class Initializer;

    // Static member variables of ToBeInitialized class

    static const int numberOfFloats;
    static float *theFloats;

    // Static instance of Initializer
    //   When this is created, its constructor initializes
    //   the ToBeInitialized class' static variables

    static Initializer initializer;
};

Dossier d'implémentation:

// Normal static scalar initializer
const int ToBeInitialized::numberOfFloats = 17;

// Constructor of Initializer class.
//    Here is where you can initialize any static members
//    of the enclosing ToBeInitialized class since this inner
//    class is a friend of it.

ToBeInitialized::Initializer::Initializer()
{
    ToBeInitialized::theFloats =
        (float *)malloc(ToBeInitialized::numberOfFloats * sizeof(float));

    for (int i = 0; i < ToBeInitialized::numberOfFloats; ++i)
        ToBeInitialized::theFloats[i] = calculateSomeFancyValue(i);
}

Cette approche a l'avantage de masquer complètement la classe Initializer du monde extérieur, en conservant tout ce qui est contenu dans la classe à initialiser.

Douglas Mandell
la source
+1 Pour donner un exemple qui conserve l'implémentation dans son propre fichier.
Andrew Larsson
1
De plus, vous devez vous assurer que cela ToBeInitialized::Initializer::Initializer()est appelé, vous devez donc l'ajouter ToBeInitialized::Initializer ToBeInitialized::initializer;au fichier d'implémentation. J'ai pris certaines choses de votre idée et de l'idée d'EFraim, et cela fonctionne exactement comme j'en ai besoin et a l'air propre. Merci mec.
Andrew Larsson
11

Test::StaticTest() est appelée exactement une fois lors de l'initialisation statique globale.

L'appelant n'a qu'à ajouter une ligne à la fonction qui doit être son constructeur statique.

static_constructor<&Test::StaticTest>::c;force l'initialisation de clors de l'initialisation statique globale.

template<void(*ctor)()>
struct static_constructor
{
    struct constructor { constructor() { ctor(); } };
    static constructor c;
};

template<void(*ctor)()>
typename static_constructor<ctor>::constructor static_constructor<ctor>::c;

/////////////////////////////

struct Test
{
    static int number;

    static void StaticTest()
    {
        static_constructor<&Test::StaticTest>::c;

        number = 123;
        cout << "static ctor" << endl;
    }
};

int Test::number;

int main(int argc, char *argv[])
{
    cout << Test::number << endl;
    return 0;
}
CurieuxGeorge
la source
C'est une solution fantastique. J'aime beaucoup la réponse de Douglas Mandel aussi, mais c'est encore plus concis.
FlintZA
C'est vraiment incroyable!
nh_
9

Pas besoin de init()fonction, std::vectorpeut être créée à partir d'une plage:

// h file:
class MyClass {
    static std::vector<char> alphabet;
// ...
};

// cpp file:
#include <boost/range.hpp>
static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
std::vector<char> MyClass::alphabet( boost::begin( ::alphabet ), boost::end( ::alphabet ) );

Notez cependant que les statiques de type classe causent des problèmes dans les bibliothèques, elles doivent donc être évitées à cet endroit.

Mise à jour C ++ 11

À partir de C ++ 11, vous pouvez le faire à la place:

// cpp file:
std::vector<char> MyClass::alphabet = { 'a', 'b', 'c', ..., 'z' };

C'est sémantiquement équivalent à la solution C ++ 98 dans la réponse d'origine, mais vous ne pouvez pas utiliser une chaîne littérale sur le côté droit, donc ce n'est pas complètement supérieur. Toutefois, si vous avez un vecteur de tout autre type que char, wchar_t, char16_tou char32_t(tableaux qui peuvent être écrits comme des chaînes), le C ++ version 11 sera strictement supprimer le code boilerplate sans introduire la syntaxe du langage, par rapport au C ++ 98 version.

Marc Mutz - mmutz
la source
Je l'aime. Mais si seulement nous pouvions le faire en une seule ligne sans l'alphabet désormais inutile.
Martin York
Pour causer des problèmes avec les bibliothèques, est-il important que la classe statique soit privée ou publique? De plus, est-ce important que la bibliothèque soit statique (.a) ou dynamique (.so)?
Zachary Kraus
@ZacharyKraus: qu'est-ce qu'une classe publique / privée ? Et non, bien que les problèmes soient différents, mais se chevauchent, peu importe que la bibliothèque soit liée de manière statique ou dynamique.
Marc Mutz - mmutz
@ MarcMutz-mmutz Désolé d'utiliser une classe publique / privée qui n'est pas une terminologie C ++ correcte. Ce à quoi je faisais référence est la solution d'EFraim ci-dessus. Dans ma version cependant, j'ai rendu le membre de classe statique privé. J'essayais de comprendre si le fait d'avoir un membre de classe statique comme public ou privé fait une différence dans le développement et la convivialité de la bibliothèque. Mon instinct me dit que cela ne devrait pas affecter la bibliothèque parce que les utilisateurs n'auront jamais accès ni au membre de la classe statique ni à l'objet qu'il construit, mais j'aimerais avoir la sagesse d'un gourou sur ce sujet.
Zachary Kraus
@ZacharyKraus: Le principal problème avec les statiques qui nécessitent une initialisation dynamique ([basic.start.init] / 2) est qu'elles exécutent du code. Dans les bibliothèques, il se peut que le code de la bibliothèque ait déjà été déchargé lorsque les destructeurs sont exécutés. Si vous voulez en savoir plus, je vous suggère de poster une question à ce sujet.
Marc Mutz - mmutz
6

Le concept de constructeurs statiques a été introduit en Java après avoir appris des problèmes en C ++. Nous n'avons donc pas d'équivalent direct.

La meilleure solution est d'utiliser des types POD qui peuvent être initialisés explicitement.
Ou faites de vos membres statiques un type spécifique qui a son propre constructeur qui l'initialisera correctement.

//header

class A
{
    // Make sure this is private so that nobody can missues the fact that
    // you are overriding std::vector. Just doing it here as a quicky example
    // don't take it as a recomendation for deriving from vector.
    class MyInitedVar: public std::vector<char>
    {
        public:
        MyInitedVar()
        {
           // Pre-Initialize the vector.
           for(char c = 'a';c <= 'z';++c)
           {
               push_back(c);
           }
        }
    };
    static int          count;
    static MyInitedVar  var1;

};


//source
int            A::count = 0;
A::MyInitedVar A::var1;
Martin York
la source
4

En essayant de compiler et d' utiliser la classe Elsewhere(d' après la réponse d' Earwicker ), j'obtiens:

error LNK2001: unresolved external symbol "private: static class StaticStuff Elsewhere::staticStuff" (?staticStuff@Elsewhere@@0VStaticStuff@@A)

Il ne semble pas possible d'initialiser des attributs statiques de types non entiers sans mettre du code en dehors de la définition de classe (CPP).

Pour faire cette compilation, vous pouvez utiliser " une méthode statique avec une variable locale statique à l'intérieur " à la place. Quelque chose comme ça:

class Elsewhere
{
public:
    static StaticStuff& GetStaticStuff()
    {
        static StaticStuff staticStuff; // constructor runs once, single instance
        return staticStuff;
    }
};

Et vous pouvez également passer des arguments au constructeur ou l'initialiser avec des valeurs spécifiques, c'est très flexible, puissant et facile à implémenter ... la seule chose est que vous avez une méthode statique contenant une variable statique, pas un attribut statique ... la syntaxe change un peu, mais reste utile. J'espère que c'est utile pour quelqu'un,

Hugo González Castro.

Communauté
la source
Cependant, soyez prudent si vous utilisez des threads. Je crois qu'en GCC, la construction de locals statiques est protégée contre l'exécution simultanée, mais en Visual C ++, ce n'est pas le cas.
Daniel Earwicker
1
À partir de C ++ 11 et dans POSIX, il doit être thread-safe.
Marc Mutz - mmutz
J'ai bien aimé deux autres solutions ci-dessus ( ceci et cela ), mais la vôtre est la seule qui assure l'initialisation de la statique dans l'ordre dans lequel elles sont nécessaires dans les bibliothèques. J'ai juste une méthode d'instance statique privée comme la vôtre ci-dessus, et j'enveloppe l'accès à d'autres valeurs dans des accesseurs statiques publics qui utilisent cette méthode d'instance au lieu de références directes. Merci.
FlintZA
Impressionnant! Ceci le complète.
Gabe Halsmer
4

Je suppose qu'une solution simple à cela sera:

    //X.h
    #pragma once
    class X
    {
    public:
            X(void);
            ~X(void);
    private:
            static bool IsInit;
            static bool Init();
    };

    //X.cpp
    #include "X.h"
    #include <iostream>

    X::X(void)
    {
    }


    X::~X(void)
    {
    }

    bool X::IsInit(Init());
    bool X::Init()
    {
            std::cout<< "ddddd";
            return true;
    }

    // main.cpp
    #include "X.h"
    int main ()
    {
            return 0;
    }
Shubham
la source
C'est ainsi que je le fais aussi.
Etherealone
1

Juste résolu le même truc. J'ai dû spécifier la définition d'un seul membre statique pour Singleton. Mais compliquez les choses - j'ai décidé que je ne voulais pas appeler ctor de RandClass () à moins que je ne l'utilise ... c'est pourquoi je ne voulais pas initialiser singleton globalement dans mon code. J'ai également ajouté une interface simple dans mon cas.

Voici le code final:

J'ai simplifié le code et j'utilise la fonction rand () et son initialzer srand ()

interface IRandClass
{
 public:
    virtual int GetRandom() = 0;
};

class RandClassSingleton
{
private:
  class RandClass : public IRandClass
  {
    public:
      RandClass()
      {
        srand(GetTickCount());
      };

     virtual int GetRandom(){return rand();};
  };

  RandClassSingleton(){};
  RandClassSingleton(const RandClassSingleton&);

  // static RandClass m_Instance;

  // If you declare m_Instance here you need to place
  // definition for this static object somewhere in your cpp code as
  // RandClassSingleton::RandClass RandClassSingleton::m_Instance;

  public:

  static RandClass& GetInstance()
  {
      // Much better to instantiate m_Instance here (inside of static function).
      // Instantiated only if this function is called.

      static RandClass m_Instance;
      return m_Instance;
  };
};

main()
{
    // Late binding. Calling RandClass ctor only now
    IRandClass *p = &RandClassSingleton::GetInstance();
    int randValue = p->GetRandom();
}
abc()
{
    IRandClass *same_p = &RandClassSingleton::GetInstance();
}
adspx5
la source
1

Voici ma variante de la solution d'EFraim; la différence est que, grâce à l'instanciation implicite du modèle, le constructeur statique n'est appelé que si des instances de la classe sont créées, et qu'aucune définition dans le.cpp fichier n'est nécessaire (grâce à la magie d'instanciation de template).

Dans le .hfichier, vous avez:

template <typename Aux> class _MyClass
{
    public:
        static vector<char> a;
        _MyClass() {
            (void) _initializer; //Reference the static member to ensure that it is instantiated and its initializer is called.
        }
    private:
        static struct _init
        {
            _init() { for(char i='a'; i<='z'; i++) a.push_back(i); }
        } _initializer;

};
typedef _MyClass<void> MyClass;

template <typename Aux> vector<char> _MyClass<Aux>::a;
template <typename Aux> typename _MyClass<Aux>::_init _MyClass<Aux>::_initializer;

Dans le .cppfichier, vous pouvez avoir:

void foobar() {
    MyClass foo; // [1]

    for (vector<char>::iterator it = MyClass::a.begin(); it < MyClass::a.end(); ++it) {
        cout << *it;
    }
    cout << endl;
}

Notez qu'il MyClass::an'est initialisé que si la ligne [1] est là, car cela appelle (et nécessite l'instanciation de) le constructeur, qui nécessite alors l'instanciation de _initializer.

Blaisorblade
la source
1

Voici une autre méthode, où le vecteur est privé pour le fichier qui contient l'implémentation en utilisant un espace de noms anonyme. C'est utile pour des choses comme les tables de recherche qui sont privées pour l'implémentation:

#include <iostream>
#include <vector>
using namespace std;

namespace {
  vector<int> vec;

  struct I { I() {
    vec.push_back(1);
    vec.push_back(3);
    vec.push_back(5);
  }} i;
}

int main() {

  vector<int>::const_iterator end = vec.end();
  for (vector<int>::const_iterator i = vec.begin();
       i != end; ++i) {
    cout << *i << endl;
  }

  return 0;
}
Jim Hunziker
la source
Bien que vous souhaitiez peut-être nommer Iet iquelque chose d'un peu plus obscur pour ne pas les utiliser accidentellement quelque part plus bas dans le fichier.
Jim Hunziker
1
Pour être honnête, il est difficile de voir pourquoi quiconque voudrait utiliser des membres statiques privés plutôt que des espaces de noms uniques dans les fichiers d'implémentation.
Jim Hunziker
1

Cela n'a certainement pas besoin d'être aussi compliqué que la réponse actuellement acceptée (par Daniel Earwicker). La classe est superflue. Il n'y a pas besoin d'une guerre linguistique dans ce cas.

Fichier .hpp:

vector<char> const & letters();

Fichier .cpp:

vector<char> const & letters()
{
  static vector<char> v = {'a', 'b', 'c', ...};
  return v;
}
AndyJost
la source
0

Vous définissez les variables de membre statiques de la même manière que vous définissez les méthodes de membre.

foo.h

class Foo
{
public:
    void bar();
private:
    static int count;
};

toto.cpp

#include "foo.h"

void Foo::bar()
{
    // method definition
}

int Foo::count = 0;
Nick Lewis
la source
2
La question CrazyJugglerDrummer ne portait pas sur un ancien type de données statique ordinaire :)
jww
0

Pour initialiser une variable statique, il suffit de le faire à l'intérieur d'un fichier source. Par exemple:

//Foo.h
class Foo
{
 private:
  static int hello;
};


//Foo.cpp
int Foo::hello = 1;
Cristián Romo
la source
La question CrazyJugglerDrummer ne portait pas sur un ancien type de données statique ordinaire :)
jww
0

Que diriez-vous de créer un modèle pour imiter le comportement de C #.

template<class T> class StaticConstructor
{
    bool m_StaticsInitialised = false;

public:
    typedef void (*StaticCallback)(void);

    StaticConstructor(StaticCallback callback)
    {
        if (m_StaticsInitialised)
            return;

        callback();

        m_StaticsInitialised = true;
    }
}

template<class T> bool StaticConstructor<T>::m_StaticsInitialised;

class Test : public StaticConstructor<Test>
{
    static std::vector<char> letters_;

    static void _Test()
    {
        for (char c = 'a'; c <= 'z'; c++)
            letters_.push_back(c);
    }

public:
    Test() : StaticConstructor<Test>(&_Test)
    {
        // non static stuff
    };
};
karmasponge
la source
0

Pour des cas simples comme ici, une variable statique enveloppée dans une fonction membre statique est presque aussi bonne. C'est simple et sera généralement optimisé par les compilateurs. Cela ne résout cependant pas le problème d'ordre d'initialisation des objets complexes.

#include <iostream>

class MyClass 
{

    static const char * const letters(void){
        static const char * const var = "abcdefghijklmnopqrstuvwxyz";
        return var;
    }

    public:
        void show(){
            std::cout << letters() << "\n";
        }
};


int main(){
    MyClass c;
    c.show();
}
Kriss
la source
0

Est-ce une solution?

class Foo
{
public:
    size_t count;
    Foo()
    {
        static size_t count = 0;
        this->count = count += 1;
    }
};
BSalita
la source
0

Un constructeur statique peut être émulé en utilisant une classe amie ou une classe imbriquée comme ci-dessous.

class ClassStatic{
private:
    static char *str;
public:
    char* get_str() { return str; }
    void set_str(char *s) { str = s; }
    // A nested class, which used as static constructor
    static class ClassInit{
    public:
        ClassInit(int size){ 
            // Static constructor definition
            str = new char[size];
            str = "How are you?";
        }
    } initializer;
};

// Static variable creation
char* ClassStatic::str; 
// Static constructor call
ClassStatic::ClassInit ClassStatic::initializer(20);

int main() {
    ClassStatic a;
    ClassStatic b;
    std::cout << "String in a: " << a.get_str() << std::endl;
    std::cout << "String in b: " << b.get_str() << std::endl;
    a.set_str("I am fine");
    std::cout << "String in a: " << a.get_str() << std::endl;
    std::cout << "String in b: " << b.get_str() << std::endl;
    std::cin.ignore();
}

Production:

String in a: How are you?
String in b: How are you?
String in a: I am fine
String in b: I am fine
Jobin
la source
Pourquoi newn'utilisez- vous un tableau de caractères que pour faire immédiatement fuir le pointeur et l'écraser !?
Eric
0

Wow, je ne peux pas croire que personne n'ait mentionné la réponse la plus évidente, et celle qui imite le plus fidèlement le comportement du constructeur statique de C #, c'est-à-dire qu'elle n'est pas appelée tant que le premier objet de ce type n'est pas créé.

std::call_once()est disponible en C ++ 11; si vous ne pouvez pas utiliser cela, cela peut être fait avec une variable de classe booléenne statique et une opération atomique de comparaison et d'échange. Dans votre constructeur, voyez si vous pouvez changer atomiquement l'indicateur de classe statique de falseà true, et si c'est le cas, vous pouvez exécuter le code de construction statique.

Pour plus de crédit, faites-en un indicateur à trois voies au lieu d'un booléen, c'est-à-dire non exécuté, exécuté et terminé. Ensuite, toutes les autres instances de cette classe peuvent se verrouiller jusqu'à ce que l'instance exécutant le constructeur statique soit terminée (c.-à-d. Émettre une clôture mémoire, puis définir l'état sur "exécution terminée"). Votre spin-lock doit exécuter l'instruction "pause" du processeur, doubler l'attente à chaque fois jusqu'à un seuil, etc. - technique assez standard de spin-lock.

En l'absence de C ++ 11, ce devrait vous aider à démarrer.

Voici un pseudocode pour vous guider. Mettez ceci dans votre définition de classe:

enum EStaticConstructor { kNotRun, kRunning, kDone };
static volatile EStaticConstructor sm_eClass = kNotRun;

Et ceci dans votre constructeur:

while (sm_eClass == kNotRun)
{
    if (atomic_compare_exchange_weak(&sm_eClass, kNotRun, kRunning))
    {
        /* Perform static initialization here. */

        atomic_thread_fence(memory_order_release);
        sm_eClass = kDone;
    }
}
while (sm_eClass != kDone)
    atomic_pause();
ulatekh
la source