konstruktor statis di C ++? Saya perlu menginisialisasi objek statis pribadi


176

Saya ingin memiliki kelas dengan anggota data statis pribadi (vektor yang berisi semua karakter az). Dalam java atau C #, saya bisa membuat "static constructor" yang akan berjalan sebelum saya membuat instance kelas apa pun, dan mengatur anggota data statis kelas. Hanya dijalankan sekali (karena variabel hanya dibaca dan hanya perlu diatur sekali) dan karena ini merupakan fungsi dari kelas, ia dapat mengakses anggota privatnya. Saya bisa menambahkan kode dalam konstruktor yang memeriksa untuk melihat apakah vektor diinisialisasi, dan menginisialisasi jika tidak, tetapi itu memperkenalkan banyak pemeriksaan yang diperlukan dan sepertinya bukan solusi optimal untuk masalah tersebut.

Pikiran muncul pada saya bahwa karena variabel hanya akan dibaca, mereka hanya bisa menjadi public static const, jadi saya dapat mengaturnya sekali di luar kelas, tetapi sekali lagi, sepertinya semacam peretasan yang jelek.

Apakah mungkin untuk memiliki anggota data statis pribadi di kelas jika saya tidak ingin menginisialisasi mereka dalam konstruktor instance?



1
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功 Pertanyaan ini difokuskan pada menjalankan kode untuk menginisialisasi objek statis privat , bukan menetapkan nilai konstan tipe primitif statis privat. Solusinya berbeda.
Gordon Gustafson

ah, saya pikir Anda benar, menarik diri.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Jawaban:


180

Untuk mendapatkan yang setara dengan konstruktor statis, Anda perlu menulis kelas biasa yang terpisah untuk menampung data statis dan kemudian membuat contoh statis dari kelas biasa itu.

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
Terima kasih! meskipun itu sangat menjengkelkan harus melakukan semua itu. Salah satu dari banyak "kesalahan" yang dipelajari C # dan java.
Gordon Gustafson

109
Iya. Saya selalu menunjukkan kepada orang-orang bahwa jika C ++ tidak membuat semua "kesalahan" itu maka bahasa lain harus membuatnya. C ++ mencakup begitu banyak landasan, bahkan membuat kesalahan, sangat bagus untuk bahasa yang mengikutinya.
quark

11
Hanya satu nuansa kecil, karena konstruktor ikut bermain, tidak ada yang menjamin ketika konstruktor untuk objek statis dijalankan. Pendekatan yang jauh lebih aman dikenal adalah kelas Di tempat lain {StaticStuff & get_staticStuff () {static StaticStuff staticStuff; // constructor berjalan sekali, ketika seseorang pertama kali membutuhkannya mengembalikan staticStuff; }}; Aku ingin tahu apakah konstruktor statis dalam C # dan Java dapat memberikan jaminan yang sama seperti kode di atas ...
Oleg Zhylin

13
@ Oleg: Ya, benar. Jaminan standar bahwa konstruktor untuk semua variabel non lokal dieksekusi sebelum utama dimasukkan. Ini juga menjamin bahwa dalam unit kompilasi, urutan konstruksi didefinisikan dengan baik dan urutan yang sama dengan deklarasi dalam unit kompilasi. Sayangnya mereka tidak mendefinisikan urutan di beberapa unit kompilasi.
Martin York

13
Ini sebenarnya adalah kasus di mana friendbanyak akal sehingga kelas Elsewheredapat dengan mudah mengakses StaticStuffinternal (tanpa merusak enkapsulasi dengan cara berbahaya, saya dapat menambahkan).
Konrad Rudolph

81

Yah, Anda bisa

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

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

Jangan lupa (dalam .cpp) ini:

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

Program masih akan terhubung tanpa baris kedua, tetapi initializer tidak akan dieksekusi.


+1 (tidak mencobanya) Tapi: Kapan ctor _init._init () dipanggil? Sebelum atau sesudah ctor MyClass ketika saya memiliki objek MyClass statis? Saya kira Anda tidak tahu ...
.

2
halo, di mana saya dapat menemukan lebih banyak tentang sihir "penginisialisasi" ini?
Karel Bílek

Bukankah seharusnya MyClass::a.push_back(i)bukan a.push_back(i)?
Neel Basu

4
@ur .: _initializeradalah sebuah subobjek dari MyClass. Subobyek diinisialisasi dalam urutan ini: subobjek kelas dasar virtual, dalam urutan-dalam, urutan kiri-ke-kanan (tetapi hanya menginisialisasi setiap sub-proyek yang berbeda sekali); kemudian sub-objek kelas dasar, dalam urutan pertama, kiri-ke-kanan; kemudian sub-proyek anggota dalam urutan deklarasi. Jadi aman menggunakan strategi EFraim, asalkan kode itu _initialiserhanya merujuk pada anggota yang dideklarasikan sebelumnya.
j_random_hacker

2
Jawaban ini lebih baik daripada yang diterima karena penulis menyebutkan inisialisasi yang sangat diperlukan pada klip kode kedua.
Jeff T.

33

Solusi C ++ 11

Sejak C ++ 11, Anda cukup menggunakan ekspresi lambda untuk menginisialisasi anggota kelas statis. Ini bahkan berfungsi jika Anda perlu memaksakan urutan konstruksi antara berbagai anggota statis, atau jika Anda memiliki anggota statis const.

File tajuk:

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

Sumber data:

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

solusi menarik. dalam hal ini jika saya melempar pengecualian siapa yang bisa menangkapnya?
rafi wiener

5
Kode inisialisasi program statis tidak boleh membuang pengecualian, atau program akan macet. Anda harus membungkus logika penginisialisasi ke try catchblok jika pengecualian mungkin dilemparkan.
emkey08

19

Dalam file .h:

class MyClass {
private:
    static int myValue;
};

Dalam file .cpp:

#include "myclass.h"

int MyClass::myValue = 0;

5
Ini berfungsi dengan baik untuk setiap anggota statis (terlepas dari jenisnya). Kekurangan dibandingkan dengan konstruktor statis adalah bahwa Anda tidak dapat memaksakan urutan antara berbagai anggota statis. Jika Anda perlu melakukan itu, lihat jawaban Earwicker.
quark

Saya melakukan hal itu, tetapi masih belum bisa dikompilasi. Dan ia mengatakan ini adalah area masalah (dalam konstruktor, bukan header)
Flotolk

14

Berikut adalah pendekatan lain yang mirip dengan Daniel Earwicker, juga menggunakan saran teman kelas Konrad Rudolph. Di sini kami menggunakan kelas utilitas teman dalam pribadi untuk menginisialisasi anggota statis kelas utama Anda. Sebagai contoh:

File tajuk:

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

File implementasi:

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

Pendekatan ini memiliki keuntungan menyembunyikan kelas Initializer dari dunia luar, menjaga agar semua yang ada dalam kelas diinisialisasi.


+1 Untuk memberi contoh yang membuat implementasi dalam file sendiri.
Andrew Larsson

1
Juga, Anda harus memastikan bahwa ToBeInitialized::Initializer::Initializer()dipanggil, jadi Anda perlu menambahkan ToBeInitialized::Initializer ToBeInitialized::initializer;ke file implementasi. Saya mengambil beberapa hal dari ide Anda dan dari ide EFraim, dan itu berfungsi persis seperti yang saya butuhkan dan terlihat bersih. Terima kasih sobat.
Andrew Larsson

11

Test::StaticTest() disebut tepat sekali selama inisialisasi global statis.

Pemanggil hanya perlu menambahkan satu baris ke fungsi yang menjadi konstruktor statis mereka.

static_constructor<&Test::StaticTest>::c;memaksa inisialisasi cselama inisialisasi statis global.

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

Ini adalah solusi yang fantastis. Saya benar-benar menyukai jawaban Douglas Mandel , tetapi ini bahkan lebih ringkas.
FlintZA

Ini sungguh menakjubkan!
nh_

9

Tidak perlu suatu init()fungsi, std::vectordapat dibuat dari rentang:

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

Perhatikan, bagaimanapun, bahwa statika tipe kelas menyebabkan masalah di perpustakaan, sehingga mereka harus dihindari di sana.

Pembaruan C ++ 11

Pada C ++ 11, Anda dapat melakukan ini sebagai gantinya:

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

Secara semantik setara dengan solusi C ++ 98 dalam jawaban asli, tetapi Anda tidak bisa menggunakan string literal di sisi kanan, jadi itu tidak sepenuhnya unggul. Namun, jika Anda memiliki vektor dari jenis lain dari char, wchar_t, char16_tatau char32_t(array yang dapat ditulis sebagai string literal), versi C ++ 11 ketat akan menghapus kode boilerplate tanpa memperkenalkan sintaks lainnya, dibandingkan dengan C ++ 98 Versi: kapan.


Saya suka itu. Padahal kalau saja kita bisa melakukannya dalam satu baris tanpa alfabet yang sekarang tidak berguna.
Martin York

Untuk menyebabkan masalah dengan perpustakaan, apakah masalah jika kelas statis adalah privat atau publik? Selain itu, apakah masalah jika perpustakaan statis (.a) atau dinamis (.so)?
Zachary Kraus

@ZacharyKraus: apa a / public private kelas ? Dan tidak, sementara masalahnya berbeda, tetapi tumpang tindih, tidak masalah apakah perpustakaan dihubungkan secara statis atau dinamis.
Marc Mutz - mmutz

@ MarcMutz-mmutz Maaf tentang penggunaan kelas publik / swasta yang tidak benar terminologi C ++. Yang saya maksudkan adalah solusi dari EFraim di atas. Namun dalam versi saya, saya menjadikan privat anggota kelas statis. Saya mencoba memahami jika memiliki anggota kelas statis sebagai publik atau pribadi membuat perbedaan dalam pengembangan dan kegunaan perpustakaan. Naluri saya mengatakan kepada saya bahwa seharusnya tidak mempengaruhi perpustakaan karena pengguna tidak akan pernah memiliki akses baik ke anggota kelas statis atau objek yang dibangunnya, tetapi saya akan senang mendapatkan kebijaksanaan guru tentang topik ini.
Zachary Kraus

@ZacharyKraus: Masalah utama dengan statika yang memerlukan inisialisasi dinamis ([basic.start.init] / 2) adalah mereka menjalankan kode. Di pustaka, bisa jadi kode pustaka telah diturunkan saat destruktor dijalankan. Jika Anda ingin mendengar lebih banyak, saya sarankan untuk mengirim pertanyaan tentang itu.
Marc Mutz - mmutz

6

Konsep konstruktor statis diperkenalkan di Jawa setelah mereka belajar dari masalah di C ++. Jadi kami tidak memiliki padanan langsung.

Solusi terbaik adalah dengan menggunakan tipe POD yang dapat diinisialisasi secara eksplisit.
Atau buat anggota statis Anda tipe tertentu yang memiliki konstruktor sendiri yang akan menginisialisasi dengan benar.

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

Ketika mencoba mengkompilasi dan menggunakan kelas Elsewhere(dari jawaban Earwicker ) saya mendapatkan:

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

Tampaknya tidak mungkin untuk menginisialisasi atribut statis tipe non-integer tanpa meletakkan beberapa kode di luar definisi kelas (CPP).

Untuk membuat kompilasi itu, Anda dapat menggunakan " metode statis dengan variabel lokal statis di dalamnya ". Sesuatu seperti ini:

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

Dan Anda juga dapat meneruskan argumen ke konstruktor atau menginisialisasi dengan nilai-nilai spesifik, sangat fleksibel, kuat, dan mudah diterapkan ... satu-satunya adalah Anda memiliki metode statis yang berisi variabel statis, bukan atribut statis ... sintaksis sedikit berubah, tetapi masih bermanfaat. Semoga ini bermanfaat bagi seseorang,

Hugo González Castro.


Padahal berhati-hatilah jika menggunakan utas. Saya percaya pada GCC konstruksi penduduk lokal statis dilindungi terhadap eksekusi bersamaan, tetapi dalam Visual C ++ tidak.
Daniel Earwicker

1
Dari C ++ 11 dan seterusnya, dan dalam POSIX, itu memiliki menjadi benang-aman.
Marc Mutz - mmutz

Saya cukup menyukai dua solusi lain di atas ( ini dan ini ), tetapi Anda adalah satu-satunya yang memastikan inisialisasi statika dalam urutan yang mereka butuhkan di perpustakaan. Saya hanya punya metode Instance statis privat seperti milik Anda di atas, dan bungkus akses ke nilai lain di accessors statis publik yang menggunakan metode Instance itu alih-alih referensi langsung. Terima kasih.
FlintZA

Luar biasa! Ini menyelesaikannya.
Gabe Halsmer

4

Saya kira solusi sederhana untuk ini adalah:

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

Ini adalah bagaimana saya melakukannya juga.
Etherealone

1

Baru saja menyelesaikan trik yang sama. Saya harus menentukan definisi anggota statis tunggal untuk Singleton. Tetapi membuat segalanya menjadi lebih rumit - saya telah memutuskan bahwa saya tidak ingin memanggil ctor dari RandClass () kecuali saya akan menggunakannya ... itu sebabnya saya tidak ingin menginisialisasi singleton secara global dalam kode saya. Saya juga telah menambahkan antarmuka sederhana dalam kasus saya.

Ini kode terakhir:

Saya menyederhanakan kode dan menggunakan fungsi rand () dan satu-satunya initializer seed 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

Inilah varian solusi EFraim saya; perbedaannya adalah bahwa, berkat instantiasi templat implisit, konstruktor statis hanya dipanggil jika instance kelas dibuat, dan bahwa tidak ada definisi dalam .cppfile yang diperlukan (berkat keajaiban instantiasi templat).

Dalam .hfile, Anda memiliki:

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;

Dalam .cppfile tersebut, Anda dapat memiliki:

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

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

Catatan yang MyClass::adiinisialisasi hanya jika baris [1] ada di sana, karena itu memanggil (dan membutuhkan instantiasi) konstruktor, yang kemudian memerlukan instantiasi _initializer.


1

Berikut metode lain, di mana vektor bersifat pribadi untuk file yang berisi implementasi dengan menggunakan namespace anonim. Ini berguna untuk hal-hal seperti tabel pencarian yang bersifat pribadi untuk implementasi:

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

Meskipun Anda mungkin ingin memberi nama Idan isesuatu yang sedikit lebih tidak jelas sehingga Anda tidak sengaja menggunakannya di tempat yang lebih rendah dalam file.
Jim Hunziker

1
Sejujurnya, sulit untuk melihat mengapa ada orang yang ingin menggunakan anggota statis privat daripada ruang nama anonmymous dalam file implementasi.
Jim Hunziker

1

Tentu saja tidak perlu serumit jawaban yang diterima saat ini (oleh Daniel Earwicker). Kelasnya berlebihan. Tidak perlu untuk perang bahasa dalam hal ini.

file .hpp:

vector<char> const & letters();

file .cpp:

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


0

Anda mendefinisikan variabel anggota statis sama dengan cara Anda mendefinisikan metode anggota.

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
Pertanyaan CrazyJugglerDrummer bukan tentang tipe data lama polos statis :)
jww

0

Untuk menginisialisasi variabel statis, Anda cukup melakukannya di dalam file sumber. Sebagai contoh:

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


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

Pertanyaan CrazyJugglerDrummer bukan tentang tipe data lama polos statis :)
jww

0

Bagaimana dengan membuat template untuk meniru perilaku 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
    };
};

0

Untuk kasus sederhana seperti di sini variabel statis yang dibungkus dengan fungsi anggota statis hampir sama baiknya. Ini sederhana dan biasanya akan dioptimalkan oleh kompiler. Ini tidak memecahkan masalah urutan inisialisasi untuk objek kompleks sekalipun.

#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

Apakah ini solusi?

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

0

Konstruktor statis dapat ditiru dengan menggunakan kelas teman atau kelas bersarang seperti di bawah ini.

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

Keluaran:

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

Mengapa Anda newmenggunakan array char hanya untuk segera membocorkan pointer dan menimpanya !?
Eric

0

Wow, saya tidak percaya tidak ada yang menyebutkan jawaban yang paling jelas, dan yang paling mirip meniru perilaku statis-konstruktor C #, yaitu tidak dipanggil sampai objek pertama dari jenis itu dibuat.

std::call_once()tersedia dalam C ++ 11; jika Anda tidak dapat menggunakannya, itu dapat dilakukan dengan variabel kelas boolean statis, dan operasi atom-bandingkan dan pertukaran. Di konstruktor Anda, lihat apakah Anda dapat mengubah secara statis flag kelas statis dari falsemenjadi true, dan jika demikian, Anda dapat menjalankan kode konstruksi statis.

Untuk kredit tambahan, buatlah bendera 3 arah, bukan boolean, yaitu tidak berjalan, berlari, dan selesai berlari. Kemudian semua instance lain dari kelas itu dapat berputar-kunci sampai instance yang menjalankan static-constructor telah selesai (yaitu mengeluarkan memory-pagar, kemudian mengatur status ke "done running"). Spin-lock Anda harus menjalankan instruksi "jeda" prosesor, gandakan tunggu setiap kali hingga ambang, dll. - teknik spin-lock yang cukup standar.

Dengan tidak adanya C ++ 11, ini akan membantu Anda memulai.

Inilah beberapa kodesemu untuk memandu Anda. Masukkan ini dalam definisi kelas Anda:

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

Dan ini di konstruktor Anda:

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();
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.