C ++ statik yapıcılar? Özel statik nesneleri başlatmam gerekiyor


176

Özel statik veri üyesi (az tüm karakterleri içeren bir vektör) ile bir sınıf istiyorum. Java veya C #, ben sadece sınıf herhangi bir örnek yapmadan önce çalışacak bir "statik yapıcı" yapabilir ve sınıf statik veri üyelerini ayarlar. Yalnızca bir kez çalıştırılır (değişkenler salt okunur olduğundan ve yalnızca bir kez ayarlanması gerektiğinden) ve sınıfın bir işlevi olduğundan özel üyelerine erişebilir. Yapıcıya vektörün başlatılıp başlatılmadığını kontrol eden bir kod ekleyebilirim ve eğer değilse başlatırım, ancak bu birçok gerekli kontrol sağlar ve soruna en uygun çözüm gibi görünmüyor.

Düşünce bana, değişkenlerin salt okunur olacağı için, sadece halka açık statik sabit olabildikleri için onları sınıfın dışında bir kez ayarlayabilirim, ama bir kez daha, çirkin bir hack gibi görünüyor.

Onları örnek oluşturucuda başlatmak istemezsem, özel statik veri üyelerine bir sınıfta sahip olmak mümkün mü?



1
Bu soru, özel statik ilkel türlerin sabit değerlerini ayarlamaya değil, özel statik nesneleri başlatmak için kod çalıştırmaya odaklanır . Çözümler farklı.
Gordon Gustafson

ah, haklısın, geri çekiliyorsun.
Ciro Santilli 法轮功 18 病 六四 事件 法轮功

Yanıtlar:


180

Statik bir kurucunun eşdeğerini elde etmek için, statik verileri tutmak için ayrı bir sıradan sınıf yazmanız ve ardından o sıradan sınıfın statik bir örneğini oluşturmanız gerekir.

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
Teşekkürler! her şeye rağmen bu çok can sıkıcı bir durum. Birçok "hata" dan biri C # ve java öğrendi.
Gordon Gustafson

109
Evet. İnsanlara her zaman, C ++ tüm bu "hataları" yapmasaydı, o zaman diğer dillerin bunları yapmak zorunda kalacağına dikkat çekerim. C ++ 'ı çok fazla zemin kaplayan, hatta hata yapan bile, takip eden diller için harikaydı.
quark

11
Sadece küçük bir nüans, kurucular devreye girerken, statik nesne için yapıcı çalıştığında kimse garanti etmez. Bilinen çok daha güvenli bir yaklaşım, Başka Yerlerde sınıftır {StaticStuff & get_staticStuff () {static StaticStuff staticStuff; // yapıcı bir kez çalışır, birisi ilk ihtiyaç duyduğunda staticStuff döndürür; }}; Acaba C # ve Java statik yapıcıları yukarıdaki kod aynı garanti verebilir eğer ...
Oleg Zhylin

13
@Oleg: Evet biliyorlar. Tüm yerel olmayan değişkenler için yapıcıların main girilmeden önce yürütüldükleri standart garantilerdir. Ayrıca, bir derleme birimi içinde inşaat sırasının iyi tanımlanmış olduğunu ve derleme birimi içindeki beyan ile aynı sıraya sahip olduğunu garanti eder. Maalesef siparişi birden fazla derleme biriminde tanımlamıyorlar.
Martin York

13
Bu aslında friendçok mantıklı bir durumdur, böylece sınıfın Elsewhereiçlerine kolayca erişebilir StaticStuff(kapsüllemeyi herhangi bir tehlikeli şekilde bozmadan ekleyebilirim).
Konrad Rudolph

81

Peki sahip olabilirsiniz

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

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

Unutmayın (.cpp içinde):

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

Program ikinci hat olmadan da bağlantı kuracaktır, ancak başlatıcı yürütülmeyecektir.


+1 (denemedi) Ancak: Ctor _init._init () ne zaman çağrılır? Statik bir MyClass nesnem olduğunda MyClass ctor'dan önce veya sonra? Sanýrým söyleyemezsin ...
ur.

2
merhaba, bu "başlatıcı" büyüsü hakkında nereden daha fazla bilgi bulabilirim?
Karel Bílek

Bunun MyClass::a.push_back(i)yerine olmamalı mı a.push_back(i)?
Neel Basu

4
@ur .: _initializerbir alt nesnedir MyClass. Alt nesneler bu sırayla başlatılır: sanal temel sınıf alt nesneleri, önce derinlik, soldan sağa sırada (ancak her farklı alt nesneyi yalnızca bir kez başlatma); daha sonra ilk önce soldan sağa doğru düz temel sınıf alt nesneleri; sonra üye alt nesneleri bildirim sırasına göre. Bu nedenle, EFraim'in stratejisini kullanmak güvenlidir, ancak bu kod _initialiseryalnızca kendisinden önce beyan edilen üyelere atıfta bulunur.
j_random_hacker

2
Bu cevap kabul edilen yanıttan daha iyidir, çünkü yazar ikinci kod klibinde vazgeçilmez bir başlangıçtan bahsetmiştir.
Jeff T.

33

C ++ 11 çözümü

C ++ 11'den beri, statik sınıf üyelerini başlatmak için lambda ifadelerini kullanabilirsiniz . Bu, çeşitli statik üyeler arasında bir inşaat sırası emretmeniz gerektiğinde veya statik üyeleriniz varsa bile çalışır const.

Başlık dosyası:

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

Kaynak dosyası:

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

ilginç bir çözüm. Bu durumda, onu yakalayabilecek bir istisna atarsam?
rafi wiener

5
Statik program başlatma kodu hiçbir zaman istisna oluşturmamalıdır, aksi takdirde program kilitlenir. try catchİstisnalar atılabiliyorsa başlatıcı mantığını bir bloğa sarmanız gerekir .
emkey08

19

.H dosyasında:

class MyClass {
private:
    static int myValue;
};

.Cpp dosyasında:

#include "myclass.h"

int MyClass::myValue = 0;

5
Bu, bağımsız statik üyeler için uygundur (türden bağımsız olarak). Statik yapıcılara kıyasla eksiklik , çeşitli statik üyeler arasında sipariş veremeyeceğinizdir . Bunu yapmanız gerekiyorsa Earwicker'ın cevabına bakınız.
quark

Ben de aynen bunu yapıyorum ama hala derlemiyor. Ve bu sorun alanı (başlıkta değil,
kurucuda

14

Konrad Rudolph'un arkadaş sınıfı önerisini kullanan Daniel Earwicker's'e benzer başka bir yaklaşım. Burada ana sınıfınızın statik üyelerini başlatmak için bir iç özel arkadaş yardımcı programı sınıfı kullanıyoruz. Örneğin:

Başlık dosyası:

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

Uygulama dosyası:

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

Bu yaklaşımın avantajı, Initializer sınıfını dış dünyadan tamamen gizleyerek sınıftaki her şeyi başlatılmasını sağlamaktır.


+1 Uygulamayı kendi dosyasında tutan bir örnek vermek için.
Andrew Larsson

1
Ayrıca, bunun ToBeInitialized::Initializer::Initializer()çağrıldığından emin olmalısınız , bu yüzden ToBeInitialized::Initializer ToBeInitialized::initializer;uygulama dosyasına eklemeniz gerekir . Fikrinizden ve EFraim'in fikrinden bazı şeyler aldım ve tam olarak ihtiyacım olan şekilde çalışıyor ve temiz görünüyor. Teşekkürler dostum.
Andrew Larsson

11

Test::StaticTest() global statik başlatma sırasında tam olarak bir kez çağrılır.

Arayanın statik yapıcısı olacak işleve yalnızca bir satır eklemesi gerekir.

static_constructor<&Test::StaticTest>::c;cglobal statik başlatma sırasında başlatılmasını zorlar .

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

Bu harika bir çözüm. Douglas Mandel'in cevabını da çok seviyorum , ama bu daha da özlü.
FlintZA

Bu gerçekten inanılmaz!
nh_

9

Bir init()fonksiyona gerek yok std::vector, bir aralıktan oluşturulabilir:

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

Bununla birlikte, sınıf tipi statiklerin kütüphanelerde soruna neden olduğunu ve bu nedenle bunlardan kaçınılması gerektiğini unutmayın.

C ++ 11 Güncellemesi

C ++ 11'den itibaren bunun yerine şunları yapabilirsiniz:

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

Orijinal cevaptaki C ++ 98 çözümüyle semantik olarak eşdeğerdir, ancak sağ tarafta bir dize hazır bilgisi kullanamazsınız, bu yüzden tamamen üstün değildir. Başka türde bir vektör varsa, ancak daha char, wchar_t, char16_tveya char32_t(dize hazır bilgi olarak yazılabilir hangi diziler), kesinlikle diğer sözdizimi tanıtan olmadan Demirbaş kodu kaldıracaktır C ++ 11 versiyonu, C ++ 98 oranla sürümü.


Bunu sevdim. Yine de, şimdi işe yaramaz alfabe olmadan bir satırda yapabilseydik.
Martin York

Kütüphanelerle ilgili sorunlara neden olmak için, statik sınıfın özel veya herkese açık olması önemli mi? Ayrıca, kitaplığın statik (.a) veya dinamik (.so) olması önemli mi?
Zachary Kraus

@ZacharyKraus: Genel / özel sınıf nedir? Ve hayır, problemler farklı olsa da, üst üste binerken, kütüphanenin statik veya dinamik olarak bağlanması önemli değildir.
Marc Mutz - mmutz

@ MarcMutz-mmutz Doğru C ++ terminolojisi olmayan genel / özel sınıfı kullanma konusunda üzgünüm. Bahsettiğim şey, yukarıdaki EFraim'in çözümü. Benim versiyonumda ise, statik sınıf üyesini özel yaptım. Statik bir sınıf üyesi olarak kamuya açık veya özel olarak sahip olmanın kütüphane geliştirme ve kullanılabilirlik açısından fark yarattığını anlamaya çalışıyordum. Bağırsam bana kütüphaneyi etkilememesi gerektiğini söylüyor çünkü kullanıcılar statik sınıf üyesine ya da binanın nesnesine asla erişemeyecekler, ancak bu konuda guru'nun bilgeliğini almak isterim.
Zachary Kraus

@ZacharyKraus: Dinamik başlatma gerektiren statikle ilgili temel sorun ([basic.start.init] / 2) kod çalıştırmalarıdır. Kütüphanelerde, yıkıcılar çalıştırıldığında kütüphane kodu zaten kaldırılmış olabilir. Daha fazlasını duymak isterseniz, bunun hakkında bir soru göndermenizi öneririm.
Marc Mutz - mmutz

6

Statik yapıcılar kavramı, C ++ problemlerinden öğrendikten sonra Java'da tanıtıldı. Yani doğrudan bir eşdeğerimiz yok.

En iyi çözüm, açıkça başlatılabilen POD tiplerini kullanmaktır.
Veya statik üyelerinizi, doğru şekilde başlatacak kendi yapıcısına sahip belirli bir tür yapın.

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

Sınıf derlemeye ve kullanmaya çalışırken Elsewhere( Earwicker'in cevabından ):

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

Sınıf tanımının (CPP) dışında bir kod koymadan tamsayı olmayan türlerin statik özniteliklerini başlatmak mümkün görünmemektedir.

Bu derlemeyi yapmak için " içinde statik yerel değişkenli statik bir yöntem " kullanabilirsiniz . Bunun gibi bir şey:

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

Ayrıca, yapıcıya argümanlar iletebilir veya belirli değerlerle başlatabilirsiniz, çok esnek, güçlü ve uygulaması kolaydır ... tek şey, statik bir özellik değil, statik bir değişken içeren statik bir yönteme sahip olmanızdır ... sözdizimi biraz değişir, ancak yine de yararlıdır. Umarım bu birisi için faydalıdır,

Hugo González Castro.


İplik kullanırken dikkatli olun. GCC'de statik yerlilerin inşasının eşzamanlı yürütmeye karşı korunduğuna inanıyorum, ancak Visual C ++ 'da değildir.
Daniel Earwicker

1
C ++ 11 itibaren ve POSIX'deki, bu sahiptir evreli olması.
Marc Mutz - mmutz

Yukarıdaki diğer iki çözümü çok beğendim ( bu ve bu ), ancak sizinki, statiklerin kütüphaneler arasında gerekli sırayla başlatılmasını sağlayan tek çözümdür . Sadece sizinki gibi özel bir statik Instance yöntemim var ve doğrudan statik referanslar yerine bu Instance yöntemini kullanan genel statik erişimcilerindeki diğer değerlere erişimi kaydırıyorum. Teşekkürler.
FlintZA

Müthiş! Bu tamamlar.
Gabe Halsmer

4

Bunun basit bir çözüm olacağını düşünüyorum:

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

Ben de öyle yapıyorum.
Etherealone

1

Sadece aynı hileyi çözdüm. Singleton için tek bir statik üyenin tanımını belirtmem gerekiyordu. Ama işleri daha karmaşık hale getirin - RandClass () 'nın ctor'unu kullanmak istemediğime karar verdim ... Ayrıca benim durumuma basit bir arayüz ekledim.

İşte son kod:

Kodu basitleştirdim ve rand () işlevini ve tek tohum başlatıcı srand () kullanıyorum

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

İşte EFraim'in çözüm çeşidim; fark, örtük şablon örnekleme sayesinde, statik yapıcı yalnızca sınıf örnekleri yaratıldığında çağrılması ve .cppdosyada herhangi bir tanımlamaya gerek olmamasıdır (şablon örnekleme sihri sayesinde).

Gelen .hdosyanın şunlara sahip:

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;

Gelen .cppdosyanın, sahip olabilir:

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

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

Bunun MyClass::ayalnızca [1] satırı varsa başlatıldığına dikkat edin, çünkü bu, daha sonra somutlaştırılmasını gerektiren yapıcıyı çağırır (ve somutlaştırılmasını gerektirir) _initializer.


1

Burada, verinin anonim bir ad alanı kullanarak uygulamayı içeren dosyaya özel olduğu başka bir yöntem var. Uygulamaya özel olan arama tabloları gibi şeyler için yararlıdır:

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

Rağmen isim Ive ibiraz daha belirsiz bir şey isteyebilirsiniz, bu yüzden onları yanlışlıkla dosyada daha düşük bir yerde kullanmayın.
Jim Hunziker

1
Dürüst olmak gerekirse, herkesin uygulama dosyalarında anonim ad alanları yerine neden özel statik üyeler kullanmak isteyeceğini görmek zor.
Jim Hunziker

1

Kesinlikle şu anda kabul edilen cevap kadar karmaşık olması gerekmez (Daniel Earwicker tarafından). Sınıf gereksizdir. Bu durumda bir dil savaşına gerek yoktur.

.hpp dosyası:

vector<char> const & letters();

.cpp dosyası:

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


0

Statik üye değişkenleri, üye yöntemlerini tanımlama yönteminize benzer şekilde tanımlarsınız.

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 soru statik düz eski veri türü hakkında değildi :)
jww

0

Statik bir değişken başlatmak için, bunu bir kaynak dosyanın içinde yapmanız yeterlidir. Örneğin:

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


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

CrazyJugglerDrummer soru statik düz eski veri türü hakkında değildi :)
jww

0

C # davranışını taklit etmek için bir şablon oluşturma hakkında.

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

Burada olduğu gibi basit durumlar için, statik üye fonksiyonunun içine sarılmış bir statik değişken neredeyse iyidir. Çok basittir ve genellikle derleyiciler tarafından optimize edilir. Bu karmaşık nesneler için başlatma sırası sorununu çözmez.

#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

Bu bir çözüm mü?

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

0

Statik bir kurucu aşağıdaki gibi bir arkadaş sınıfı veya iç içe sınıf kullanılarak taklit edilebilir.

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

Çıktı:

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

Neden newbir char dizisini sadece imleci derhal sızdırmak ve üzerine yazmak için kullanıyorsunuz?
Eric

0

Vay canına, kimsenin en bariz cevaptan bahsetmediğine ve C # 'ın statik yapıcı davranışını en yakın taklit edene inanamıyorum, yani bu türün ilk nesnesi oluşturulana kadar çağrılmıyor.

std::call_once()C ++ 11'de mevcuttur; bunu kullanamazsanız, statik bir boolean sınıf değişkeni ve bir karşılaştırma ve değiştirme atomik işlemi ile yapılabilir. Yapıcınızda sen atomik Sınıf-statik bayrağı değiştirebilir olmadığını görmek falseiçin true, ve eğer öyleyse, statik-inşaat kod çalıştırabilir.

Ekstra kredi için, bir boole yerine 3 yönlü bir bayrak yapın, yani koşmayın, koşmayın ve koşmayın. Daha sonra bu sınıfın diğer tüm örnekleri, statik yapıcıyı çalıştıran örnek tamamlanana kadar (örneğin bir bellek çiti düzenleyin, sonra durumu "tamamlandı" olarak ayarlayın) kilitleyebilir. Döndürme kilidiniz işlemcinin "duraklatma" talimatını uygulamalı, bir eşik değere kadar her seferinde beklemeyi iki katına çıkarmalıdır. - Oldukça standart dönüş kilitleme tekniği.

C ++ 11 yokluğunda, bu başlamanız gerekir.

İşte size yol gösterecek bazı sahte kodlar. Bunu sınıf tanımınıza ekleyin:

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

Ve bu yapıcıda:

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();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.