2014-11-20 5 views
-2

나는 최근에 C++ 가상 테이블에 대해 의문을 가지고 있습니다.C++에 가상 테이블이 필요합니까?

왜 C++은 가상 테이블을 사용합니까?

=> C++ 컴파일러는 실제 함수 주소

---> 이유를 모르기 때문에?

=> C++ 컴파일러는 정확한 유형 모르기 때문에 (고양이? 개? 동물?) 객체의

에 대한 포인터 "panimal"점 --- 왜? 컴파일러가 객체 유형을 알아낼 수있는 방법이 있습니까?

=> 예, 저는 컴파일러가 추적 객체 유형을 통해이를 수행 할 수 있다고 생각합니다.

개체 포인터가 값을 가져 오는 원본을 생각해 봅시다. 실제로 2 개의 출처.

  1. 다른 포인터 "또 다른 포인터가"그 값을 얻을 않는 클래스 인스턴스 의
  2. 주소? 결국에는 "클래스 인스턴스"에서 값을 가져 오는 포인터가 있습니다. 그래서

는 원본 소스 오브젝트에 뒤로 할당 스레드를 추적을 통해

=> 컴파일러는 포인터의 정확한 유형을 알아낼 수있다. => 컴파일러

정확한 함수의 주소가없는 가상 테이블이 필요하지 않습니다 =>

을 호출되는 알고있다.

개체 유형 추적은 각 클래스 인스턴스의 가상 테이블 기념품과 가상 테이블 포인터를 저장합니다.

개체 유형 추적이 작동하지 않는 위치는 어디입니까?

라이브러리 링크.

라이브러리 함수가 기본 클래스 포인터를 반환하면 컴파일러에서 원래 소스 객체를 추적 할 수있는 방법이 없습니다. 컴파일러는 아마도 을 라이브러리 코드와 none-library 코드에 맞출 수 있습니다. 내 보낸 라이브러리 클래스의 경우 가상 테이블을 사용하십시오. 다른 클래스의 경우 메모리를 절약하기 위해 오브젝트 유형을 추적하면됩니다.

위의 진술에 오류가 있는지 확실하지 않으므로 친절하게도 지적하십시오. 미리 감사드립니다 ~

+0

간단합니다. 추적은 vtable보다 훨씬 비쌉니다. – ikh

+0

yeath는 컴파일러에서 더 비싸지 만보다 효율적인 실행 코드를 생성합니다. 그리고 대부분의 추적은 몇 파일에서 수행 할 수 있다고 생각합니다. 시간 비용은 그리 크지 않습니다. – Guocheng

+0

아, 죄송합니다. 나는 당신이 꼼수 추적과 같은 것을 말하고 있다고 생각했습니다 :) – ikh

답변

1

경우에 따라 컴파일러는 컴파일 타임에 포인터가 가리키는 유형을 파악할 수 있습니다. 그것은 할 수없는 경우를 만드는 것은 아주 쉽습니다.

int x; 
cin >> x; 
Animal* p; 
if (x == 10) 
    p = new Cat(); 
else 
    p = new Dog(); 

컴파일러가 모든 경우에 객체의 유형을 증명할 수있는 경우,이 같은-경우 규칙에 따라, 그 생성 된 코드에서 가상 테이블을 제거 무료입니다.

+0

대단히 고마워요! 알았다. – Guocheng

0

컴파일러는 정확한 포인터 유형을 알아낼 수 있습니다.

네,하지만 어떻게 런타임에 올바른 함수를 호출 하시겠습니까? 컴파일러는 알고 있지만 C++에는 상속 된 유형의 가상 함수에 대한 vtable의 필요성, 런타임에 전달되는 객체의 유형을 알려주는 가상 시스템이 없다는 것을 알고 있습니다. 컴파일러가 각 가상 함수의 실행으로 이끄는 모든 다른 코드 경로에 대한 코드를 생성하여 런타임에 올바른 함수를 호출하겠습니까? 가능한 한 훨씬 더 큰 바이너리로 이어질 것입니다.

+0

컴파일러는 정확한 유형을 알고 있기 때문에 정확한 함수의 주소 (또는 기호)를 알고 있습니다. "call function_cat_spark"와 같은 코드를 생성 할 수 있습니다. (Cat이 동물의 하위 클래스라고 가정하십시오.) – Guocheng

+0

어셈블리에서 자신을 표현하는 컴파일러가 컴퓨터 코드에서 어떻게 구현하는지 제안 하시겠습니까? 컴파일러는 런타임에는 존재하지 않습니다. – Julius

+0

각 가능한 유형 조합에 대해 각 기능의 사본이 필요하거나 각 포인터와 연관된 기능 포인터가있는 맵을 사용하는 스위치가 필요합니다. 하지만 그것은 vtable을 사용하는 것과 정확히 같습니다. – Julius

0

이 예제에서는 컴파일러가 정적 코드 분석을 수행 할 수 있는지 여부에 관계없이 ptrA-> f()에서 호출되는 실제 메서드가 명확 해집니다. 런타임에만 알 수 있습니다.

#include <sys/time.h> 
#include <iostream> 
#include <stdlib.h> 

struct A { 
    virtual int f() 
    { 
     std::cout<<"class A\n"; 
    } 
}; 

struct B: public A { 
    int f() 
    { 
     std::cout<<"class B\n"; 
    } 
}; 

int main() 
{ 
    A objA; 
    B objB; 
    A* ptrA; 

    timeval tv; 
    gettimeofday(&tv, NULL); 

    unsigned int seed = (unsigned int)tv.tv_sec; 
    int randVal = rand_r(&seed); 
    if(randVal < RAND_MAX/2) 
    { 
     ptrA=&objA; 
    } 
    else 
    { 
     ptrA=&objB; 
    } 

    ptrA->f(); 
    return 0; 
}` 
+1

이 답변은 본질적으로 받아 들여진 답변 (2 년 전부터)의 사본입니다 (설명이 적음). 왜 이것을 추가 할 필요가 있다고 느꼈습니까? – UnholySheep

관련 문제