정적 변수 초기화 순서
C ++는 컴파일 단위 (.cpp 파일)의 변수가 선언 순서대로 초기화되도록 보장합니다. 컴파일 단위의 수에 대해이 규칙은 각각에 대해 개별적으로 작동합니다 (클래스 외부의 정적 변수를 의미 함).
그러나 변수 초기화 순서는 다른 컴파일 단위에서 정의되지 않습니다.
gcc 및 MSVC에 대한이 주문에 대한 설명을 어디에서 볼 수 있습니까? ?
당신이 말했듯이 순서는 다른 컴파일 단위에서 정의되지 않습니다.
동일한 컴파일 단위 내에서 순서가 잘 정의되어 있습니다. 정의와 동일한 순서입니다.
이는 언어 수준이 아니라 링커 수준에서 해결되기 때문입니다. 따라서 링커 문서를 확인해야합니다. 나는 이것이 유용한 방법으로 도움이 될 것이라고 정말로 의심하지만.
gcc : ld 확인
링크되는 객체 파일의 순서를 변경해도 초기화 순서가 변경 될 수 있음을 발견했습니다. 따라서 링커뿐만 아니라 빌드 시스템에서 링커를 호출하는 방법에 대해 걱정해야합니다. 문제를 해결하려는 시도조차도 사실상 시작이 아닙니다.
이것은 일반적으로 자체 초기화 중에 서로를 참조하는 전역을 초기화 할 때만 문제가됩니다 (따라서 생성자가있는 객체에만 영향을 미침).
문제를 해결할 수있는 기술이 있습니다.
- 지연 초기화.
- Schwarz 카운터
- 모든 복잡한 전역 변수를 동일한 컴파일 단위에 넣습니다.
- 참고 1 : 전역 :
이전에 초기화 될 수있는 정적 저장 기간 변수를 참조하기 위해 느슨하게 사용됩니다main()
. - 참고 2 : 잠재적
으로 일반적인 경우 정적 저장 기간 변수가 main 이전에 초기화 될 것으로 예상하지만 컴파일러는 일부 상황에서 초기화를 지연 할 수 있습니다 (규칙은 복잡합니다. 자세한 내용은 표준 참조).
모듈 간의 생성자 순서는 주로 링커에 개체를 전달하는 순서의 함수입니다.
그러나 GCC를 사용 init_priority
하면 전역 ctor 의 순서를 명시 적으로 지정할 수 있습니다 .
class Thingy
{
public:
Thingy(char*p) {printf(p);}
};
Thingy a("A");
Thingy b("B");
Thingy c("C");
예상대로 'ABC'를 출력하지만
Thingy a __attribute__((init_priority(300))) ("A");
Thingy b __attribute__((init_priority(200))) ("B");
Thingy c __attribute__((init_priority(400))) ("C");
'BAC'를 출력합니다.
절대적으로 필요한 경우가 아니면이 정보에 의존해서는 안된다는 것을 이미 알고 있으므로 여기에 있습니다. 다양한 도구 모음 (MSVC, gcc / ld, clang / llvm 등)에 대한 일반적인 관찰은 개체 파일이 링커에 전달되는 순서가 초기화되는 순서라는 것입니다.
이것에 대한 예외가 있으며 나는 그들 모두를 주장하지는 않지만 여기에 내가 나 자신을 만났던 것들이 있습니다.
1) 4.7 이전의 GCC 버전은 실제로 링크 라인의 역순으로 초기화됩니다. GCC의이 티켓 은 변경이 발생했을 때이며 초기화 순서에 의존하는 많은 프로그램을 망가 뜨 렸습니다.
2) GCC 및 Clang에서 생성자 함수 우선 순위를 사용 하면 초기화 순서가 변경 될 수 있습니다. 이것은 "생성자"로 선언 된 함수에만 적용된다는 점에 유의하십시오 (즉, 전역 객체 생성자처럼 실행되어야 함). 나는 이와 같은 우선 순위를 사용해 보았고 생성자 함수에서 우선 순위가 가장 높더라도 우선 순위가없는 모든 생성자 (예 : 일반 전역 개체, 우선 순위가없는 생성자 함수)가 먼저 초기화된다는 것을 발견했습니다 . 즉, 우선 순위는 우선 순위가있는 다른 기능에 대해서만 상대적이지만 실제 1 급 시민은 우선 순위가없는 것입니다. 설상가상으로이 규칙은 위의 (1) 점으로 인해 4.7 이전의 GCC에서 사실상 반대입니다.
3) Windows에는 DllMain () 이라는 매우 깔끔하고 유용한 공유 라이브러리 (DLL) 진입 점 함수 가 있습니다. 정의 된 경우 모든 전역 데이터가 초기화 된 후 직접 DLL_PROCESS_ATTACH와 같은 매개 변수 "fdwReason"으로 실행됩니다. 사용 하는 응용 프로그램이 DLL의 함수를 호출 하기 전에 이는 경우에 따라 매우 유용 하며 GCC가있는 다른 플랫폼이나 C 또는 C ++가있는 Clang에서 이와 유사한 동작 이 전혀 없습니다 . 가장 가까운 방법은 우선 순위가있는 생성자 함수를 만드는 것입니다 (위의 (2) 참조). 이는 절대적으로 동일하지 않으며 DllMain ()이 작동하는 많은 사용 사례에서 작동하지 않습니다.
4) If you are using CMake to generate your build systems, which I often do, I have found that the order of the input source files will be the order of their resultant object files given to the linker. However, often times your application/DLL is also linking in other libraries, in which case those libraries will be on the link line after your input source files. If you are looking to have one of your global objects be the very first one to initialize, then you are in luck and your can put the source file containing that object to be the first in the list of source files. However, if you are looking to have one be the very last one to initialize (which can effectively replicate DllMain() behavior!) then you can make a call to add_library() with that one source file to produce a static library, and add the resulting static library as the very last link dependency in your target_link_libraries() call for your application/DLL. Be wary that your global object may get optimized out in this case and you can use the --whole-archive flag to force the linker not to remove unused symbols for that specific tiny archive file.
Closing Tip
To absolutely know the resulting initialization order of your linked application/shared-library, pass --print-map to ld linker and grep for .init_array (or in GCC prior to 4.7, grep for .ctors). Every global constructor will be printed in the order that it will get initialized, and remember that the order is opposite in GCC prior to 4.7 (see point (1) above).
The motivating factor for writing this answer is that I needed to know this information, had no other choice but to rely on initialization order, and found only sparse bits of this information throughout other SO posts and internet forums. Most of it was learned through much experimentation, and I hope that this saves some people the time of doing that!
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12 - this link moves around. this one is more stable but you will have to look around for it.
edit: osgx supplied a better link.
In addition to Martin's comments, coming from a C background, I always think of static variables as part of the program executable, incorporated and allocated space in the data segment. Thus static variables can be thought of as being initialised as the program loads, prior to any code being executed. The exact order in which this happens can be ascertained by looking at the data segment of map file output by the linker, but for most intents and purposes the initialisation is simultaeneous.
Edit: Depending on construction order of static objects is liable to be non-portable and should probably be avoided.
If you really want to know the final order I would recommend you to create a class whose constructor logs the current timestamp and create several static instances of the class in each of your cpp files so that you could know the final order of initialization. Make sure to put some little time consuming operation in the constructor just so you don't get the same time stamp for each file.
참고URL : https://stackoverflow.com/questions/211237/static-variables-initialisation-order
'Programing' 카테고리의 다른 글
IE에서 "화살표 기능"이 작동하지 않는 이유는 무엇입니까? (0) | 2020.12.12 |
---|---|
다른 CPU 코어에서 스레드를 어떻게 생성합니까? (0) | 2020.12.12 |
Linux에서 로컬 함수 호출을 추적하는 도구 (0) | 2020.12.12 |
C #에서는 스레드 안전을 위해 Queue.Synchronized 또는 lock ()을 사용하는 것이 더 낫습니까? (0) | 2020.12.12 |
실제로 C ++에서 Private 또는 Protected 상속이 필요한 이유는 무엇입니까? (0) | 2020.12.12 |