Advanced C++

[C++ 클래스] 가상 함수 테이블

로파이 2021. 1. 29. 20:51

가상 함수, virtual 키워드로 메서드를 정의하면 실제 인스턴스의 타입에 해당하는 메서드로 호출할 수 있다.

 

함수의 호출 과정은,

클래스 메서드를 포함한 일반 함수 내용을 가리키고 있는 함수 포인터들이 메모리 어딘가에 저장되고 관리된다.

따라서 가상 함수로 정의하지 않으면 프로그램이 실행될 때 참조형 클래스에 미리 정의된 메서드를 호출하게된다.

 

가상 함수의 원리는 미리 정의된 메서드를 호출하지 않고 실행중 실제로 "참조하고 있는" 타입을 판별해야한다는 의미이다.

 

C++에서는 참조하고 있는 타입을 따로 판별하는 기능이 없기 때문에 실행 중 이를 아는 것은 불가능하다.

따라서 객체의 인스턴스마다 호출해야하는 함수를 관리해야한다는 뜻이다. 

 

이러한 함수를 함수 포인터 배열로 저장하고 있는 것을가상 함수 테이블(vtable)이라고 한다.각 인스턴스는 가상 함수 테이블을 가리키고 있는 함수 포인터 배열의 포인터(void **)를 관리한다.

class ClassA
{
public:
	const char* _name;
public:
	ClassA(const char* name) : _name(name) {}
	virtual void print_first(){}
	virtual void print_second(){}
	virtual void print_third(){}
	void print_fourth(){}
};

class ClassB : public ClassA
{
public:
	ClassB(const char* name) : ClassA(name) {}
	virtual void print_first(){}
	void print_second(){}
};

int main()
{
	ClassA A("class A");
	ClassB B("class B");
	return 0;
}

 

위 코드를 디버깅 모드로 살펴보면인스턴스마다 함수 포인터 배열 (void **) __vfptr 변수를 저장하고 내용으로 가상 함수 테이블을 가리키고 있다.

특징을 살펴보면 다음과 같다.

  • virtual 키워드로 정의한 가상 함수의 포인터만 저장된다.
  • 자식 클래스에서 virtual로 선언하지 하지 않아도 부모 클래스에서 가상 함수로 정의되었다면 가상 함수이다.
  • 저장되는 순서는 부모 클래스의 정의를 따른다.

가상 함수 테이블 생성 과정

 

가상 함수 테이블은 __vfptr이 초기화되면서 생성되므로 생성자를 호출할 때 할당된다.

  • ClassB의 생성자를 호출할 때 먼저 부모 클래스인 ClassA의 생성자가 호출되는데
  • 첫번째 그림과 같이 이때 상태는 ClassA의 인스턴스의 가상함수테이블과 같다.
  • 두번째 그림에서 부모 클래스 생성자 함수가 종료되고 자신의 __vfptr이 자신이 가지고 있는 메서드로 바뀌어 있는 것을 확인할 수 있다.

이렇게 가상 함수로 정의된 함수들은 인스턴스가 가지고 있는 가상 함수 테이블를 참조하여 함수를 호출하게 된다.

  • 일반 메서드와 함수들은 컴파일 시간에 호출해야는 주소가 결정되기 때문에 "정적 바인딩"이라 한다.
  • 클래스의 가상 함수들은 실행 시간에 주소가 결정되기 때문에 "동적 바인딩"이라고 한다.
  • 메모리의 정적/동적 할당과 똑같은 개념이다.

 

'Advanced C++' 카테고리의 다른 글

[C++] 예외 처리 Exception  (0) 2021.02.20
[C++ 클래스] 순수 가상 소멸자  (0) 2021.02.18
[C++ 클래스] 가상 함수  (0) 2021.01.29
[C++] 이동 시맨틱과 r-value 참조형  (0) 2021.01.28
[C++] l-value와 r-value  (0) 2021.01.28