1 minute read

global static object, global object 가 사용되기 전에 초기화되는 걸 보장하는 idiom이다.

같은 translation unit, 즉 같은 cpp 파일에선 보이는 순서에 따라 초기화되지만 이게 다른 translation unit이면 linker가 정렬하는 순서를 따른다. 그래설라무네 링커보고 그냥 알아서 잘 해주세요 하다가 초기화 순서가 잘못돼서 죽을 때가 있는데, 이런 현상을 static initialization order fiasco라고 한다. 덧붙여 MSVC에서 해제 순서는 스택을 사용하기 때문에 초기화 순서 반대이다.

일단 조때는 경우부터(나오는 예제는 More Idioms에서 가져왔다.)

struct Bar
{
    Bar()       { std::cout << "Bar::Bar()\n"; }
    void f()    { std::cout << "Bar::f()\n"; }
};
struct Foo
{
    Foo()       { bar_.f (); }
    static Bar bar_;
};
Foo f;
Bar Foo::bar_;

int main() { return 0; }
Bar::f()
Bar::Bar()

Foo 인스턴스 선언을 Foo::bar_ static 멤버 초기화보다 먼저 했다. 그래서 생성자가 호출돼서 초기화되기 전에 f()가 호출됐다. 이렇게 같은 translation unit 안이라면 먼저 초기화돼야 하는 인스턴스를 위로 올려서 조때는 걸 막을 수 있겠지만 이게 여러 translation unit에 흩어져 있다면 어떻게 해결해야 하나? 골이 띵~

그래서 Construct On First Use Idiom이 나온게지.

struct Foo
{
    Foo()       { bar().f(); }
    Bar& bar()
    {
        static Bar b;
        return b;
    }
};

local static object 초기화 규칙을 이용한 테크닉이다. 좀 더 자세히 들여다보자. 참조(reference)를 리턴하는 함수를 정의하고 안에 local static object로 선언한다. 그럼 초기화 규칙에 따라 프로그램 진행에서 최초로 등장할 때 초기화가 된다. 간단하면서도 아름답게 초기화가 되기 전에 사용하는 걸 막을 수 있게 된다. 아주 익숙한 idiom일 수도 있는데, Effective C++로 유명한 Scott Meyers가 이 idiom을 사용해서 singleton을 구현했다.

물론 단점도 존재. 프로그램이 끝날 때 소멸하기 때문에 따로 소멸 시기를 정할 수 없다. static은 원래 이모냥.

참고: More C++ Idioms/Construct On First Use