가상 함수가 있는 클래스가 vtable로 구현된 경우 가상 함수가 없는 클래스는 어떻게 구현됩니까?

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

  •  01-07-2019
  •  | 
  •  

문제

특히, 어쨌든 어떤 종류의 함수 포인터가 있어야 하지 않을까요?

도움이 되었습니까?

해결책

가상 멤버가 아닌 함수는 일반 함수와 거의 비슷하지만 액세스 확인 및 암시적 개체 매개변수가 있기 때문에 실제로는 구문 설탕일 뿐입니다.

struct A 
{
  void foo ();
  void bar () const;
};

기본적으로 다음과 같습니다:

struct A 
{
};

void foo (A * this);
void bar (A const * this);

특정 객체 인스턴스에 대해 올바른 함수를 호출하려면 vtable이 필요합니다.예를 들어 다음과 같은 경우가 있습니다.

struct A 
{
  virtual void foo ();
};

'foo'의 구현은 다음과 유사할 수 있습니다.

void foo (A * this) {
  void (*realFoo)(A *) = lookupVtable (this->vtable, "foo");
  (realFoo)(this);   // Make the call to the most derived version of 'foo'
}

다른 팁

"라는 문구가 생각난다.가상 함수가 있는 클래스는 vtable로 구현됩니다."는 당신을 오해하고 있습니다.

이 문구는 가상 함수가 있는 클래스가 구현된 것처럼 들리게 합니다.A 방식으로" 및 가상 함수가 없는 클래스가 구현됩니다."B 방식으로".

현실에서는 가상 함수를 이용한 클래스, 게다가 클래스로 구현되면 vtable도 있습니다.이를 보는 또 다른 방법은 "'vtables'가 클래스의 '가상 함수' 부분을 구현합니다"라는 것입니다.

둘 다 어떻게 작동하는지에 대한 자세한 내용은 다음과 같습니다.

모든 클래스(가상 또는 비가상 메서드 포함)는 구조체입니다.그만큼 오직 C++에서 구조체와 클래스의 차이점은 기본적으로 멤버가 구조체에서는 공개이고 클래스에서는 비공개라는 것입니다.따라서 여기서는 구조체와 클래스를 모두 지칭하기 위해 클래스라는 용어를 사용하겠습니다.기억하세요, 그것들은 거의 동의어입니다!

데이터 멤버

클래스는 (구조체와 마찬가지로) 각 멤버가 순서대로 저장되는 연속 메모리 블록입니다.CPU 아키텍처상의 이유로 멤버 간에 간격이 있는 경우가 있으므로 블록이 해당 부분의 합보다 클 수 있다는 점에 유의하세요.

행동 양식

메서드나 "멤버 함수"는 환상입니다.실제로는 "멤버 함수"라는 것이 없습니다.함수는 항상 메모리 어딘가에 저장된 일련의 기계 코드 명령어입니다.호출을 하기 위해 프로세서는 해당 메모리 위치로 점프하여 실행을 시작합니다.모든 메소드와 함수는 '전역'이라고 말할 수 있으며, 그 반대의 표시는 컴파일러에 의해 시행되는 편리한 환상입니다.

분명히 메소드는 특정 객체에 속한 것처럼 작동하므로 더 많은 일이 진행되고 있는 것이 분명합니다.메서드(함수)의 특정 호출을 특정 개체에 연결하기 위해 모든 멤버 메서드에는 해당 개체에 대한 포인터인 숨겨진 인수가 있습니다.회원은 숨겨진 C++ 코드에 직접 추가하지는 않지만 마법 같은 것은 없습니다. 매우 현실적입니다.당신이 이렇게 말할 때:

void CMyThingy::DoSomething(int arg);
{
    // do something
}

컴파일러 정말 다음을 수행합니다.

void CMyThingy_DoSomething(CMyThingy* this, int arg)
{
    /do something
}

마지막으로 이렇게 쓰면:

myObj.doSomething(aValue);

컴파일러는 다음과 같이 말합니다.

CMyThingy_DoSomething(&myObj, aValue);

어디에서나 함수 포인터가 필요하지 않습니다!컴파일러는 사용자가 호출하는 메서드를 이미 알고 있으므로 해당 메서드를 직접 호출합니다.

정적 메서드는 훨씬 더 간단합니다.그들은 가지고 있지 않습니다 이것 포인터이므로 작성한 대로 정확하게 구현됩니다.

그렇군요!나머지는 편리한 구문 설탕입니다.컴파일러는 메서드가 어떤 클래스에 속하는지 알고 있으므로 어떤 클래스를 지정하지 않고는 함수를 호출할 수 없도록 합니다.또한 해당 지식을 사용하여 번역합니다. myItem 에게 this->myItem 그렇게 하는 것이 분명할 때.

(그래 맞아:메소드의 멤버 액세스는 언제나 포인터가 보이지 않더라도 포인터를 통해 간접적으로 수행됨)

(편집하다:마지막 문장을 삭제하고 별도로 게시하여 별도로 비판할 수 있습니다)

다형성을 사용하려면 가상 메서드가 필요합니다.그만큼 virtual 수정자는 후기 바인딩을 위해 VMT에 메서드를 넣은 다음 런타임 시 어떤 클래스가 실행될 메서드를 결정합니다.

메소드가 가상이 아닌 경우 컴파일 타임에 어느 클래스 인스턴스에서 실행될지 결정됩니다.

함수 포인터는 주로 콜백에 사용됩니다.

가상 함수가 있는 클래스가 vtable로 구현되면 가상 함수가 없는 클래스는 vtable 없이 구현됩니다.

vtable에는 적절한 메서드에 대한 호출을 전달하는 데 필요한 함수 포인터가 포함되어 있습니다.메서드가 가상이 아닌 경우 호출은 클래스의 알려진 유형으로 이동하며 간접 참조가 필요하지 않습니다.

가상이 아닌 메소드의 경우 컴파일러는 일반 함수 호출(예: 매개변수로 전달된 이 포인터를 사용하여 특정 주소에 대한 CALL)을 생성하거나 인라인할 수도 있습니다.가상 함수의 경우 컴파일러는 일반적으로 컴파일 타임에 코드를 호출할 주소를 알지 못하므로 런타임에 vtable에서 주소를 조회하는 코드를 생성한 다음 메서드를 호출합니다.사실, 가상 함수의 경우에도 컴파일러는 때때로 컴파일 타임에 올바른 코드를 올바르게 확인할 수 있습니다(예: 포인터/참조 없이 호출된 지역 변수에 대한 메서드).

(별도로 비판할 수 있도록 원래 답변에서 이 섹션을 가져왔습니다.훨씬 더 간결하고 질문의 요점에 부합하므로 어떤면에서는 훨씬 더 나은 답변입니다)

아니요, 함수 포인터가 없습니다.대신 컴파일러가 문제를 해결합니다. 뒤집어서.

컴파일러는 다음을 사용하여 전역 함수를 호출합니다. 객체에 대한 포인터 누군가에게 전화하는 대신 객체 내에서 가리키는 함수

왜?왜냐하면 일반적으로 그렇게 하는 것이 훨씬 더 효율적이기 때문입니다.간접 호출은 비용이 많이 드는 지침입니다.

런타임 중에는 변경할 수 없으므로 함수 포인터가 필요하지 않습니다.

분기는 메서드에 대한 컴파일된 코드에 직접 생성됩니다.클래스에 전혀 없는 함수가 있는 경우와 마찬가지로 해당 함수에 바로 분기가 생성됩니다.

컴파일러/링커는 호출될 메소드를 직접 연결합니다.vtable 간접 참조가 필요하지 않습니다.그런데, 그게 "스택 대 스택"과 무슨 관련이 있나요?더미"?

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