2010-07-27 2 views
6

clas (child)가 base1과 base2를 상속하는 기본 클래스를 가진 단일 상속을 가진 객체에 보통 얼마나 많은 vptrs가 필요한가? 객체가 제공 한 vptr의 수를 확인하는 전략은 단일 상속과 다중 상속이 있습니다. 표준은 vptrs에 대해 지정하지 않지만 구현이 가상 함수 구현을 수행하는 방법을 알고 싶습니다.클래스의 객체 (단일/다중 상속을 사용)의 vptr은 몇 개입니까?

답변

6

왜 신경 씁니까? 간단한 대답은 입니다.이긴하지만, 좀 더 완벽한 것을 원합니다.

이것은 표준의 일부가 아니므로 임의의 구현은 원하는대로 자유롭게 수행 할 수 있지만 일반적인 경험적 규칙에 따라 가상 테이블 포인터를 사용하는 구현에서 0을 근사치로 사용하여 동적 디스패치를 ​​수행 할 수 있습니다. 계층에 새로운 가상 메소드를 추가하는 클래스가 있으므로 가상 테이블에 대한 포인터가 많아야합니다. 필요 (어떤 경우에는 가상 테이블 확장, 그리고베이스와 파생 된 형식은 하나의 vptr 공유 할 수 있습니다) 기본적으로

// some examples: 
struct a { void foo(); };   // no need for virtual table 
struct b : a { virtual foo1(); }; // need vtable, and vptr 
struct c : b { void bar(); };  // no extra virtual table, 1 vptr (b) suffices 
struct d : b { virtual bar(); }; // extra vtable, need b.vptr and d.vptr 

struct e : d, b {};     // 3 vptr, 2 for the d subobject and one for 
            // the additional b 
struct f : virtual b {}; 
struct g : virtual b {}; 
struct h : f, g {};     // single vptr, only b needs vtable and 
            // there is a single b 

자신의 동적 파견 (직접 부모를 다시 사용할 수 없습니다)을 필요로하는 유형의 각 하위 객체를 자체 가상 테이블 및 vptr.

실제로 컴파일러는 서로 다른 vtable을 단일 vtable로 병합합니다. db에있는 함수 세트를 통해 새로운 가상 함수를 추가하면 컴파일러는 새로운 슬롯을 vtable의 끝에 추가하여 잠재적 인 두 테이블을 하나로 병합하므로 d의 vtable은 다음과 같은 확장 버전이됩니다. 바이너리 호환성을 유지하는 끝에 여분의 요소가있는 b의 vtable (d vtable은 b에서 사용할 수있는 메서드에 액세스하기 위해 b vtable로 해석 될 수 있음) d 개체는 vptr을 갖습니다.

다중 상속의 경우 개별 객체 인 경우보다 완전한 객체의 하위 객체와 동일한 레이아웃을 갖는 각 기본 객체가 필요하기 때문에 좀 더 복잡해집니다. 따라서 다른 영역을 가리키는 추가 vptrs가있게됩니다 전체 개체의 vtable.

마지막으로 가상 상속의 경우 상황이 훨씬 복잡해지고 동일한 완성 된 개체에 대해 vtr이 여러 개있을 수 있습니다. 건설/파괴가 진행됨에 따라 vptr이 업데이트됩니다 (vptr은 건설/파괴가 진행됨에 따라 항상 업데이트되지만 가상 상속의 경우에는 동일한 유형의 여러 vtable을)

+1

"struct d : b {가상 바();} // 추가 vtable, 필요 b.vptr 및 d.vptr'"클래스에 둘 이상의 vptr을 도입하는 컴파일러가 없다고 생각합니다. 가상이 아닌 SI와 – curiousguy

4

아무것도에 대한 vptr에서/VTABLE이 지정되지 않은 미세 인쇄, 그래서이있을 것 동안 가상 상속없이 vptr에서이 기지의 vtable을 가리 킵니다 이것은 훌륭한 세부 사항에 대한 컴파일러 의존성이 될 것입니다. 그러나 단순한 경우는 거의 모든 현대 컴파일러에 의해 동일하게 처리됩니다 iler (필자는 "거의"라고 쓴다.)

경고를 받았습니다.

개체 레이아웃 : 비 가상 상속

당신이 기본 클래스에서 상속, 그리고 그들이 vptr에서이 있다면, 당신은 자연스럽게 많은 이 클래스에 vptr에서을 상속합니다.

질문 : 컴파일러가 상속 된 vptr이 이미있는 클래스에 vptr을 언제 추가합니까?

컴파일러는 중복 vptr에서 추가 피하기 위해 노력할 것 : 여기 B

struct B { 
    virtual ~B(); 
}; 

struct D : B { 
    virtual void foo(); 
}; 

은 vptr에서이 있으므로 D가 자신의 vptr에서 얻을하지 않습니다, 그것은 기존의 vptr에서이 재사용을; B의 vtable은 foo()에 대한 항목으로 확장됩니다. D의 VTABLE은 B, 의사 코드의 VTABLE에서 "파생"입니다 :

struct B_vtable { 
    typeinfo *info; // for typeid, dynamic_cast 
    void (*destructor)(B*); 
}; 

struct D_vtable : B_vtable { 
    void (*foo)(D*); 
}; 

글씨는 다시이 실제의 vtable의 단순화, 아이디어를 얻을 수 있습니다.

이 아닌 가상의 단일 상속 가상 상속

는 구현 사이의 변화에 ​​대한 거의 여지가 없다. 가상 상속의 경우 컴파일러간에 훨씬 더 많은 변형이 있습니다.

struct B2 : virtual A { 
}; 
A*- B2*에서 변환이

, 그래서 B2 객체는이 기능을 제공해야 하나 offset_of_A_from_B2

  • 하십시오 A* 회원
  • 중 하나를 int의 멤버로

    • 중 하나를 vptr을 사용하여 vtable에 offset_of_A_from_B2을 저장하여

    일반적으로 클래스는 이 아니며은 가상 기본 클래스의 vptr을 다시 사용합니다 (그러나 매우 특별한 경우도 있음).

  • 관련 문제