2011-01-06 2 views
7

http://en.wikipedia.org/wiki/Typeidtypeid는 어떻게 작동하며 객체는 클래스 정보를 어떻게 저장합니까?

이것은 나에게 수수께끼 인 것 같습니다. 컴파일러는 객체의 유형에 대한 정보를 어떻게 저장합니까? 기본적으로 빈 클래스는 일단 인스턴스화되면 메모리에 크기가 0이 아닙니다.

+1

C++ 객체의 크기는 0이 아닙니다 (기본 클래스 하위 객체가 될 수 있음을 제외하고). 이것은 'typeid' 때문에 발생하는 것은 아니지만 크기가 0 인 무언가 배열을 만든다면 모든 객체는 같은 주소에 있기 때문입니다. 별개의 객체가 각각 고유 한 주소를 가지므로 크기가 0 인 객체가없는 언어의 속성으로 유용합니다. 어쨌든 단일 상속에서 일반적으로 발생하는 것과 동일한 객체의 여러 기본 클래스가 동일한 주소에있을 수 있다는 생각에 모든 것이 사용되기 때문에 기본 클래스 하위 객체에 대해서는 별 문제가되지 않습니다. –

답변

9

어떻게 저장되는지는 구현에 따라 결정됩니다. 많은 다른 방법이 있습니다.

그러나 non-polymorphic 유형의 경우 은 없습니다.을 저장할 필요가 있습니다. 비 다형성 유형의 경우 typeid정적 유형 표현식 유형에 대한 정보를 반환합니다. 즉, 컴파일 시간은 유형입니다. 유형은 항상 컴파일 타임에 알려 지므로 추가 정보를 특정 객체와 연관시킬 필요가 없습니다 (sizeof처럼 실제로는 어디서나 객체 크기를 저장할 필요가 없습니다). "비어있는 객체"는 비 다형성 유형의 객체이므로 아무 것도 저장할 필요가 없으며 크기가 0 인 경우 아무런 문제가 없습니다. (한편, 다형성 객체는 정말 "빈"결코 및 "메모리 제로 사이즈"를하지 않아도됩니다.) 실제로는 실행에 대한 즉, 표현의 동적 유형에 대한 정보를 반환하지 typeid 다형성 유형의

- 시간 유형. 이를 구현하려면 런타임에 실제 객체 안에 저장해야합니다. 위에서 말했듯이, 다른 컴파일러는 그것을 다르게 구현합니다. MSVC++에서, 예를 들어, 각 다형성 객체에 저장된 VMT 포인터는 실제 VMT 외에도 객체에 대한 RTTI - 런타임 유형 정보를 포함하는 데이터 구조를 가리 킵니다.

질문에 0 크기의 개체가 언급되어 있다는 사실은 아마도 typeid이 수행 할 수있는 작업과 수행 할 수없는 작업에 대해 오해가 있음을 나타냅니다. 다시 typeid다형성 유형에 대해서만 객체의 실제 (즉 동적) 유형을 결정할 수 있음을 기억하십시오.. 비 다형성 유형의 경우 typeid은 객체의 실제 유형을 결정할 수 없으며 원시 컴파일 타임 기능으로 되돌아갑니다.

+0

사람이 C++에 대해 징징 대 이유가 # 1 인 이유는 메모리 제어가 약간 손실 되었기 때문입니다. 간단히 말해 컨트롤 프로그래머의 편을 들기는하지만 형식 검사는 실제로 어떻게 수행됩니까? 정수입니까? 실제 구현은 어디서 볼 수 있습니까? – jokoon

+0

@ gokoon : 컴파일러에서 문서화하지 않으면 생성 된 코드에서 리버스 엔지니어링해야합니다 (예 : 컴파일을 asm). –

+0

@gokoon : Open Source/Free C++ 컴파일러에서 실제 구현을 볼 수 있습니다. 모든 컴파일러가 동일한 방식으로 작업해야하는 것은 아니기 때문에 Visual C++와 같은 특정 컴파일러에 관심이 있다면 특히 그 컴파일러를 연구해야합니다. –

1

형식 정보를 사용하지 않더라도 빈 클래스에는 0 바이트가 없으며 표준 요구 사항이 맞다면 항상 무언가를 가지고 있습니다.

나는 typeid가 vtable 포인터와 비슷하게 구현된다고 믿는다. 객체는 typeid에 대한 "숨겨진"포인터를 가질 것이다.

+2

typeid를 지원하기 위해 모든 인스턴스를 추가 할 필요가 없습니다. vtable 항목을 사용할 수 있습니다. 각 인스턴스는 이미 동적 유형별 vtable을 가리키고 있기 때문입니다. 이런 이유로 적어도 하나의 가상 메소드가있는 경우에만 typeid를 사용하여 객체의 동적 유형을 가져올 수 있습니다. –

+0

그게 가능합니다. – bcsanches

0

한 가지 질문에 몇 가지 질문이 있습니다.

C++ 개체는 메모리를 차지합니다. 메모리를 차지하지 않으면 객체가 아닙니다 (기본 클래스 하위 객체는 공간을 차지할 수 없지만). 따라서 객체는 적어도 1 바이트를 차지해야합니다.

클래스에 가상 함수가 없으면 컴파일러에서 형식 정보를 저장하지 않습니다. 이 경우 유형 정보에 대한 포인터는 종종 가상 함수 테이블의 음수 오프셋에 저장됩니다. 표준에는 가상 테이블이나 형식 정보 형식이 언급되어 있지 않으므로 순전히 구현 세부 사항입니다.

6

는이 가상 메소드를 가지고있는 것처럼 모든 클래스를 상상하지만, 이미 다른 가상, 한 개체가 각 유형에 대해 생성되는 경우에만 :

extern std::type_info __Example_info; 
struct Example { 
    virtual std::type_info const& __typeid() const { 
    return __Example_info; 
    } 
}; 
// "__" used to create reserved names in this pseudo-implementation 

는 그런 상상 의 사용에을 유형 ID 객체 typeid(obj)obj.__typeid()이됩니다. 마찬가지로 포인터에 대한 사용은 pointer->__typeid()이됩니다. null 포인터 (bad_typeid를 던짐)에 대한 사용을 제외하고, 포인터 참조는 역 참조 이후에 포인터가 아닌 경우와 동일하므로 더 이상 언급하지 않겠습니다. 형식에 직접 적용 할 경우 컴파일러에서 필요한 개체에 직접 참조를 삽입한다고 가정하십시오. typeid(Example)__Example_info이됩니다.

클래스가 RTTI가없는 경우 (가 더 virtuals에가 없다, 즉, 예를 들면 NoRTTI 이하)이면 가상 하지이다 동일한 __typeid 방법 상상. 이것은 위와 같은 메소드 호출로의 동일한 변환을 가능하게하며, 적절하다면 그 메소드의 가상 또는 비 가상 디스패치에 의존한다. 모든 가상 메소드에 대해 수행 할 수있는 것처럼 일부 가상 메소드 호출을 비 가상 디스패치로 변환 할 수도 있습니다.

다음
struct NoRTTI {}; // a hierarchy can mix RTTI and no-RTTI, just as use of 
        // virtual methods can be in a derived class even if the base 
        // doesn't contain any 
struct A : NoRTTI { virtual ~A(); }; // one virtual required for RTTI 
struct B : A {}; // ~B is virtual through inheritance 

void typeid_with_rtti(A &a, B &b) { 
    typeid(a); typeid(b); 
    A local_a; // no RTTI required: typeid(local_a); 
    B local_b; // no RTTI required: typeid(local_b); 

    A &ref = local_b; 
    // no RTTI required, if the compiler is smart enough: typeid(ref) 
} 

, 유형 ID는 (B 나중에 유형에 대한 기본 클래스 수) 두 매개 변수에 대한 RTTI를 사용해야하지만, 지역 변수 중 하나에 대한 RTTI를 필요로하지 않기 때문에 동적 유형 (또는 "실행시의 형태")는 절대적으로 알려져 있습니다. 우연히 가상 통화가 가상 디스패치를 ​​피할 수있는 방법과 일치합니다. 여기

struct StillNoRTTI : NoRTTI {}; 

void typeid_without_rtti(NoRTTI &obj) { 
    typeid(obj); 
    StillNoRTTI derived; typeid(derived); 
    NoRTTI &ref = derived; typeid(ref); 

    // typeid on types never uses RTTI: 
    typeid(A); typeid(B); typeid(NoRTTI); typeid(StillNoRTTI); 
} 

NoRTTI에 해당하는 것 중 하나 OBJ 또는 심판에 사용!이 전자는 파생 클래스 될 경우에도 마찬가지입니다 심지어 심판 확실히 파생 클래스의 인 것처럼 (OBJ 정말 의 인스턴스 A 또는 B 될 수있다). 다른 모든 용도 (함수의 마지막 줄)도 정적으로 해결됩니다.

이 예의 함수에서 각 typeid은 함수 이름에서 알 수 있듯이 RTTI를 사용합니다. 따라서 주석 처리 된 용도는 with_rtti입니다.

+0

나는 그 이상을 다시 읽을 필요가있다. 꽤 자세한 것 같아서 이것을 받아 들인 대답으로 설정하지는 않지만, 잔인한 예제를 사용하는 것보다 아래의 대답이 더 자세하게 설명됩니다. – jokoon

+0

@ gokoon : 아, 잠시 동안 대답 해 주셨습니다 ...:) 실용적인 예제로 설명을 짜는 것이 가장 효과적입니다. 특히 직접 시도해 볼 수있는 경우 (예 : typeid (obj)를 obj .__ typeid() 변환에 수동으로 적용) 등의 작업을 할 수 있습니다. (나는 단지 명확성에 초점을 맞추기 위해 업데이트를했습니다.) –