프로그래밍 공부
카테고리
작성일
2024. 1. 12. 16:20
작성자
WDmil
728x90

C++에서 다형성을 지원하기 위한 메커니즘 중 하나로, 가상함수들의 주소를 담고있는 테이블이다.

 

가상함수 테이블의 역할

  • 다형성을 지원하기 위해 가상함수의 주소를 저장한다.
    함수의 주소를 저장한다는 점에서 함수테이블과 동작방식이 비슷하다.
  • 객체의 실제 타입에 따라 올바른 함수를 호출하는 데 사용한다.

 

우선, 가상함수 테이블에 대해 더 자세히 알려면, 가상함수부터 알아야 한다.

 

가상함수

virtual 키워드가 붙은 멤버 함수로, 파생클레이스에서 재정의할 수 있다.

 

가상함수를 선언하는 순간. 해당 클래스는 가상 함수 테이블을 가질 가능성이 생긴다.

만약, 완전가상인 =0 또는 null선언을 해주면 무조건 가상 함수 테이블을 가진다.

 

가상함수 테이블

클래스에 가상 함수가 하나라도 존재한다면, 해당 클래스의 객체는 가상함수 테이블을 사용할 수 있다.

 

가상함수 테이블은 각 클래스당 한 번만 생성되고, 모든 해당 클래스의 객체가 공유한다.

(이건 함수테이블과 비슷한 성질이다)

 

생성시점

가상함수 테이블은 컴파일 타임에 생성되며, 클래스당 하나만 존재한다.

 

클래스의 가상함수에 대한 주소 정보가 이 테이블에 저장된다.

 

런타임시

 

객체가 생성될 때 마다 해당 클래스의 가상함수 테이블을 가리키는 포인터가 객체에 추가된다.

 

가상함수 호출 시 해당 객체의 가상함수 테이블을 참조하여 올바른 함수로 타고가. 호출하게 된다.

 

class Base {
public:
    virtual void print() const {
        std::cout << "Base" << std::endl;
    }
};

class Derived : public Base {
public:
    void print() const override {
        std::cout << "Derived" << std::endl;
    }
};

int main() {
    Base* basePtr = new Derived;
    basePtr->print();  // Derived의 print 함수 호출
    delete basePtr;
    return 0;

위의 코드 예시를 살펴보면, 가상함수의 선언방식을 이해할 수 있다.

 

Base의 print는 가상함수에 포함되며, 런타임 시 해당 클래스의 가상함수 테이블을 가리키는 포인터가 객체에 생성되고,

가상함수 테이블을 참조하여, 올바른 주솟값에 해당하는 함수를 호출하게 된다.

 

즉, 타고가는 순서가. Derived에 포함되어있는 print는 가상함수 테이블의 포인터가 연결되어 있고,

 

해당 가상함수 테이블에서 하위권 데이터로 연결되는 가상함수 테이블의 Derived->print로 이어지는 순서로 연결된다.

 

좀더 자세히 설명하자면 다음과 같다.

 

1. 가상 함수 호출:

  • Base 포인터로 Derived 객체를 가리킬 때, 해당 포인터에는 Base 클래스의 가상 함수 테이블을 가리키는 포인터가 있다.
  • Base 클래스의 가상 함수 테이블에서는 print 함수에 대한 주소가 있으며, 해당 주소로 이동하여 Base::print 함수를 호출하게 된다.

그러나 print 함수가 가상 함수임을 고려하면, 실제 객체의 타입을 확인하고 올바른 함수를 호출해야 한다.

 

2. 런타임 다운캐스팅 및 실제 함수 호출:

  • Base::print 함수 내에서 실제 객체의 타입을 확인하기 위해 가상 함수 테이블을 사용한다.
  • 이 때, Derived 클래스의 가상 함수 테이블로 이동하고 해당 주소에서 Derived::print 함수를 호출한다.
  • 따라서 Derived::print 함수의 내용이 실행되어 실제로 Derived에 대한 print가 호출된다.
728x90