C ++ 컴파일러는 호출 할 가상 기능의 어떤 구현을 어떻게 알 수 있습니까?

StackOverflow https://stackoverflow.com/questions/203126

  •  03-07-2019
  •  | 
  •  

문제

다음은 다형성의 예입니다 http://www.cplusplus.com/doc/tutorial/polymorphism.html (가독성을 위해 편집) :

// abstract base class
#include <iostream>
using namespace std;

class Polygon {
    protected:
        int width;
        int height;
    public:
        void set_values(int a, int b) { width = a; height = b; }
        virtual int area(void) =0;
};

class Rectangle: public Polygon {
    public:
        int area(void) { return width * height; }
};

class Triangle: public Polygon {
    public:
        int area(void) { return width * height / 2; }
};

int main () {
    Rectangle rect;
    Triangle trgl;
    Polygon * ppoly1 = &rect;
    Polygon * ppoly2 = &trgl;
    ppoly1->set_values (4,5);
    ppoly2->set_values (4,5);
    cout << ppoly1->area() << endl; // outputs 20
    cout << ppoly2->area() << endl; // outputs 10
    return 0;
}

내 질문은 컴파일러가 ppoly1이 사각형이고 ppoly2가 삼각형이라는 것을 어떻게 알 수 있습니까? 그래서 올바른 영역 () 함수를 호출 할 수 있습니까? "Polygon * ppoly1 = ▭ ▭"라인을보고 ERT가 직사각형이라는 것을 알면서도 모든 경우에 작동하지 않을 것입니까? 이런 일을한다면 어떨까요?

cout << ((Polygon *)0x12345678)->area() << endl;

해당 임의의 메모리 영역에 액세스 할 수 있다고 가정합니다.

나는 이것을 테스트하지만 지금은 컴퓨터에서는 할 수 없습니다.

(나는 분명한 것을 놓치지 않기를 바랍니다 ...)

도움이 되었습니까?

해결책

각 객체 (적어도 하나의 가상 함수가있는 클래스에 속함)는 A라고하는 포인터가 있습니다. vptr. 그것은 vtbl 실제 클래스 중 (가상 함수를 가진 각 클래스는 적어도 하나는 하나 이상의 다중 상속 시나리오에 대해 하나 이상을 가지고 있음).

그만큼 vtbl 각 가상 기능마다 하나의 포인터가 포함되어 있습니다. 따라서 런타임에 코드는 객체를 사용합니다. vptr 찾기 위해 vtbl, 그리고 거기에서 실제 재정의 함수의 주소.

특정 경우, Polygon, Rectangle, 그리고 Triangle 각각은 a vtbl, 각각은 관련성을 가리키는 하나의 항목이 있습니다 area 방법. 당신의 ppoly1 가질 것입니다 vptr 지적 Rectangle'에스 vtbl, 그리고 ppoly2 마찬가지로 Triangle'에스 vtbl. 도움이 되었기를 바랍니다!

다른 팁

Chris Jester-Young 이 질문에 대한 기본 답변을 제공합니다.

위키 백과 더 깊이있는 치료법이 있습니다.

이러한 유형의 사물이 어떻게 작동하는지 (그리고 다중 및 가상 상속을 포함한 모든 유형의 상속)에 대한 자세한 내용을 알고 싶다면 최고의 자원 중 하나는 Stan Lippman 's입니다. "C ++ 객체 모델 내부".

바인딩의 측면을 무시하면 실제로이를 결정하는 컴파일러가 아닙니다.

VTables 및 VPointers를 통해 도출 된 객체가 실제로 런타임에있는 C ++ 런타임입니다.

Scott Meyer의 저서 효과적인 C ++는 이것이 어떻게 이루어 지는지에 대한 좋은 설명을 적극 권장합니다.

파생 클래스의 메소드의 기본 매개 변수가 무시되고 기본 클래스의 기본 매개 변수가 여전히 가져 오는 방법에도 포함됩니다! 그것은 구속력이 있습니다.

질문의 두 번째 부분에 답하기 위해 : 그 주소는 아마도 올바른 장소에 v-table이 없을 것이며 광기가 이어질 것입니다. 또한 표준에 따라 정의되지 않았습니다.

cout << ((Polygon *)0x12345678)->area() << endl;

이 코드는 재난이 일어나기를 기다리고 있습니다. 컴파일러는 모두 올바르게 컴파일되지만 시간이 런 시간에있어서 유효한 v-table을 가리키지 않으며 운이 좋으면 프로그램이 충돌합니다.

C ++에서는 이와 같은 오래된 C 스타일 캐스트를 사용해서는 안됩니다. dynamic_cast 그렇게 :

Polygon *obj = dynamic_cast<Polygon *>(0x12345678)->area();
ASSERT(obj != NULL);

cout << obj->area() << endl;

주어진 포인터가 유효한 다각형 객체가 아닌 경우 Dynamic_cast는 NULL을 반환합니다.

가상 기능 테이블. 재치에 따르면, 다각형 유래 객체는 모든 (정적이 아닌) 함수의 구현에 대한 기능 포인터를 포함하는 가상 함수 테이블을 가지고 있습니다. 삼각형을 인스턴스화하면 area () 함수의 가상 함수 포인터가 삼각형 :: area () 함수를 가리 킵니다. 사각형을 인스턴스화하면 영역 () 함수는 사각형 :: area () 함수를 가리 킵니다. 가상 함수 포인터는 메모리의 객체 데이터와 함께 저장되므로 해당 객체를 다각형으로 참조 할 때마다 해당 객체의 적절한 영역 ()이 사용됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top