1

올바르게 이해하면 클래스 정의는 vtable에 가상 함수의 특정 순서를 부과합니다. 따라서 주어진 함수는 해당 함수의 처음부터 특정 오프셋에있는 것으로 알려져 있습니다. 표. 그러나, 나는 그것이 다형성과 어떻게 작용하는지 이해하지 못합니다.vtable 및 다형성 - 함수의 오프셋

class B1 { 
    virtual void funcB1(); 
}; 

class B2 { 
    virtual void funcB2() {} 
}; 

class D : public B1, public B2 { 
    virtual void funcB1() {} 
    virtual void funcB2() {} 
}; 

void main(...) { 
    B1 *b1 = new D(); 
    B2 *b2 = new D(); 
    B1 *realB1 = new B1(); 
    B2 *realB2 = new B2(); 

    b1->funcB1(); 
    b2->funcB2(); 
    realB1->funcB1(); 
    realB2->funcB2(); 
} 

생성 된 코드는 funcB2에 다른 오프셋으로 어떻게 액세스하는지 어떻게 알 수 있습니까?

답변

1

두 개의 기본 클래스에서 클래스를 작성하면 각 부분은 결과 클래스에서 완전히 작동하는 블록으로 표시되고 vtable에 대한 자체 포인터로 완성됩니다. 생성 된 코드가 호출 할 함수를 아는 방식입니다. D의 포인터를 캐스팅하여 B1B2으로 생성하면 생성 된 코드가 가상 테이블에 동일한 오프셋을 사용할 수 있습니다. 이 produces the following output on ideone

D *d = new D(); 
B1 *b1 = dynamic_cast<B1*>(d); 
B2 *b2 = dynamic_cast<B2*>(d); 
printf("%p %p %p", (void*)d, (void*)b1, (void*)b2); 

:

0x91c7008 0x91c7008 0x91c700c 

참고 방법 D*B1* 인쇄 같은 값, B2*가 다른 값을 인쇄하는 동안. b2->funcB2()을 호출하면 포인터 b2이 이미 D 개체의 다른 부분을 가리키고 다른 vtable (즉, B2의 레이아웃을 가진 개체)을 가리키고 있으므로 생성 된 코드는 b2에 대해 다르게 처리 할 필요가 없습니다. 귀하의 예에서는 realB2입니다.

+0

B2 *를 허용하는 함수를 호출 할 때 컴파일러가 실제 B2 또는 D를 가져 오는 지 여부를 어떻게 알 수 있습니까? 이를 확인하고 그에 따라 오프셋을 계산하는 런타임 코드가 추가됩니까? – Dorfl

+0

@Dorfl 컴파일러가'B2 * '를 사용하는 함수를 호출하면, 호출 시점에 자동으로'D *'의 캐스트를 삽입하여 함수 포인터가 보이고 동작하게합니다 마치 B2에 대한 순수한 포인터 인 것처럼. – dasblinkenlight

+0

마침내 얻었습니다! 감사! 죄송합니다. 아직 투표 할 수 없지만 필요한 평판이 없습니다. – Dorfl

1

일반적으로 D 개체에는 두 개의 vtable 포인터가 있습니다 (각 기본 클래스에 하나씩). 각각의 기본 클래스에 대해 동일한 바이너리 레이아웃을 포함해야하므로 실제로는 피할 수 없습니다. 컴파일러는 한 유형에서 다른 유형으로 캐스팅 할 때마다 포인터 수정을 삽입합니다. 기본 클래스 각각에 캐스팅 한 후 포인터 주소를 인쇄하면 그 차이가 있음을 알 수 있습니다.