본문 바로가기
개발/C, C++

C++ - 가상 함수 테이블 (Virtual Function Table)

by 피로물든딸기 2021. 11. 7.
반응형

C, C++ 전체 링크

 

virtual이 선언된 클래스가 있는 경우, 가상 함수 테이블은 rdata 섹션에 생성된다.

 

가상 함수 테이블(vtable)은 함수 포인터 배열이며,

이 포인터를 따라가면 가상 함수로 선언된 멤버 함수들의 주소에 배열 형태로 접근할 수 있다.

즉, 가상 함수 테이블이 실제 호출되어야 할 함수의 위치를 저장하고 있다.

 

가상 함수 테이블을 확인하기 위해 아래의 코드를 실행해보자.

#include <stdio.h>
#include <iostream>

using namespace std;

class FOOD
{
private:
	int price = 0;
public:
	FOOD() { cout << "FOOD Constructor" << endl; }
	virtual ~FOOD() { cout << "FOOD deleted " << endl; }

	virtual void printName() { cout << "FOOD Class" << endl; }
	virtual void printPrice() { cout << this->price << endl; }
	void printLine() { cout << "=================" << endl; }
};

class SNACK : public FOOD
{
private:
	int price = 1000;
public:
	SNACK() { cout << "SNACK Constructor" << endl; }
	virtual ~SNACK() { cout << "SNACK deleted " << endl; }

	void printName() { cout << "SNACK Class" << endl; }
	void printPrice() { cout << "Snack : " << this->price << endl; }
};

int main(void)
{
	FOOD* fd = new SNACK();

	fd->printName();
	
	delete fd;

	return 0;
}

 

FOOD와 SNACK의 생성자에 F9로 break point를 잡는다.

 

F5를 이용해 debug 모드를 실행한다.

 

최초로 쓰레기 값이 할당되어 있는 것을 알 수 있다. F5로 다음으로 넘어간다.

 

FOOD는 price = 0이다. 그리고 FOOD의 __vfptr을 볼 수 있다.

 

SNACK의 생성자에서 break 되었다.

price = 1000으로 할당 되었고 __vfptr의 함수 포인터들이 변경된 것을 알 수 있다.

변경된 함수 포인터는 destructor, printName, printPrice다.

 

내부적으로 각 생성자에서 __vfptr에 각 클래스의 가상 함수 테이블의 주소를 할당하고 있다.

따라서 SNACK의 printName을 호출하는 것은 실제로 fd->__vfptr->printName(); 이다.

여기서 __vfptr은 SNACK 생성자에서 할당된 vtable이 된다.


이제 실행한 코드에서 virtual을 지우고 위와 같이 테스트해보자.

#include <stdio.h>
#include <iostream>

using namespace std;

class FOOD
{
private:
	int price = 0;
public:
	FOOD() { cout << "FOOD Constructor" << endl; }
	~FOOD() { cout << "FOOD deleted " << endl; }

	void printName() { cout << "FOOD Class" << endl; }
	void printPrice() { cout << this->price << endl; }
	void printLine() { cout << "=================" << endl; }
};

class SNACK : public FOOD
{
private:
	int price = 1000;
public:
	SNACK() { cout << "SNACK Constructor" << endl; }
	~SNACK() { cout << "SNACK deleted " << endl; }

	void printName() { cout << "SNACK Class" << endl; }
	void printPrice() { cout << "Snack : " << this->price << endl; }
};

int main(void)
{
	FOOD* fd = new SNACK();

	fd->printName();
	
	delete fd;

	return 0;
}

 

최초의 쓰레기 값은 아래와 같이 할당되었다.

FOOD class의 price = 0이 할당 되었다.

virtual 함수를 가지지 않기 때문에 __vfptr이 없다.

 

SNACK의 생성자가 호출되었어도 역시 __vfptr이 없다.

 

따라서 가상 함수 테이블은 가상 함수를 가지고 있는 클래스에서만 생성된다.


Late binding

 

바인딩 - 함수나 변수의 주소가 결정되는 것.

 

컴파일 타임에 함수나 변수의 주소가 결정되는 것은 Early binding이고, 

런타임에 결정되는 경우는 Late binding이다. 또는 동적 바인딩 Dynamic binding이라고도 한다.

 

가상 함수는 Late binding의 한 예시가 된다.

반응형

댓글