2013-02-16 4 views
4

Here아래에 다음 문장이 있습니다. 어느 헤더입니까? :아래 스키마는 어떻게 cin, cout, ...에 대한 정의가 하나만있을 것입니까?

마지막으로, <iostream>는 여덟 표준 전역 개체 (CIN, cout을, 등)를 제공합니다. 이 작업을 올바르게 수행하기 위해이 헤더는 <istream><ostream> 헤더 내용을 개 제공하지만 그 밖의 것은 없습니다. 이 헤더의 내용은 이제

#include <ostream> 
#include <istream> 

namespace std 
{ 
    extern istream cin; 
    extern ostream cout; 
    .... 

    // this is explained below 
    static ios_base::Init __foo; // not its real name 
} 

, 앞서 언급 한 런타임 처벌과 같이 : 자신의 코드 중 하나가 그들을 사용하기 전에 전역 개체가 초기화해야합니다; 이 표준에 의해 보장 된 입니다. 다른 전역 개체와 마찬가지로이 개체는 한 번만 초기화되고 이어야합니다. 위와 같은 방법으로 일반적으로 구조로 이루어지며 iOS_base :: Init는 중첩 클래스 인 이 이런 이유로 지정되었습니다.

어떻게 작동합니까? 헤더가 코드 앞에 포함되기 때문에 __foo 객체는 객체보다 먼저 생성됩니다. (전역 개체는 선언 된 순서로 빌드되고 은 역순으로 소멸됩니다.) 생성자가 처음 실행되면 개의 스트림 개체가 설정됩니다.

내 질문 : 나는 여러 .cpp 파일의 헤더 파일 <iostream>을 포함 할 때, 어떻게 보장 위의 방식이 개체에 대한 정의가있을 것입니다 않습니다 cin, cout, 등 ...?

+0

"하나의 정의"는 재미 있지 않습니다 - 이것은 (컴파일 된) 표준 라이브러리의 일부일뿐입니다. 재미있는 것은 "하나의 초기화"입니다. (나는이 기술을 [Schwartz counter]라고 부른다 (http://stackoverflow.com/q/9251763/596781). –

+2

AFAIK,'cout','cin' 외. 객체는 헤더에서'extern'으로 선언되고, 실제 정의는 표준 lib의 파일 안에 있습니다. 이제'ios_base :: Init'는'static'으로 선언됩니다. 따라서 각 번역 단위는 자체 복사본을 가지고 있습니다. 'Init' 객체의 생성자/소멸자는 표준 스트림 객체를 초기화/파괴 할시기를 알려주는 일종의 참조 계산을합니다. 적어도 그것은 [stdlibC++가 그것을하는 것처럼 보입니다] (http://gcc.gnu.org/viewcvs/trunk/libstdc%2B%2B-v3/include/std/iostream?view=markup). – jrok

+0

@jrok 당신이 말한 것은 모두 나에게 분명합니다. 필자가 이해할 수없는 것은 여러 개의'.cpp' 파일에''이 포함되어있을 때'ios_base :: Init'가 전역 객체 cin, cout, ...의 다중 정의를 피하는 방법입니다. –

답변

3

실제로 그런 것은 아닙니다. 하나의 정의 문제는 라이브러리의 일부인 .cpp 파일에서 스트림 객체를 한 번 정의하면 해결됩니다. 질문의 코드는 선언 표준 스트림을 포함합니다.

보증 대상은 이 사용되기 전에으로 초기화된다는 것입니다. C++에서 전역 개체의 한 가지 문제는 각 .cpp 파일 내에서 으로 초기화되는 반면 링커가 개체를 개별 파일에서 가져 오는 순서를 알 수 없다는 것입니다. 한 객체가 초기화되기 전에 다른 객체를 사용하려고 시도 할 때 문제가 발생할 수 있으며 정확한 순서를 알 수 없습니다 (Static initialization order fiasco 참조).

여기에서 사용되는 하나의 대안은 Init 개체를 스트림 개체를 선언하는 헤더에 넣는 것입니다. 스트림을 사용하기 전에 헤더를 포함해야하기 때문에이 Init 개체는 파일의 맨 위에 있으므로 스트림을 사용할 수있는 다른 개체보다 먼저 생성됩니다.

이제는 Init 클래스의 생성자가 스트림 객체의 초기화를 담당합니다. 이것은 일찍하고 단 한 번만해야합니다. 정확히 어떻게 각 구현에 맡겨 지지만, 코드는 생성 된 Init 객체의 수를 추적하는 카운터를 사용함을 암시합니다 (그리고 아마도 첫 번째를 특별히 처리 할 것임).

이것은 표준 언어만을 사용하여이를 수행하는 한 가지 방법입니다. 일부 구현에는 #pragmas 또는 init_priority 지시문과 같이 사용자 코드 앞에 라이브러리 코드를 넣도록 링커에 알리는 등의 다른 트릭이 있습니다. 이 경우에는 Init 클래스를 사용하지 않고 마술에 의해서만 작동합니다.

+0

'보장은 무엇입니까? 개체가 사용되기 전에 초기화된다는 것입니다. C++에서 전역 개체의 한 가지 문제는 각 .cpp 파일 내에서 순서대로 초기화되지만 링커가 개체를 개별 파일에서 가져 오는 순서를 알 수 없다는 것입니다. 한 객체가 다른 객체를 사용하기 전에 다른 객체를 사용하려고 시도하면 문제가 발생할 수 있습니다. 정확한 순서는 알 수 없습니다. "이 문제는 C++ 11에서 이미 해결되었으며 적어도 이 답변 http://stackoverflow.com/a/8785008/1042389. –

+0

유일한 차이점은 C++ 11에서는'Init' 객체가 생성 된 것처럼 구현이 동작해야한다고 말합니다. 그것이 C++ 03에서 작동하는 방식입니다. 철자가 안된 것입니다 (명백한 것으로 가정 되었기 때문에 :-). See [Library Defect Report # 369] (http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#369) –

+0

'하나의 정의 문제는 단순히 스트림을 정의함으로써 해결됩니다. 개체는 라이브러리의 일부인 .cpp 파일에 한 번 있습니다. '당신은 이것을 지원하기위한 참고 자료가 있습니까? 표준이 이것에 대해 아무 말도하지 않습니까? –

관련 문제