statyczne konstruktory w C ++? Muszę zainicjować prywatne obiekty statyczne

176

Chcę mieć klasę z prywatnym statycznym składnikiem danych (wektorem zawierającym wszystkie znaki az). W Javie lub C # mogę po prostu stworzyć „statyczny konstruktor”, który będzie działał przed utworzeniem jakichkolwiek instancji klasy, i ustawiam statyczne składowe danych klasy. Jest uruchamiany tylko raz (ponieważ zmienne są tylko do odczytu i muszą być ustawione tylko raz), a ponieważ jest to funkcja klasy, ma dostęp do swoich prywatnych członków. Mógłbym dodać kod w konstruktorze, który sprawdza, czy wektor jest zainicjowany, i inicjuje go, jeśli tak nie jest, ale wprowadza to wiele niezbędnych kontroli i nie wydaje się optymalnym rozwiązaniem problemu.

Przychodzi mi do głowy myśl, że skoro zmienne będą tylko do odczytu, mogą być po prostu publicznymi stałymi statycznymi, więc mogę ustawić je raz poza klasą, ale znowu wygląda to na brzydki hack.

Czy można mieć prywatne statyczne elementy członkowskie w klasie, jeśli nie chcę ich inicjować w konstruktorze instancji?

Gordon Gustafson
źródło
3
Możliwy duplikat Jak zainicjować prywatne statyczne elementy członkowskie w C ++?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功 To pytanie koncentruje się na uruchamianiu kodu w celu zainicjowania prywatnych obiektów statycznych , a nie na ustawianiu stałych wartości prywatnych statycznych typów pierwotnych. Rozwiązania są różne.
Gordon Gustafson,
ah, myślę, że masz rację, wycofując się.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

180

Aby uzyskać odpowiednik konstruktora statycznego, musisz napisać oddzielną zwykłą klasę do przechowywania danych statycznych, a następnie utworzyć statyczną instancję tej zwykłej klasy.

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
źródło
12
dzięki! chociaż to bardzo irytujące, że trzeba to wszystko robić. Jeden z wielu "błędów", z których nauczyły się C # i Java.
Gordon Gustafson
109
Tak. Zawsze zwracam uwagę ludzi, że gdyby C ++ nie popełnił tych wszystkich "błędów", to inne języki musiałyby je popełnić. C ++ obejmujący tak duży obszar, nawet popełniający błędy, był świetny dla języków, które po nim nastąpiły.
kwark
11
Tylko jeden mały niuans, ponieważ w grę wchodzą konstruktory, nikt nie gwarantuje, że konstruktor dla obiektu statycznego zostanie wykonany. Dobrze znanym, dużo bezpieczniejszym podejściem jest klasa Gdzie indziej {StaticStuff & get_staticStuff () {static StaticStuff staticStuff; // konstruktor działa raz, kiedy ktoś go najpierw potrzebuje, zwraca staticStuff; }}; Zastanawiam się, czy statyczne konstruktory w C # i Javie mogą zapewnić taką samą gwarancję jak powyższy kod ...
Oleg Zhylin
13
@Oleg: Tak, robią. Standard gwarantuje, że konstruktory dla wszystkich zmiennych nielokalnych zostaną wykonane przed wprowadzeniem main. Gwarantuje również, że w jednostce kompilacji kolejność konstrukcji jest dobrze zdefiniowana i taka sama jak w deklaracji w jednostce kompilacji. Niestety nie określają kolejności w wielu jednostkach kompilacji.
Martin York
13
Jest to właściwie przypadek, w którym friendma to duży sens, aby klasa Elsewheremogła łatwo uzyskać dostęp do StaticStuffwewnętrznych elementów (bez przerywania hermetyzacji w żaden niebezpieczny sposób, dodam).
Konrad Rudolph
81

Cóż, możesz mieć

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

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

Nie zapomnij (w .cpp) o tym:

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

Program będzie nadal łączył się bez drugiej linii, ale inicjator nie zostanie wykonany.

EFraim
źródło
+1 (nie wypróbowałem) Ale: Kiedy wywoływana jest funkcja ctor _init._init ()? Przed czy po rekordzie MyClass, gdy mam statyczny obiekt MyClass? Chyba nie możesz powiedzieć ...
ur.
2
cześć, gdzie mogę znaleźć więcej informacji na temat tej magii „inicjatora”?
Karel Bílek
Nie powinno być MyClass::a.push_back(i)zamiast tego a.push_back(i)?
Neel Basu
4
@ur .: _initializerjest podobiektem MyClass. Podobiekty są inicjowane w następującej kolejności: podobiekty wirtualnej klasy bazowej, w kolejności od początku do końca, od lewej do prawej (ale inicjalizacja każdego odrębnego podobiektu tylko raz); następnie zwykłe podobiekty klasy bazowej, w kolejności od lewej do prawej; następnie podobiekty składowe w kolejności deklaracji. Dlatego można bezpiecznie używać strategii EFraim, pod warunkiem, że kod _initialiserodnosi się tylko do członków zadeklarowanych przed nim.
j_random_hacker
2
Ta odpowiedź jest lepsza niż zaakceptowana, ponieważ autor wspomniał o niezbędnej inicjalizacji na drugim klipie kodu.
Jeff T.
33

Rozwiązanie C ++ 11

Od C ++ 11 możesz po prostu użyć wyrażeń lambda do zainicjowania statycznych składowych klas. Działa to nawet wtedy, gdy trzeba narzucić kolejność konstrukcji między różnymi statycznymi elementami lub jeśli masz statyczne elementy, które są const.

Plik nagłówkowy:

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

Plik źródłowy:

// 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
źródło
ciekawe rozwiązanie. w takim przypadku, jeśli wyrzucę wyjątek, kto może go złapać?
rafi wiener
5
Statyczny kod inicjujący program nigdy nie może zgłaszać żadnych wyjątków, w przeciwnym razie program ulegnie awarii. try catchJeśli mogą zostać zgłoszone wyjątki, należy zawinąć logikę inicjatora w blok.
emkey08
19

W pliku .h:

class MyClass {
private:
    static int myValue;
};

W pliku .cpp:

#include "myclass.h"

int MyClass::myValue = 0;
Mrówka
źródło
5
Działa to dobrze w przypadku pojedynczych statycznych elementów członkowskich (niezależnie od typu). Wadą w porównaniu z konstruktorami statycznymi jest to, że nie można narzucić kolejności między różnymi statycznymi składowymi. Jeśli musisz to zrobić, zobacz odpowiedź Earwicker.
kwark
Robię dokładnie to, ale nadal się nie kompiluje. I mówi, że to jest obszar problemowy (w konstruktorze, a nie w nagłówku)
Flotolk
14

Oto inne podejście, podobne do Daniela Earwickera, wykorzystujące również sugestię klasy przyjaciela Konrada Rudolpha. Tutaj używamy wewnętrznej klasy użytkowej znajomego prywatnego, aby zainicjować statyczne elementy składowe Twojej głównej klasy. Na przykład:

Plik nagłówkowy:

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

Plik wdrożeniowy:

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

To podejście ma tę zaletę, że całkowicie ukrywa klasę Initializer przed światem zewnętrznym, zachowując wszystko zawarte w klasie, która ma zostać zainicjowana.

Douglas Mandell
źródło
+1 Za podanie przykładu, który przechowuje implementację w swoim własnym pliku.
Andrew Larsson,
1
Musisz także upewnić się, że ToBeInitialized::Initializer::Initializer()zostanie wywołany, więc musisz dodać ToBeInitialized::Initializer ToBeInitialized::initializer;do pliku implementacji. Wziąłem kilka rzeczy z Twojego pomysłu oraz z pomysłu EFraima i działa dokładnie tak, jak tego potrzebuję i wygląda na czysty. Dzięki.
Andrew Larsson,
11

Test::StaticTest() jest wywoływana dokładnie raz podczas globalnej inicjalizacji statycznej.

Wzywający musi tylko dodać jedną linię do funkcji, która ma być ich konstruktorem statycznym.

static_constructor<&Test::StaticTest>::c;wymusza inicjalizację cpodczas globalnej inicjalizacji statycznej.

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;
}
Ciekawski George
źródło
To fantastyczne rozwiązanie. Bardzo podoba mi się również odpowiedź Douglasa Mandela , ale jest ona jeszcze bardziej zwięzła.
FlintZA
To jest naprawdę niesamowite!
nh_
9

Nie potrzebujesz init()funkcji, std::vectormożna ją utworzyć z zakresu:

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

Należy jednak pamiętać, że statyka typu klasy powoduje problemy w bibliotekach, dlatego należy ich tam unikać.

Aktualizacja C ++ 11

Począwszy od C ++ 11, możesz to zrobić zamiast tego:

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

Jest semantycznie równoważne z rozwiązaniem C ++ 98 w oryginalnej odpowiedzi, ale nie można użyć literału ciągu po prawej stronie, więc nie jest całkowicie lepszy. Jednakże, jeśli masz wektor innego rodzaju niż char, wchar_t, char16_tlub char32_t(tablice, z których może być zapisany jako napisowych), wersja C ++ 11 będzie bezwzględnie usunąć kod szablonowe bez wprowadzania innej składni, w porównaniu do C ++ 98 wersja.

Marc Mutz - mmutz
źródło
Lubię to. Chociaż gdybyśmy tylko mogli to zrobić w jednej linii bez teraz bezużytecznego alfabetu.
Martin York
Czy w przypadku problemów z bibliotekami ma znaczenie, czy klasa statyczna jest prywatna czy publiczna? Ponadto, czy ma znaczenie, czy biblioteka jest statyczna (.a) czy dynamiczna (.so)?
Zachary Kraus
@ZacharyKraus: co to są zajęcia publiczne / prywatne ? I nie, chociaż problemy są różne, ale nakładają się, nie ma znaczenia, czy biblioteka jest połączona statycznie czy dynamicznie.
Marc Mutz - mmutz
@ MarcMutz-mmutz Przepraszamy za używanie klasy publicznej / prywatnej, która nie jest poprawną terminologią C ++. To, o czym mówiłem, to rozwiązanie EFraima powyżej. Jednak w mojej wersji ustawiłem statyczny element członkowski klasy jako prywatny. Próbowałem zrozumieć, czy posiadanie statycznego członka klasy jako publicznego czy prywatnego ma wpływ na rozwój i użyteczność biblioteki. Moje przeczucie mówi mi, że nie powinno to wpływać na bibliotekę, ponieważ użytkownicy nigdy nie będą mieli dostępu ani do statycznego elementu klasy, ani do obiektu, który tworzy, ale chciałbym uzyskać mądrość jakiegoś guru na ten temat.
Zachary Kraus
@ZacharyKraus: Głównym problemem ze statyką, która wymaga dynamicznej inicjalizacji ([basic.start.init] / 2), jest to, że uruchamiają kod. W bibliotekach może się zdarzyć, że kod biblioteki został już wyładowany podczas działania destruktorów. Jeśli chcesz dowiedzieć się więcej, proponuję zadać pytanie na ten temat.
Marc Mutz - mmutz
6

Pojęcie konstruktorów statycznych zostało wprowadzone w Javie po zapoznaniu się z problemami w C ++. Nie mamy więc bezpośredniego odpowiednika.

Najlepszym rozwiązaniem jest użycie typów POD, które można jawnie zainicjować.
Lub nadaj statycznym elementom członkowskim określony typ, który ma własny konstruktor, który zainicjuje go poprawnie.

//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
źródło
4

Próbując skompilować i użyć klasy Elsewhere(z odpowiedzi Earwickera ) otrzymuję:

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

Wydaje się, że nie jest możliwe zainicjowanie statycznych atrybutów typów innych niż całkowite bez umieszczenia kodu poza definicją klasy (CPP).

Aby dokonać tej kompilacji, możesz zamiast tego użyć „ metody statycznej ze statyczną zmienną lokalną wewnątrz ”. Coś takiego:

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

Możesz również przekazać argumenty do konstruktora lub zainicjować go określonymi wartościami, jest bardzo elastyczny, wydajny i łatwy do zaimplementowania ... jedyną rzeczą jest to, że masz metodę statyczną zawierającą zmienną statyczną, a nie atrybut statyczny ... składnia zmienia się nieco, ale nadal jest przydatna. Mam nadzieję, że komuś się to przyda,

Hugo González Castro.

Społeczność
źródło
Chociaż uważaj, jeśli używasz nici. Wierzę, że w GCC konstrukcja statycznych lokalizacji lokalnych jest chroniona przed równoczesnym wykonywaniem, ale w Visual C ++ tak nie jest.
Daniel Earwicker,
1
Począwszy od C ++ 11 iw POSIX, musi być bezpieczny dla wątków.
Marc Mutz - mmutz
Dość podobały mi się dwa inne rozwiązania powyżej ( to i to ), ale Twoje jest jedynym, które zapewnia inicjalizację statyki w kolejności, w jakiej są potrzebne w bibliotekach. Mam tylko prywatną statyczną metodę Instancji, taką jak Twoja powyżej, i zawijam dostęp do innych wartości w publicznych statycznych metodach dostępu, które używają tej metody Instance zamiast bezpośrednich odwołań. Dzięki.
FlintZA
Niesamowite! To wszystko kończy.
Gabe Halsmer
4

Myślę, że prostym rozwiązaniem będzie:

    //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
źródło
Ja też tak to robię.
Etherealone
1

Właśnie rozwiązałem tę samą sztuczkę. Musiałem określić definicję pojedynczego statycznego elementu członkowskiego dla Singletona. Ale komplikuj sprawę - zdecydowałem, że nie chcę wywoływać ctora RandClass (), chyba że go użyję ... dlatego nie chciałem inicjalizować singletona globalnie w swoim kodzie. Dodałem też prosty interfejs w moim przypadku.

Oto ostateczny kod:

Uprościłem kod i użyłem funkcji rand () i jej pojedynczego ziarna 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
źródło
1

Oto mój wariant rozwiązania EFraim; różnica polega na tym, że dzięki niejawnej instancji szablonu konstruktor statyczny jest wywoływany tylko wtedy, gdy tworzone są instancje klasy i nie .cppjest potrzebna żadna definicja w pliku (dzięki magii tworzenia instancji szablonu).

W .hpliku masz:

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;

W .cpppliku możesz mieć:

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

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

Zauważ, że MyClass::ajest inicjowane tylko wtedy, gdy istnieje linia [1], ponieważ wywołuje (i wymaga utworzenia instancji) konstruktora, który następnie wymaga instancji _initializer.

Blaisorblade
źródło
1

Oto inna metoda, w której wektor jest prywatny dla pliku zawierającego implementację przy użyciu anonimowej przestrzeni nazw. Jest to przydatne w przypadku takich rzeczy, jak tabele wyszukiwania, które są prywatne dla implementacji:

#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
źródło
Chociaż możesz chcieć nazwać Ii icoś bardziej niejasnego, aby nie przypadkowo użyć ich gdzieś niżej w pliku.
Jim Hunziker
1
Szczerze mówiąc, trudno zrozumieć, dlaczego ktoś miałby chcieć używać prywatnych statycznych elementów członkowskich zamiast anonimowych przestrzeni nazw w plikach implementacji.
Jim Hunziker
1

Z pewnością nie musi być tak skomplikowane, jak obecnie akceptowana odpowiedź (autorstwa Daniela Earwickera). Klasa jest zbędna. W tym przypadku nie ma potrzeby wojny językowej.

plik .hpp:

vector<char> const & letters();

plik .cpp:

vector<char> const & letters()
{
  static vector<char> v = {'a', 'b', 'c', ...};
  return v;
}
AndyJost
źródło
0

Definiujesz statyczne zmienne składowe podobnie do sposobu definiowania metod składowych.

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;
Nick Lewis
źródło
2
Pytanie CrazyJugglerDrummer nie dotyczyło statycznego, zwykłego starego typu danych :)
jww
0

Aby zainicjować zmienną statyczną, wystarczy zrobić to wewnątrz pliku źródłowego. Na przykład:

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


//Foo.cpp
int Foo::hello = 1;
Cristián Romo
źródło
Pytanie CrazyJugglerDrummer nie dotyczyło statycznego, zwykłego starego typu danych :)
jww
0

Co powiesz na utworzenie szablonu naśladującego zachowanie języka 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
źródło
0

W prostych przypadkach, takich jak tutaj, zmienna statyczna opakowana wewnątrz statycznej funkcji składowej jest prawie tak dobra. Jest to proste i zazwyczaj zostanie zoptymalizowane przez kompilatory. Nie rozwiązuje to jednak problemu kolejności inicjalizacji dla złożonych obiektów.

#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
źródło
0

Czy to jest rozwiązanie?

class Foo
{
public:
    size_t count;
    Foo()
    {
        static size_t count = 0;
        this->count = count += 1;
    }
};
BSalita
źródło
0

Konstruktor statyczny można emulować przy użyciu klasy zaprzyjaźnionej lub klasy zagnieżdżonej, jak poniżej.

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

Wynik:

String in a: How are you?
String in b: How are you?
String in a: I am fine
String in b: I am fine
Praca w
źródło
Dlaczego newtablica znaków służy tylko do natychmiastowego wycieku wskaźnika i nadpisania go !?
Eric
0

Wow, nie mogę uwierzyć, że nikt nie wspomniał o najbardziej oczywistej odpowiedzi, która najbardziej naśladuje zachowanie statycznego konstruktora C #, tj. Nie jest wywoływana, dopóki nie zostanie utworzony pierwszy obiekt tego typu.

std::call_once()jest dostępny w C ++ 11; jeśli nie możesz tego użyć, możesz to zrobić za pomocą statycznej zmiennej typu boolean oraz operacji atomowej typu „porównaj i zamień”. Sprawdź w swoim konstruktorze, czy możesz atomowo zmienić flagę statyczną klasy z falsena true, a jeśli tak, możesz uruchomić kod konstrukcji statycznej.

Aby uzyskać dodatkowe punkty, utwórz flagę trójstronną zamiast wartości logicznej, tj. Nie uruchomiono, uruchomiono i zakończono uruchamianie. Następnie wszystkie inne instancje tej klasy mogą się blokować, dopóki instancja, na której działa konstruktor statyczny, nie zakończy się (tj. Wystawi ochronę pamięci, a następnie ustaw stan na „gotowe do działania”). Twój spin-lock powinien wykonywać instrukcję "pauza" procesora, podwajać czas oczekiwania aż do progu itd. - całkiem standardowa technika blokowania obrotów.

W przypadku braku C ++ 11 to powinno Ci zacząć.

Oto pseudokod, który Cię poprowadzi. Umieść to w definicji swojej klasy:

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

A to w twoim konstruktorze:

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
źródło