서로 다른 번역 단위에 정의된 전역 객체들의 초기화 순서 [C++]

이 문제는 스캇 마이어 저 ' effective c++' 에서 언급된 내용으로 제 4항 객체를 사용하기 전에 반드시 그 객체를 초기화하자. 라는 문항의 내용을 참조하길 바랍니다.

책에서 깊게 논의된 내용은 아니라 직접 실험을 통한 검증과 그 예를 정리함에 의의를 둔다.

이 문제를 바라보는 바람직한 시각은 추상적인 생각보다는 물리적인 컴파일 과정을 떠올리는 것이 중요하다. 컴파일과 링크 과정에 벌어지는 객체의 초기화와 참조 과정은 분명 물리적 순서가 있기 때문이다. 하지만 여러 모듈 사이에서 물리적 순서를 결정하여 컴파일 하는 것은 쉬운 일이 아니다. 또한, 이런 물리적 순서로 인해 발생하는 문제도 생길 수 있다.

이런 문제로 전역객체의 사용은 꼭 필요한 경우가 아니면 권장하지 않는다. 다음은 전역변수의 사용으로 인한 문제다.

두 개의 클래스 A와 B가 있다고 가정하자. 두 클래스의 객체를 각각의 cpp 파일에서 생성하고 외부에서 사용할 수 있도록 헤더파일에 extern으로 선언하자. 이 경우는 문제가 발생하지 않는다.

조금 수정하여 B객체와 A객체에 연관성을 부여하자.

B객체의 생성자에 A객체를 참조한 값을 이용하도록 하자.

tsf는 A의 객체다.

tsf없이 B가 초기화가 가능한가? tsf의 초기화가 선행되어야 제대로 된 값이 b에 들어갈 것이다. 그럼 class_A와 class_B 의 번역단위가 다른 상태인데 A객체가 먼저 초기화 된다는 보장이 있는가? 이에 대해서는 몇 가지 실험을 해보도록 하겠다.

temp는 class_B의 객체다. Print()의 기능은 단순히 멤버 변수의 값을 출력해 주는데 temp 객체의 멤버변수는 class_A객체의 함수 getNumber()에 의해서 결정지어진다. 이 함수는 또한 값을 반환하는데 제대로 초기화 된 경우 3을 반환 한다.

동일한 컴파일(class_A.cpp 와 class_B.cpp의 순서만 바꿨다.) 다른 값이다. 원하는 값이 나오게 하려면 위 컴파일 순서를 어떻게 결정해야 할까.? 위와 같이 한두 개의 모듈을 사용하는 경우는 어찌 해보겠지만, 포함관계가 복잡해 진다면 운에 맏겨야 할 듯 싶다.

이에 대한 해결책은 책 에 설명 되어 있다. 간단히 설명하자면, extern 변수객체 대신 static 객체를 함수 내부에서 사용하는 것이다. 여기서 가지는 의문은 static으로 선언하면 외부 모듈에서 사용하지 못하는 것 아닌가? 이를 사용하기 위해서 함수 내부에서 static으로 선언하고 참조형으로 반환하는 것이다.

static으로 선언했으므로 함수가 반환돼도 파괴되지 않고, 참조를 이용하므로 모듈 외부에서도 사용할 수 있다. 이는 싱글톤 패턴(singleton pattern)이다.

제대로 해결된 화면이다.

이 댓글을 비밀 댓글로