statische Konstruktoren in C ++? Ich muss private statische Objekte initialisieren


176

Ich möchte eine Klasse mit einem privaten statischen Datenelement haben (einem Vektor, der alle Zeichen az enthält). In Java oder C # kann ich einfach einen "statischen Konstruktor" erstellen, der ausgeführt wird, bevor ich Instanzen der Klasse erstelle, und die statischen Datenelemente der Klasse einrichten. Es wird nur einmal ausgeführt (da die Variablen schreibgeschützt sind und nur einmal festgelegt werden müssen) und da es eine Funktion der Klasse ist, kann es auf seine privaten Mitglieder zugreifen. Ich könnte dem Konstruktor Code hinzufügen, der prüft, ob der Vektor initialisiert ist, und ihn initialisiert, wenn dies nicht der Fall ist, aber dies führt viele notwendige Prüfungen ein und scheint nicht die optimale Lösung für das Problem zu sein.

Mir fällt der Gedanke ein, dass die Variablen, da sie schreibgeschützt sind, nur öffentliche statische Konstanten sein können, sodass ich sie einmal außerhalb der Klasse festlegen kann, aber es scheint wieder einmal ein hässlicher Hack zu sein.

Ist es möglich, private statische Datenelemente in einer Klasse zu haben, wenn ich sie nicht im Instanzkonstruktor initialisieren möchte?



1
@CiroSantilli question 改造 running 六四 六四 法轮功 Diese Frage konzentriert sich auf das Ausführen von Code zum Initialisieren privater statischer Objekte , ohne konstante Werte privater statischer primitiver Typen festzulegen . Die Lösungen sind unterschiedlich.
Gordon Gustafson

Ah, ich denke du hast recht und ziehst dich zurück.
Ciro Santilli 5 冠状 病. 事件 法轮功

Antworten:


180

Um das Äquivalent eines statischen Konstruktors zu erhalten, müssen Sie eine separate gewöhnliche Klasse schreiben, die die statischen Daten enthält, und dann eine statische Instanz dieser gewöhnlichen Klasse erstellen.

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

};

12
Vielen Dank! obwohl es sehr ärgerlich ist, das alles tun zu müssen. Einer der vielen "Fehler", aus denen C # und Java gelernt haben.
Gordon Gustafson

109
Ja. Ich weise die Leute immer darauf hin, dass wenn C ++ nicht all diese "Fehler" gemacht hätte, andere Sprachen sie machen müssten. C ++, das so viel Boden abdeckt und sogar Fehler macht, war für die darauf folgenden Sprachen großartig.
Quark

11
Nur eine kleine Nuance, da Konstruktoren ins Spiel kommen, garantiert niemand, wenn der Konstruktor für statische Objekte ausgeführt wird. Ein bekannter, viel sicherer Ansatz ist die Klasse Elsewhere {StaticStuff & get_staticStuff () {static StaticStuff staticStuff; // Konstruktor wird einmal ausgeführt, wenn jemand ihn zum ersten Mal benötigt return staticStuff; }}; Ich frage mich, ob statische Konstruktoren in C # und Java die gleiche Garantie wie der obige Code bieten können ...
Oleg Zhylin

13
@Oleg: Ja, das tun sie. Die Standardgarantie, dass die Konstruktoren für alle nicht lokalen Variablen ausgeführt werden, bevor main eingegeben wird. Es wird auch garantiert, dass innerhalb einer Kompilierungseinheit die Reihenfolge der Konstruktion genau definiert ist und dieselbe Reihenfolge wie die Deklaration innerhalb der Kompilierungseinheit. Leider definieren sie die Reihenfolge nicht über mehrere Kompilierungseinheiten hinweg.
Martin York

13
Dies ist tatsächlich ein Fall, in dem friendes sehr sinnvoll ist, dass die Klasse Elsewhereleicht auf StaticStuffdie Interna zugreifen kann (ohne die Kapselung auf gefährliche Weise zu unterbrechen, möchte ich hinzufügen).
Konrad Rudolph

81

Nun, du kannst haben

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

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

Vergessen Sie nicht (in der .cpp):

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

Das Programm wird weiterhin ohne die zweite Zeile verknüpft, aber der Initialisierer wird nicht ausgeführt.


+1 (habe es nicht ausprobiert) Aber: Wann heißt ctor _init._init ()? Vor oder nach dem ctor von MyClass, wenn ich ein statisches MyClass-Objekt habe? Ich denke du kannst es nicht sagen ...
ur.

2
Hallo, wo kann ich mehr über diese "Initialisierungs" -Magie erfahren?
Karel Bílek

Sollte es nicht MyClass::a.push_back(i)statt sein a.push_back(i)?
Neel Basu

4
@ur.: _initializerist ein Unterobjekt von MyClass. Unterobjekte werden in dieser Reihenfolge initialisiert: Unterobjekte der virtuellen Basisklasse in der Reihenfolge der Tiefe von links nach rechts (wobei jedes einzelne Unterobjekt nur einmal initialisiert wird); dann einfache Basisobjekt-Unterobjekte in der Reihenfolge Tiefe zuerst von links nach rechts; dann Mitgliedsunterobjekte in der Reihenfolge der Deklaration. Daher ist es sicher, die Strategie von EFraim zu verwenden, vorausgesetzt, der Code _initialiserbezieht sich nur auf Mitglieder, die zuvor deklariert wurden.
j_random_hacker

2
Diese Antwort ist besser als die akzeptierte, da der Autor die unverzichtbare Initialisierung im zweiten Codeclip erwähnt hat.
Jeff T.

33

C ++ 11 Lösung

Seit C ++ 11 können Sie einfach verwenden Lambda-Ausdrücke verwenden , um statische Klassenmitglieder zu initialisieren. Dies funktioniert sogar, wenn Sie eine Konstruktionsreihenfolge zwischen den verschiedenen statischen Elementen festlegen müssen oder wenn Sie statische Elemente haben const.

Header-Datei:

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

Quelldatei:

// 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();

interessante Lösung. in diesem Fall, wenn ich eine Ausnahme auslöse, wer kann sie fangen?
Rafi Wiener

5
Der statische Programminitialisierungscode darf niemals Ausnahmen auslösen, da das Programm sonst abstürzt. Sie müssen die Initialisierungslogik in einen try catchBlock einschließen, wenn Ausnahmen ausgelöst werden können.
Emkey08

19

In der .h-Datei:

class MyClass {
private:
    static int myValue;
};

In der CPP-Datei:

#include "myclass.h"

int MyClass::myValue = 0;

5
Dies funktioniert gut für einzelne statische Elemente (unabhängig vom Typ). Der Mangel im Vergleich zu statischen Konstruktoren besteht darin, dass Sie keine Reihenfolge zwischen den verschiedenen statischen Elementen festlegen können. Wenn Sie das tun müssen, lesen Sie die Antwort von Earwicker.
Quark

Ich mache genau das, aber es wird immer noch nicht kompiliert. Und es heißt, dies sei der Problembereich (im Konstruktor, nicht der Header)
Flotolk

14

Hier ist ein anderer Ansatz ähnlich dem von Daniel Earwicker, der auch Konrad Rudolphs Vorschlag für eine Freundschaftsklasse verwendet. Hier verwenden wir eine innere private Freund-Dienstprogrammklasse, um die statischen Mitglieder Ihrer Hauptklasse zu initialisieren. Beispielsweise:

Header-Datei:

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

Implementierungsdatei:

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

Dieser Ansatz hat den Vorteil, dass die Initializer-Klasse vollständig vor der Außenwelt verborgen bleibt und alles, was in der zu initialisierenden Klasse enthalten ist, erhalten bleibt.


+1 Für ein Beispiel, das die Implementierung in einer eigenen Datei hält.
Andrew Larsson

1
Außerdem müssen Sie sicherstellen, dass dies ToBeInitialized::Initializer::Initializer()aufgerufen wird, und Sie müssen ToBeInitialized::Initializer ToBeInitialized::initializer;es der Implementierungsdatei hinzufügen . Ich habe einige Dinge von Ihrer Idee und von EFraims Idee übernommen, und es funktioniert genau so, wie ich es brauche und sieht sauber aus. Danke, Mann.
Andrew Larsson

11

Test::StaticTest() wird während der globalen statischen Initialisierung genau einmal aufgerufen.

Der Aufrufer muss der Funktion, die sein statischer Konstruktor sein soll, nur eine Zeile hinzufügen.

static_constructor<&Test::StaticTest>::c;Erzwingt die Initialisierung cwährend der globalen statischen Initialisierung.

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

Dies ist eine fantastische Lösung. Ich mag die Antwort von Douglas Mandel auch sehr, aber das ist noch prägnanter.
FlintZA

Das ist wirklich unglaublich!
nh_

9

Keine init()Funktion erforderlich , std::vectorkann aus einem Bereich erstellt werden:

// 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 ) );

Beachten Sie jedoch, dass Statiken vom Klassentyp Probleme in Bibliotheken verursachen, sodass sie dort vermieden werden sollten.

C ++ 11 Update

Ab C ++ 11 können Sie dies stattdessen tun:

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

Es entspricht semantisch der C ++ 98-Lösung in der ursprünglichen Antwort, aber Sie können kein String-Literal auf der rechten Seite verwenden, sodass es nicht völlig überlegen ist. Wenn Sie jedoch einen Vektor von jeder anderen Art haben als char, wchar_t, char16_toder char32_t(Arrays von denen eine als Stringliterale geschrieben werden kann), die C ++ 11 - Version wird ausschließlich Standardcode entfernen , ohne andere Syntax Einführung, im Vergleich zu dem C ++ 98 Ausführung.


Ich mag das. Wenn wir es nur in einer Zeile ohne das jetzt nutzlose Alphabet tun könnten.
Martin York

Ist es für Probleme mit Bibliotheken wichtig, ob die statische Klasse privat oder öffentlich ist? Ist es außerdem wichtig, ob die Bibliothek statisch (.a) oder dynamisch (.so) ist?
Zachary Kraus

@ ZacharyKraus: Was ist eine öffentliche / private Klasse ? Und nein, obwohl die Probleme unterschiedlich sind, sich aber überschneiden, spielt es keine Rolle, ob die Bibliothek statisch oder dynamisch verknüpft ist.
Marc Mutz - mmutz

@ MarcMutz-mmutz Tut mir leid, dass ich eine öffentliche / private Klasse verwende, deren C ++ - Terminologie nicht korrekt ist. Worauf ich mich bezog, ist die Lösung von EFraim oben. In meiner Version habe ich das statische Klassenmitglied jedoch privat gemacht. Ich habe versucht zu verstehen, ob ein statisches Klassenmitglied als öffentlich oder privat einen Unterschied in der Bibliotheksentwicklung und Benutzerfreundlichkeit macht. Mein Bauch sagt mir, dass es keine Auswirkungen auf die Bibliothek haben sollte, da Benutzer niemals Zugriff auf das statische Klassenmitglied oder das Objekt haben, das es erstellt, aber ich würde gerne die Weisheit eines Gurus zu diesem Thema erhalten.
Zachary Kraus

@ZacharyKraus: Das Hauptproblem bei Statiken, die eine dynamische Initialisierung erfordern ([basic.start.init] / 2), besteht darin, dass sie Code ausführen. In Bibliotheken kann es sein, dass der Bibliothekscode bereits entladen wurde, wenn die Destruktoren ausgeführt werden. Wenn Sie mehr hören möchten, schlage ich vor, eine Frage dazu zu stellen.
Marc Mutz - mmutz

6

Das Konzept der statischen Konstruktoren wurde in Java eingeführt, nachdem sie aus den Problemen in C ++ gelernt hatten. Wir haben also kein direktes Äquivalent.

Die beste Lösung ist die Verwendung von POD-Typen, die explizit initialisiert werden können.
Oder machen Sie Ihre statischen Elemente zu einem bestimmten Typ, der über einen eigenen Konstruktor verfügt, der ihn korrekt initialisiert.

//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;

4

Beim Versuch, eine Klasse zu kompilieren und zu verwendenElsewhere (aus Earwickers Antwort ), erhalte ich:

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

Es scheint nicht möglich zu sein, statische Attribute von nicht ganzzahligen Typen zu initialisieren, ohne Code außerhalb der Klassendefinition (CPP) einzufügen.

Um diese Kompilierung durchzuführen, können Sie stattdessen " eine statische Methode mit einer statischen lokalen Variablen " verwenden. Etwas wie das:

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

Sie können dem Konstruktor auch Argumente übergeben oder ihn mit bestimmten Werten initialisieren. Er ist sehr flexibel, leistungsstark und einfach zu implementieren. Sie verfügen lediglich über eine statische Methode, die eine statische Variable und kein statisches Attribut enthält. Die Syntax ändert sich ein wenig, ist aber dennoch nützlich. Hoffe das ist nützlich für jemanden,

Hugo González Castro.


Seien Sie jedoch vorsichtig, wenn Sie Gewinde verwenden. Ich glaube, dass in GCC die Konstruktion statischer lokaler Elemente vor gleichzeitiger Ausführung geschützt ist, in Visual C ++ jedoch nicht.
Daniel Earwicker

1
Von C ++ 11 weiter, und in POSIX, es hat threadsicher sein.
Marc Mutz - mmutz

Ich mochte zwei andere Lösungen oben ( dies und das ), aber Ihre ist die einzige, die die Initialisierung der Statik in der Reihenfolge sicherstellt, in der sie für alle Bibliotheken benötigt wird. Ich habe nur eine private statische Instanzmethode wie Ihre oben und verpacke den Zugriff auf andere Werte in öffentliche statische Accessoren, die diese Instanzmethode anstelle von direkten Verweisen verwenden. Vielen Dank.
FlintZA

Genial! Damit ist es abgeschlossen.
Gabe Halsmer

4

Ich denke, eine einfache Lösung dafür wird sein:

    //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;
    }

So mache ich es auch.
Etherealone

1

Habe gerade den gleichen Trick gelöst. Ich musste die Definition eines einzelnen statischen Elements für Singleton angeben. Aber machen Sie die Dinge komplizierter - ich habe beschlossen, ctor von RandClass () nicht aufzurufen, es sei denn, ich werde es verwenden ... deshalb wollte ich Singleton in meinem Code nicht global initialisieren. Außerdem habe ich in meinem Fall eine einfache Schnittstelle hinzugefügt.

Hier ist der endgültige Code:

Ich habe den Code vereinfacht und verwende die Funktion rand () und ihren Single Seed Initialisierer 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();
}

1

Hier ist meine Variante der EFraim-Lösung. Der Unterschied besteht darin, dass der statische Konstruktor dank der impliziten Vorlageninstanziierung nur aufgerufen wird, wenn Instanzen der Klasse erstellt werden, und dass keine Definition in der .cppDatei erforderlich ist (dank der Magie der Vorlageninstanziierung).

In der .hDatei haben Sie:

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;

In der .cppDatei können Sie haben:

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

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

Beachten Sie, dass dies MyClass::anur initialisiert wird, wenn Zeile [1] vorhanden ist, da dies den Konstruktor aufruft (und dessen Instanziierung erfordert), der dann die Instanziierung von erfordert _initializer.


1

Hier ist eine andere Methode, bei der der Vektor für die Datei, die die Implementierung enthält, privat ist, indem ein anonymer Namespace verwendet wird. Dies ist nützlich für Dinge wie Nachschlagetabellen, die für die Implementierung privat sind:

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

Vielleicht möchten Sie einen Namen Iund ietwas Dunkleres benennen, damit Sie sie nicht versehentlich irgendwo weiter unten in der Datei verwenden.
Jim Hunziker

1
Um ehrlich zu sein, ist es schwer zu verstehen, warum jemand private statische Mitglieder anstelle anonymer Namespaces in Implementierungsdateien verwenden möchte.
Jim Hunziker

1

Es muss sicherlich nicht so kompliziert sein wie die derzeit akzeptierte Antwort (von Daniel Earwicker). Die Klasse ist überflüssig. In diesem Fall ist kein Sprachkrieg erforderlich.

.hpp Datei:

vector<char> const & letters();

CPP-Datei:

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


0

Sie definieren statische Elementvariablen ähnlich wie Sie Elementmethoden definieren.

foo.h

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

foo.cpp

#include "foo.h"

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

int Foo::count = 0;

2
CrazyJugglerDrummer Frage war nicht über einen statischen einfachen alten Datentyp :)
jww

0

Um eine statische Variable zu initialisieren, tun Sie dies einfach innerhalb einer Quelldatei. Beispielsweise:

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


//Foo.cpp
int Foo::hello = 1;

CrazyJugglerDrummer Frage war nicht über einen statischen einfachen alten Datentyp :)
jww

0

Wie wäre es mit einer Vorlage, die das Verhalten von C # nachahmt?

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

0

Für einfache Fälle wie hier ist eine statische Variable, die in eine statische Elementfunktion eingeschlossen ist, fast genauso gut. Es ist einfach und wird normalerweise von Compilern optimiert. Dies löst jedoch nicht das Problem der Initialisierungsreihenfolge für komplexe Objekte.

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

0

Ist das eine Lösung?

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

0

Ein statischer Konstruktor kann mithilfe einer Friend-Klasse oder einer verschachtelten Klasse wie folgt emuliert werden.

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

Ausgabe:

String in a: How are you?
String in b: How are you?
String in a: I am fine
String in b: I am fine

Warum verwenden Sie newein char-Array nur, um den Zeiger sofort zu verlieren und zu überschreiben?
Eric

0

Wow, ich kann nicht glauben, dass niemand die offensichtlichste Antwort erwähnt hat und eine, die das Verhalten des statischen Konstruktors von C # am besten nachahmt, dh sie wird erst aufgerufen, wenn das erste Objekt dieses Typs erstellt wird.

std::call_once()ist in C ++ 11 verfügbar; Wenn Sie das nicht verwenden können, können Sie dies mit einer statischen booleschen Klassenvariablen und einer atomaren Operation zum Vergleichen und Austauschen durchführen. In Ihrem Konstruktor finden Sie, wenn Sie atomar die Klasse statische Flagge von ändern können falsezu true, und wenn ja , können Sie die statische Konstruktion Code ausführen.

Machen Sie es für zusätzliche Gutschrift zu einem 3-Wege-Flag anstelle eines Booleschen, dh nicht ausgeführt, ausgeführt und ausgeführt. Dann können alle anderen Instanzen dieser Klasse durch Sperren gesperrt werden, bis die Instanz, auf der der statische Konstruktor ausgeführt wird, beendet ist (dh einen Speicherzaun ausgeben und dann den Status auf "Fertig ausführen" setzen). Ihr Spin-Lock sollte die "Pause" -Anweisung des Prozessors ausführen, die Wartezeit jedes Mal bis zu einem Schwellenwert verdoppeln usw. - eine ziemlich übliche Spin-Locking-Technik.

In Abwesenheit von C ++ 11, diese sollten Sie loslegen.

Hier ist ein Pseudocode, der Sie führt. Fügen Sie dies in Ihre Klassendefinition ein:

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

Und das in Ihrem Konstruktor:

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();
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.