2011-10-15 2 views
3

오늘 완전히 이상한 문제가 발생하여 완전히 이해하지 못했습니다. 희망을 갖고 여기 누군가가 도울 수 있기를 바랍니다.정적 클래스의 초기화되지 않은 정적 데이터 멤버

설정이 매우 쉽습니다. std :: set 유형의 정적 멤버가있는 클래스가 있습니다. 클래스에는 인수의 수가 다른 2 개의 템플릿 생성자가 있습니다. 두 생성자 모두 동일한 동작이므로 생성자가 templatized이며 생성자가 std :: set에 검색 및 삽입한다는 점에 유의하십시오.

다음과 같은 문제가 발생합니다. 클래스의 정적 인스턴스의 경우 정적 std :: set (find())에서 호출되는 첫 번째 메서드에서 생성자가 충돌합니다. 세트가 초기화되지 않은 것 같습니다. 정적 멤버 변수가 초기화되기 전에 생성자가 호출되는 것처럼 보입니다.

////////// Header File 

class ConVar : public IListener 
{ 
    friend EventHandler; // Event Handler auto registers all instances of convar to commands 

public: // Auto 

    template< typename T > 
    ConVar(string const& name, string const& description, T const& default_value); 

private: 
    static std::set<u32> mRegisteredVars; 
}; 


//////// INL file (included from header) 

template< typename T > 
ConVar::ConVar(string const& name, string const& description, T const& default_value) 
    : mName(name), 
    mhName(name), 
    mDescription(description), 
    mClamp(false) 
{ 
    u32 hname = CONSTHASH(name.c_str()); 
    ErrorIf(mRegisteredVars.find(hname) != mRegisteredVars.end(), "Attempt to create same ConVar multiple times. Note the ConVars are static singletons!"); 

    *this = default_value; 

    mRegisteredVars.insert(hname); 

    gCore.Events.Subscribe(mhName, this); 
    } 

    ///////////// .cpp file 

    std::set<u32> ConVar::mRegisteredVars; 

충돌이 발견 방법에 ErrorIf 내부 발생

여기서 간단한 예이다. 그 줄에 주석을 달면 삽입되는 줄에서 충돌합니다.

생성자가 main (클래스의 정적 인스턴스) 전에 호출되었습니다 여기에서 무슨 일이 벌어 질지 아는 사람이 있습니까?

+0

우리는 그냥 추측하고있는 경우 외에는 동작을 보여주는 완전히 실행되는 예제가 필요합니다. 그러나 당신이 초기화 문제의 글로벌 질서를 치는 것처럼 들린다. 전역 범위에'ConVar '유형의 객체를 만드시겠습니까? –

답변

3

생성자에서 서로 액세스하는 전역 개체는 해당 인스턴스의 순서에 문제가 있습니다.

사용 어디에 그런

//Change 
static std::set<u32> mRegisteredVars; 

//Into 
static std::set<u32>& getRegisteredVarsSet() 
{ 
    static std::set<u32>& mRegisteredVars; 
    return mRegisteredVars; 
} 
// Obviously remove the `std::set<u32> ConVar::mRegisteredVars;` 
// From the cpp file. 

을 시도해보십시오 : mRegisteredVars 변경 이제 정적의 생성자에서 mRegisteredVars에 액세스하는 경우에도

getRegisteredVarsSet()

이 주위에 몇 가지 방법이 있습니다 저장 기간 객체에서 getRegisteredVarsSet() 호출 (검색)을 사용하면 mRegisteredVars가 반환되기 전에 완전히 초기화되어 사용할 수 있습니다.

함수의 정적 멤버이기 때문에 수명이 프로그램의 길이이므로 호출간에 상태가 유지됩니다.

3

main이 호출되기 전에 정적/전역 변수에 액세스하지 마십시오. 이것을 "Static Initialization Order Fiasco"이라고합니다.

mRegisteredVars은 그 시점에서 단순히 종료되지 않습니다. 당신이하는 일은 정의되지 않은 행동입니다.

+0

지적 해 주셔서 감사합니다. 이 실패를 알지 못했습니다. 클래스 자체가 정적 인 경우 라하더라도 컴파일러가 생성자에 액세스하기 전에 정적 멤버를 초기화하는 것으로 컴파일러가 인식하는 것이 거의 확실했습니다. – Millianz

관련 문제