C ++의 정적 생성자? 개인 정적 객체를 초기화해야합니다


176

개인 정적 데이터 멤버 (모든 문자 az를 포함하는 벡터)가있는 클래스를 갖고 싶습니다. Java 또는 C #에서는 클래스의 인스턴스를 만들기 전에 실행될 "정적 생성자"를 만들고 클래스의 정적 데이터 멤버를 설정할 수 있습니다. 변수는 읽기 전용이고 한 번만 설정하면되므로 한 번만 실행되며 클래스의 함수이므로 개인 멤버에 액세스 할 수 있습니다. 벡터가 초기화되었는지 확인하고 그렇지 않은 경우 초기화하는 코드를 생성자에 추가 할 수 있지만 필요한 검사가 많이 발생하고 문제에 대한 최적의 솔루션처럼 보이지 않습니다.

변수는 읽기 전용이기 때문에 공개 정적 const 일 수 있으므로 클래스 외부에서 한 번 설정할 수 있지만 다시 한 번 추한 해킹처럼 보입니다.

인스턴스 생성자에서 초기화하지 않으려는 경우 클래스에 개인 정적 데이터 멤버를 가질 수 있습니까?



1
@CiroSantilli 新疆 改造 中心 六四 事件 法轮功이 질문은 개인 정적 기본 유형의 상수 값을 설정하지 않고 개인 정적 객체 를 초기화하는 코드 실행에 중점을 둡니다 . 솔루션이 다릅니다.
Gordon Gustafson

아, 당신이 옳다고 생각합니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

답변:


180

정적 생성자와 동등한 것을 얻으려면 정적 데이터를 보유하기 위해 별도의 일반 클래스를 작성한 다음 해당 일반 클래스의 정적 인스턴스를 만들어야합니다.

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
감사! 그 모든 것을 해야하는 것은 매우 성가신 일입니다. 많은 "실수"C # 및 Java 중 하나가 배웠습니다.
Gordon Gustafson

109
예. 나는 항상 사람들에게 C ++이 그러한 "실수"를 모두하지 않았다면 다른 언어로 만들었을 것이라고 지적한다. 너무 많은 근거를 다루고 실수를 저지르는 C ++은 그 뒤를 따르는 언어에 좋았습니다.
quark

11
정적 객체에 대한 생성자가 실행될 때 아무도 생성자를 사용하지 않기 때문에 단 하나의 뉘앙스가 있습니다. 잘 알려진 훨씬 안전한 방법은 클래스 Elsewhere입니다. {StaticStuff & get_staticStuff () {static StaticStuff staticStuff; // 누군가가 처음 필요할 때 생성자가 한 번 실행됩니다. staticStuff; }}; C # 및 Java의 정적 생성자가 위 코드와 동일한 보장을 제공 할 수 있는지 궁금합니다.
Oleg Zhylin

13
@Oleg : 그렇습니다. 표준은 main이 입력되기 전에 모든 비 로컬 변수의 생성자가 실행된다는 것을 보증합니다. 또한 컴파일 단위 내에서 구성 순서가 잘 정의되어 있고 컴파일 단위 내의 선언과 동일한 순서를 보장합니다. 불행히도 여러 컴파일 단위에서 순서를 정의하지 않습니다.
마틴 요크

13
이것은 실제로 friend클래스 ElsewhereStaticStuff내부에 쉽게 액세스 할 수 있도록 많은 의미가 있는 경우입니다 (위험한 방식으로 캡슐화를 중단하지 않고 추가 할 수 있음).
Konrad Rudolph

81

당신은 할 수 있습니다

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

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

.cpp에서 이것을 잊지 마십시오.

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

프로그램은 여전히 ​​두 번째 줄없이 연결되지만 초기화 프로그램은 실행되지 않습니다.


+1 (시도하지 않은) 그러나 : ctor _init._init ()는 언제 호출됩니까? 정적 MyClass 객체가있을 때 MyClass의 ctor 전후에? 당신이 말할 수없는 것 같아요 ...
ur.

2
안녕하세요,이 "초기화"마법에 대한 자세한 정보는 어디에서 찾을 수 있습니까?
Karel Bílek

MyClass::a.push_back(i)대신 해서는 안 a.push_back(i)됩니까?
Neel Basu

4
@ur .: _initializer는의 하위 객체입니다 MyClass. 서브 오브젝트는 다음 순서로 초기화됩니다. 가상 기본 클래스 서브 오브젝트, 깊이 우선, 왼쪽에서 오른쪽 순서 (단, 각 개별 서브 오브젝트를 한 번만 초기화). 그런 다음 깊이 우선, 왼쪽에서 오른쪽 순서로 일반 기본 클래스 하위 오브젝트; 그런 다음 선언 순서대로 멤버 하위 오브젝트를 작성하십시오. 따라서 코드 _initialiser는 이전에 선언 된 멤버 만 참조 하면 EFraim의 전략을 사용하는 것이 안전 합니다.
j_random_hacker

2
저자가 두 번째 코드 클립에서 필수적인 초기화를 언급했기 때문에이 대답은 허용 된 것보다 낫습니다.
Jeff T.

33

C ++ 11 솔루션

C ++ 11부터는 람다 식 을 사용 하여 정적 클래스 멤버를 초기화 할 수 있습니다 . 이것은 다양한 정적 멤버 사이에 구성 순서를 적용해야하거나 정적 멤버가 인 경우에도 작동합니다 const.

헤더 파일 :

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

소스 파일:

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

흥미로운 해결책. 이 경우 예외를 던지면 누가 잡을 수 있습니까?
rafi wiener

5
정적 프로그램 초기화 코드는 예외를 발생 시키지 않아야합니다 . 그렇지 않으면 프로그램이 중단됩니다. try catch예외가 발생할 수있는 경우 초기화 로직을 블록 으로 래핑해야합니다 .
emkey08

19

.h 파일에서 :

class MyClass {
private:
    static int myValue;
};

.cpp 파일에서 :

#include "myclass.h"

int MyClass::myValue = 0;

5
이것은 유형에 관계없이 개별 정적 멤버에 적합합니다. 정적 생성자와 비교할 때 부족한 점 은 다양한 정적 멤버간에 순서 를 부과 할 수 없다는 것 입니다. 그렇게해야한다면 Earwicker의 답변을 참조하십시오.
quark

나는 정확히 그렇게하고 있지만 여전히 컴파일되지 않습니다. 그리고 이것은 이것이 문제 영역이라고 말합니다 (헤더가 아닌 생성자에서)
Flotolk

14

다음은 Konrad Rudolph의 친구 클래스 제안을 사용하여 Daniel Earwicker와 유사한 또 다른 접근법입니다. 여기서는 내부 개인 친구 유틸리티 클래스를 사용하여 기본 클래스의 정적 멤버를 초기화합니다. 예를 들면 다음과 같습니다.

헤더 파일 :

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

구현 파일 :

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

이 접근 방식은 Initializer 클래스를 외부 세계에서 완전히 숨기고 클래스에 포함 된 모든 항목을 초기화하는 이점이 있습니다.


+1 구현을 자체 파일로 유지하는 예제를 제공합니다.
Andrew Larsson

1
또한 ToBeInitialized::Initializer::Initializer()호출 되는지 확인 해야하므로 ToBeInitialized::Initializer ToBeInitialized::initializer;구현 파일 에 추가해야 합니다. 나는 당신의 아이디어와 EFraim의 아이디어에서 몇 가지를 취했고, 그것이 내가 필요로하고 깨끗하게 보이는 것처럼 정확하게 작동합니다. 고마워
Andrew Larsson

11

Test::StaticTest() 전역 정적 초기화 중에 정확히 한 번 호출됩니다.

호출자는 정적 생성자가 될 함수에 한 줄만 추가하면됩니다.

static_constructor<&Test::StaticTest>::c;c전역 정적 초기화 중 초기화를 강제합니다 .

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

이것은 환상적인 솔루션입니다. 나는 Douglas Mandel의 답변 도 정말로 좋아 하지만 이것은 더 간결합니다.
FlintZA

정말 놀랍습니다!
nh_

9

init()기능이 필요하지 않으며 std::vector범위에서 만들 수 있습니다.

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

그러나 클래스 유형의 정적 요소는 라이브러리에서 문제를 일으키므로 피해야합니다.

C ++ 11 업데이트

C ++ 11부터는 다음과 같이 할 수 있습니다.

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

원래 답변의 C ++ 98 솔루션과 의미 상 동일하지만 오른쪽에 문자열 리터럴을 사용할 수 없으므로 완전히 우수하지는 않습니다. 당신이 다른 유형의 벡터이있는 경우 그러나,보다 char, wchar_t, char16_t또는 char32_t(문자열 리터럴로 기록 될 수있는 배열), 엄격하게 다른 구문을 도입하지 않고 상용구 코드를 제거합니다 C ++ 11 버전은 C ++ 98에 비해 버전.


나는 그것을 좋아한다. 우리가 지금 쓸모없는 알파벳없이 한 줄로 할 수 있다면.
Martin York

라이브러리에 문제를 일으키는 이유는 정적 클래스가 개인용인지 공개 형인지 중요합니까? 또한 라이브러리가 static (.a) 또는 dynamic (.so)인지 여부가 중요합니까?
Zachary Kraus

@ZacharyKraus : 공개 / 개인 클래스 란 무엇입니까? 그리고 아닙니다. 문제는 다르지만 중복되지만 라이브러리가 정적으로 또는 동적으로 연결되어 있는지는 중요하지 않습니다.
Marc Mutz-mmutz

@ MarcMutz-mmutz 올바른 C ++ 용어가 아닌 공개 / 개인 클래스를 사용하여 죄송합니다. 내가 언급 한 것은 위의 EFraim의 솔루션입니다. 내 버전에서는 정적 클래스 멤버를 비공개로 만들었습니다. 정적 또는 정적 클래스 멤버가 공용 또는 개인으로 라이브러리 개발 및 유용성에 차이가 있는지 이해하려고했습니다. 내 직감은 사용자가 정적 클래스 멤버 또는 해당 건물에 액세스 할 수 없기 때문에 라이브러리에 영향을 미치지 않아야한다고 말하지만이 주제에 대한 전문가의 지혜를 얻고 싶습니다.
Zachary Kraus

@ZacharyKraus : 동적 초기화 ([basic.start.init] / 2)가 필요한 정적의 주요 문제는 코드를 실행한다는 것입니다. 라이브러리에서 소멸자가 실행될 때 라이브러리 코드가 이미 언로드되었을 수 있습니다. 더 듣고 싶다면 질문을 올리는 것이 좋습니다.
Marc Mutz-mmutz

6

정적 생성자 개념은 C ++의 문제점을 알게 된 후 Java로 도입되었습니다. 따라서 우리는 직접적으로 동등한 것이 없습니다.

가장 좋은 해결책은 명시 적으로 초기화 할 수있는 POD 유형을 사용하는 것입니다.
또는 정적 멤버를 올바르게 초기화하는 자체 생성자가있는 특정 유형으로 만드십시오.

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

컴파일하고 클래스를 사용 하려고 할 때 Elsewhere( Earwicker의 답변에서 ) 다음을 얻습니다.

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

CPP (클래스 정의) 외부에 일부 코드를 넣지 않으면 정수가 아닌 유형의 정적 속성을 초기화 할 수 없습니다.

컴파일하려면 " 내부에 정적 로컬 변수가있는 정적 메소드 "를 대신 사용할 수 있습니다. 이 같은:

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

또한 생성자에 인수를 전달하거나 특정 값으로 초기화 할 수 있습니다. 매우 유연하고 강력하며 구현하기 쉽습니다 ... 유일한 것은 정적 속성이 아닌 정적 변수를 포함하는 정적 메서드가 있다는 것입니다. 구문은 약간 변경되지만 여전히 유용합니다. 이것이 누군가에게 유용하기를 바랍니다.

휴고 곤잘레스 카스트로


스레드를 사용하는 경우주의하십시오. GCC에서는 정적 로컬 구성이 동시 실행으로부터 보호되지만 Visual C ++에서는 그렇지 않다고 생각합니다.
Daniel Earwicker

1
C에서 + + 11 이후, 및 POSIX, 그것은 스레드 안전합니다.
Marc Mutz-mmutz

위의 두 가지 다른 솔루션 ( thisthis )을 매우 좋아 했지만 라이브러리 전체에서 필요한 순서로 정적 초기화를 보장하는 유일한 솔루션 입니다. 위와 같은 개인 정적 인스턴스 메소드가 있으며 직접 참조 대신 해당 인스턴스 메소드를 사용하는 공용 정적 접근 자의 다른 값에 대한 액세스를 래핑합니다. 감사.
FlintZA

대박! 이것으로 완료됩니다.
Gabe Halsmer

4

이것에 대한 간단한 해결책은 다음과 같습니다.

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

이것도 내가하는 방법입니다.
Etherealone

1

같은 속임수를 해결했습니다. Singleton에 대한 단일 정적 멤버의 정의를 지정해야했습니다. 그러나 일을 더 복잡하게 만드십시오-RandClass ()의 ctor를 사용하지 않는 한 ctor를 호출하지 않기로 결정했습니다 ... 그래서 내 코드에서 전역 적으로 싱글 톤을 초기화하고 싶지 않았습니다. 또한 필자의 경우 간단한 인터페이스를 추가했습니다.

최종 코드는 다음과 같습니다.

코드를 단순화하고 rand () 함수와 단일 시드 이니셜 라이저 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

다음은 EFraim 솔루션의 변형입니다. 차이점은 암시 적 템플릿 인스턴스화 덕분에 클래스의 인스턴스가 생성 된 경우에만 정적 생성자가 호출되고.cpp 파일에 가 필요 마법 입니다.

에서 .h파일, 당신은 :

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;

에서 .cpp파일, 당신은 할 수 있습니다 :

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

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

참고 MyClass::a그 다음 호출의 인스턴스를 필요 생성자 (및 인스턴스화 필요) 때문에, 라인 [1]이있는 경우에만 초기화된다 _initializer.


1

익명 네임 스페이스를 사용하여 구현이 포함 된 파일의 벡터가 개인용 인 다른 방법이 있습니다. 구현 전용 인 룩업 테이블과 같은 것들에 유용합니다.

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

당신이 이름을 지정할 수 있지만 Ii실수로 사용하지 않도록 좀 더 애매한 뭔가 어딘가에 파일에 내립니다.
Jim Hunziker

1
솔직히 말해서 왜 누군가가 구현 파일에서 익명의 네임 스페이스가 아닌 개인 정적 멤버를 사용하려고하는지 알기가 어렵습니다.
Jim Hunziker

1

현재 받아 들여지는 답변 (Daniel Earwicker의 답변)만큼 복잡 할 필요는 없습니다. 수업은 불필요합니다. 이 경우 언어 전쟁이 필요하지 않습니다.

.hpp 파일 :

vector<char> const & letters();

.cpp 파일 :

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


0

멤버 메소드를 정의하는 방식과 유사하게 정적 멤버 변수를 정의합니다.

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 질문은 정적 평범한 오래된 데이터 유형에 관한 것이 아닙니다 :)
jww

0

정적 변수를 초기화하려면 소스 파일 내부에서 수행하십시오. 예를 들면 다음과 같습니다.

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


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

CrazyJugglerDrummer 질문은 정적 평범한 오래된 데이터 유형에 관한 것이 아닙니다 :)
jww

0

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

여기와 같은 간단한 경우 정적 멤버 함수 안에 싸인 정적 변수가 거의 좋습니다. 간단하고 일반적으로 컴파일러에 의해 최적화됩니다. 그래도 복잡한 객체의 초기화 순서 문제는 해결되지 않습니다.

#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

이것이 해결책입니까?

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

0

정적 생성자는 아래와 같이 friend 클래스 또는 중첩 클래스를 사용하여 에뮬레이션 할 수 있습니다.

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

산출:

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

new포인터를 즉시 누설하고 덮어 쓰기 위해 char 배열을 사용하고 있습니까 ?
에릭

0

와우, 나는 가장 명백한 대답을 언급 한 사람을 믿을 수 없으며 C #의 정적 생성자 동작을 가장 가깝게 모방 한 사람, 즉 해당 유형의 첫 번째 객체가 생성 될 때까지 호출되지 않습니다.

std::call_once()C ++ 11에서 사용 가능합니다. 그것을 사용할 수 없다면 정적 부울 클래스 변수와 비교 및 ​​교환 원자 연산으로 수행 할 수 있습니다. 생성자에서 클래스 정적 플래그를에서 false로 변경할 수 true있는지 확인하고, 그렇다면 정적 생성 코드를 실행할 수 있습니다.

추가 크레딧을 얻으려면 부울 대신 3 방향 플래그로 설정하십시오 (예 : 달리기, 달리기 및 실행 완료). 그런 다음 정적 생성자를 실행하는 인스턴스가 완료 될 때까지 해당 클래스의 다른 모든 인스턴스를 스핀 잠금 할 수 있습니다 (예 : 메모리 울타리를 실행 한 후 상태를 "실행 완료"로 설정). 스핀 락은 프로세서의 "일시 정지"명령을 실행하고 임계 값 등이 올 때마다 대기 시간을 두 배로 늘려야합니다. 표준 스핀 락킹 기술입니다.

C ++ 11 없는 경우 시작해야합니다.

여기 당신을 안내하는 의사 코드가 있습니다. 이것을 클래스 정의에 넣으십시오.

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

그리고 이것은 당신의 생성자에서 :

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();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.