2010-06-23 5 views
-2

기본 질문에 대한 답변이 필요합니다. 나는 다시 길을 잃었다. - :(가상 함수 및 클래스

Q1이 유효이 성명 :
Whenever we define the function to be pure virtual function, this means that function has no body.

Q2는 - 그리고 동적 컴파일러는이 방법을 다음 vtable을하고 VPTRs를 사용하여 코드를 최적화하면 내 말은 런타임 바인딩의 개념은 무엇인가? ? 다형성

Q3 - 어떤 vtable을 AND VPTRs이며, 그 크기는 어떻게 변경합니까

Q4 -이 코드를 참조하십시오?

class base 
{ 
    public: 
     virtual void display() 
     { 
      cout<<"Displaying from base"; 
     } 
}; 

class derived:public base 
{ 
    public: 
     void display(){cout<<"\nDisplaying from derived";} 
}; 

int main() 
{ 
    base b,*bptr; 
    derived d; 
    bptr=&b; 
    bptr->display(); 
    bptr=&d; 
    bptr->display(); 
} 

출력 : 기본 클래스의 포인터가 파생 클래스와 그 반대의 멤버 함수를 가리킬 수 있습니다 이유

Displaying from base 
Displaying from derieved 

누군가가 대답 할 수주십시오 가능하지, 왜?

+4

숙제 같은 냄새. –

+1

코드 스 니펫을 수정해야합니다. –

+4

숙제이기 때문에 사람들에게 답변을 제공하도록 요청하는 대신 자신의 답변 (또는 초안/부분 답변)이 무엇인지 알려주고 질문을하거나 사람들에게 답변에 대해 의견을 말하거나 수정하도록 요청하십시오. – ChrisW

답변

4
  1. 거짓. 파생 클래스가 상기 함수를 구현해야한다는 것을 의미합니다. 함수에 대한 정의를 계속 제공 할 수 있으며 Base::Function()으로 호출 할 수 있습니다. *

  2. 가상 테이블은 가상 함수를 구현하는 방법입니다. (그러나 표준에서는 이것을 요구하지 않습니다.) 다형성 호출을 할 때, 컴파일러는 함수 테이블에서 함수를 찾아서 호출하여 런타임 바인딩을 가능하게합니다. (테이블은 컴파일시 생성됩니다.)

  3. 위 참조. 가상 기능이 많을수록 크기가 변경됩니다. 그러나 인스턴스는 테이블을 저장하는 것이 아니라 테이블에 대한 포인터를 저장하므로 클래스 크기는 단일 크기 만 증가합니다.

  4. Sounds like you need a book.

이의 전형적인 예는 여기 * :

struct IBase 
{ 
    virtual ~IBase(void) = 0; 
}; 

inline IBase::~IBase(void) {} 

이것은 순수 가상 함수가없는 추상 클래스하지 않을 것이지만, 소멸자가가 필요 정의 (파생 클래스가 소멸 될 때 호출되기 때문에).

(210)

2

1) 필수는 아닙니다. 순수 가상 함수에 몸을 제공하는 경우가 있습니다.

2) 호출 할 함수는 런타임에 결정됩니다.

+0

불완전하더라도 여기에는 아무 것도 잘못되었습니다. 왜 -1일까요? – GManNickG

+0

이것이 올바른 이유는 이것이 왜 왜곡되었는지 궁금합니다. –

+0

IMO 때문에 설명이나 예문없이 불완전한 질문에 적절/도움이되지 않습니다. – ChrisW

1
  1. 거짓. 파생 클래스가 메서드를 구현해야 함을 의미하며 해당 수준의 메서드 정의 (있는 경우)는 가상 메서드를 재정의하는 것을 고려하지 않습니다.
  2. vtable은 컴파일 타임에 구현되지만 런타임에 사용됩니다. 컴파일러는 vtable을 통해 호출을 리디렉션하며 객체의 런타임 유형에 따라 달라집니다 (기본 포인터의 포인터는 정적 유형 base*이지만 실행시 derived 유형의 객체를 가리킬 수 있음).
  3. vptrs는 vtable에 대한 포인터이며 크기를 변경하지 않습니다. vtables은 코드 (메소드 또는 일부 어댑터 코드를 가리킬 수 있음)에 대한 포인터의 테이블이며 클래스에 선언 된 각 가상 메소드에 대해 하나의 항목을가집니다. 코드의 수정 후에

:

포인터가 호출 동안 제 base 유형의 객체를 참조하지만 번째 호출 위치에서 derived 유형의 객체를 포인트. 동적 디스패치 메커니즘 (vtable)은 호출을 적절한 메소드로 라우트합니다.

가상 함수를 선언하는 각 클래스에서 컴파일러가 가상 테이블에 대한 포인터 공간을 예약하고 가상 테이블 자체를 생성하여 정의에 대한 포인터를 추가한다는 점을 이해하는 데 도움이되는 일반적인 구현입니다. 각 가상 메소드의 객체의 메모리 레이아웃에는 추가 포인터 만 있습니다.

파생 클래스가 기본 클래스 메서드를 재정의하면 컴파일러는 해당 수준의 최종 재정의 자에 대한 포인터가 포함 된 다른 vtable을 생성합니다. 기본 클래스와 파생 클래스의 메모리 레이아웃은 base 하위 객체 부분 (일반적으로 시작 부분)에서 일치하지만 base 객체의 vptr 값은 기본 vtable을 가리키고 derived 객체의 vptr 값 파생 된 vtable을 가리 킵니다.

컴파일러에서 bptr->display()과 같은 호출을 볼 때 기본 클래스의 정의를 확인한 다음 첫 번째 가상 메서드임을 확인한 후 호출을 bptr->hidden_vptr[0]()으로 리디렉션합니다. 포인터가 실제 기본 인스턴스를 참조하는 경우 base::display에 대한 포인터가되며 derived 인스턴스의 경우에는 derived::display을 가리 킵니다.

이 답변에는 많은 손길이 있습니다. 이 모든 것은 구현에 의해 정의되며 (언어는 디스패치 메커니즘을 지정하지 않습니다.) 대부분의 경우 디스패치 메커니즘은 더 복잡합니다. 예를 들어 다중 상속이 발생하면 vtable은 최종 재정의자를 직접 가리 키지 않지만 첫 번째 기본을 제외하고 모두 base 하위 객체가 0과 일치하지 않으므로 첫 번째 암시 적 매개 변수 오프셋을 재설정하는 코드의 어댑터 블록에 연결됩니다. 대부분의 파생 된 객체가 메모리에 있습니다.이 질문의 범위를 훨씬 벗어납니다.이 답변은 대략적인 아이디어이며 실제 시스템에는 복잡성이 더해졌습니다.

1

은 정확 하

유효이 문 : 그것은 몸이있을 수 있습니다. 보다 정확한 정의는 "메서드를 순수 가상으로 정의 할 때마다 메서드가 구체적인 하위 클래스에서 정의 (오버라이드)되어야 함을 의미합니다."

동적 바인딩의 개념은 무엇입니까? 컴파일러가 VTABLEs와 VPTRs를 사용하여 코드를 최적화한다면 어떻게 Run-Time Polymorphism이되는 걸까요? 당신은 런타임에서 슈퍼 클래스 (예를 들어, 모양)의 인스턴스가있는 경우

, 당신이하지/반드시이 그 서브 클래스의 어느 (예를 들어, 원형 또는 사각형) 알 필요가 없습니다.

VTABLES 및 VPTR이란 무엇이며 크기가 어떻게 바뀌나요?

클래스 당 하나의 가상 테이블 (하나 이상의 가상 메소드가있는 클래스)이 있습니다. vtable에는 클래스의 가상 메소드 주소에 대한 포인터가 들어 있습니다.

개체 당 하나의 vptr이 있습니다 (하나 이상의 가상 메서드가있는 개체의 경우). vptr은 해당 객체의 클래스에 대한 vtable을 가리 킵니다.

vtable의 크기는 클래스의 가상 함수 수와 함께 증가합니다. vptr의 크기는 아마도 일정합니다.

누군가 기본 클래스의 포인터가 파생 클래스의 멤버 함수를 가리킬 수 있고 그 반대가 가능하지 않은 이유에 대해 대답 할 수 있습니까? 그 이유는 무엇입니까? 그런 다음 기본 클래스 함수를 호출 할 경우 (이 가상, 그리고 가상의 기본 동작은 vptr에서/vtable을 통해 대부분의 파생 버전을 호출하는 것입니다 때문에)

당신은 이렇게 명시 적으로, 예를 들어, 말을 이렇게 :

bptr->base::display();