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의 한 예시가 된다.
출처
https://bloodstrawberry.tistory.com/508
C++ 가상 함수 테이블 virtual function table
C, C++ 전체 링크 virtual이 선언된 클래스가 있는 경우, 가상 함수 테이블은 rdata 섹션에 생성된다. 가상 함수 테이블(vtable)은 함수 포인터 배열이며, 이 포인터를 따라가면 가상 함수로 선언된 멤
bloodstrawberry.tistory.com
'C++ > 기본 문법' 카테고리의 다른 글
<C++> 캐스트(Cast)의 종류와 사용법 (0) | 2022.09.28 |
---|---|
<C++> Template(템플릿) 정리 (0) | 2022.09.28 |
<C++11> SmartPointer(스마트 포인터) (0) | 2022.09.28 |
Lvalue와 Rvalue 차이 (0) | 2022.09.28 |
<C++> string reverse(), substr() (0) | 2022.07.20 |