2009-11-27 2 views
1

다음 동작의 근본 원인을 이해하는 데 도움을주십시오. 파일 a.cpp에서인라인 함수 정의가 다른 함수 객체의 인스턴스화는 연결 순서에 따라 다릅니다.

내가 가진 : 파일 b.cpp에서

namespace NS { 
    struct Obj { 
     void pong(){ cout << "X in "__FILE__ << endl; } 
     double k; 
    }; 
    X::X() { Obj obj; obj.pong(); } 
    void X::operator()() { cout << "X says hello" << endl; } 
} 

내가 가진 :

namespace NS { 
    struct Obj { 
     void pong(){ cout << "Y in "__FILE__ << endl; } 
     bool m; 
    }; 
    Y::Y() { Obj obj; obj.pong(); } 
    void Y::operator()() { cout << "Y says hello" << endl; } 
} 

main는 X, y를을 만들고 자신의 연산자()의 호출

int main(int argc, char *argv[]) 
{ 
    NS::X x; 
    x(); 

    NS::Y y; 
    y(); 

    return 0; 
} 

이 프로그램의 출력은,210 또는 b.cpp 먼저 컴파일 얻는다 : 첫 번째 경우에서 a.cppObjb.cpp에서 ObjNS::XNS::Y 모두 인스턴스화 제 경우 NS::Y의 생성자 내에 인스턴스화되어있다.

% g++ b.cpp a.cpp main.cpp 
% ./a.out 

X in a.cpp 
X says hello 
Y in b.cpp 
Y says hello 

% g++ b.cpp a.cpp main.cpp 
% ./a.out 

Y in b.cpp 
X says hello 
Y in b.cpp 
Y says hello 

Linux 또는 Visual Studio (2005)에서 경고 메시지가 표시되지 않습니다. 내가 구조체의 선언 외부에 Obj::pong()을 정의하면 Obj :: pong 함수가 여러 번 정의되었다는 링커 오류가 발생합니다.

제가 조금 더 실험했는데, 원인이 인라인 여부와 관련이 있어야한다는 것을 알게되었습니다. 왜냐하면 -O3으로 컴파일하면 각 객체가 자신의 번역 단위에서 Obj를 사용하기 때문입니다.

그런 다음 질문이 변경됩니다. 최적화되지 않은 컴파일 중에 인라인 함수의 두 번째 정의는 어떻게됩니까? 그들은 조용히 무시 당하고 있습니까?

답변

2

이것은 정의되지 않은 동작입니다. 클래스 정의가 동일한 클래스 유형을 정의하므로 둘 다 동일해야합니다. 링커의 경우, 하나의 임의의 정의를 방출 된 것으로 선택할 수 있음을 의미합니다.

분리형으로 만들려면 이름없는 네임 스페이스에 중첩해야합니다. 이 네임 스페이스의 어떤 그 번역 단위에 대해 고유하게됩니다 님의

namespace NS { 
    namespace { 
    struct Obj { 
     void pong(){ cout << "Y in "__FILE__ << endl; } 
     bool m; 
    }; 
    } 
    Y::Y() { Obj obj; obj.pong(); } 
    void Y::operator()() { cout << "Y says hello" << endl; } 
} 

그럼 질문 변경 : 최적화되지 않은 컴파일시 인라인 함수의 두 번째 정의에 무슨 일? 그들은 조용히 무시 당하고 있습니까? 그들은 프로그램에서 여러 번 정의 될 수 있으며, 프로그램은 것처럼 동작 : 인라인 함수에 대한

, 동일한 원리가 적용됩니다 (클래스 정의 내에서 정의 된 함수를 명시 적으로 인라인을 선언되지 않은 경우에도, 인라인 있습니다) 한 번만 정의되었습니다. 링커에게 다시 한 번 정의를 제외한 모든 것을 버릴 수 있음을 의미합니다. 어느 것이 선택되어지는지는 불명확합니다.

+0

감사합니다. 상황을 해결하는 방법을 알고 있었지만, 컴파일러가 그런 상황에서하는 일에 관심이 있습니다. –

+0

@andreas buykx, 컴파일러는 하나의 정의를 제외하고 모두 버립니다. 더 이상 말할 수있는 것이 확실하지 않습니다. 특히 관심이있는 것은 무엇입니까? –

+0

컴파일러는 각 cpp 파일과 별도로 작동하기 때문에이 결정을 내리는 컴파일러가 될 수 없습니다. 링커는 사용할 구현을 결정하고, 분명히 첫 번째를 선택합니다. –

0

링커는 맹 글링 된 이름을 처리합니다. 여기 봐주십시오 http://en.wikipedia.org/wiki/Name_mangling

그래서 요하네스 말했듯이, 동작은 정의되지 않은,하지만 세부 사항은 명확하게 할 수있다 : 탁구()가 네임 스페이스의 외부에 정의 된 경우
, 그 이름이 고유하게 와 링커가 불평 바르게.

그러나 네임 스페이스에 숨겨진 이름은 다른 번역 단위의 이름 공간과 겹치기 때문에 - 링커는 불평하지 않습니다. 대신 하나의 심볼을 사용합니다.
그게 전부입니다.

필자는 이것이 명시되어 있지 않으며 모든 컴파일러/링커에 대해 구현에 따라 다르다고 생각합니다.

+0

네임 스페이스는 실제로 여기에서 중요하지 않습니다. 네임 스페이스를 제거하면 모든 것이 설명 된대로 작동합니다. –