#include <iostream>
class Shape{
public:
void func() {
printf("shape\n");
}
};
class Square : public Shape{
void func() {
printf("square\n");
}
};
class Triangle : public Shape{
void func() {
printf("triangle\n");
}
};
int main(void) {
Shape* s;
Square* sq;
Triangle* t;
s = new Square;
s->func();
return 0;
}
업캐스팅 후 함수를 호출할 때 컴파일러는 참조자 기준으로 함수를 호출한다.
#include <iostream>
class Shape{
public:
virtual void func() {
printf("shape\n");
}
};
class Square : public Shape{
void func() {
printf("square\n");
}
};
class Triangle : public Shape{
void func() {
printf("triangle\n");
}
};
int main(void) {
Shape* s;
Square* sq;
Triangle* t;
s = new Square;
s->func();
return 0;
}
그러나 참조자의 함수가 virtual이라면 실 형식 기준으로 함수를 호출한다.
그러나 업캐스팅의 경우 컴파일 시간에 정해지는 건데 어떻게 컴파일러는 객체의 클래스형에 따라 재정의된 가상 함수를 호출할 수 있는걸까?
C++는 가상함수를 처리하기 위해 가상함수를 지니고 있는 클래스마다 가상 함수 테이블(Virtual method table) 을 만든다. 즉, 가상함수 포인터를 모아둔 배열을 생성하고 이것을 vtable이라 부른다.
또한 컴파일러는 가상함수를 갖는 클래스 안에, 가상 함수테이블을 가리키는 포인터를 멤버 변수로 추가시키는데 이를 vptr라고 한다.
만약 하위 클래스의 객체가 생성되면, 상위 클래스의 생성자를 먼저 호출할 것이다. 그럴 경우 아래와 같은 코드가 실행된다.
__vptr == 상위 클래스의 vtable의 주소
__vptr은 현재 상위 클래스의 가상 함수 테이블을 가리키게 된다.
그러나 그 후 상위 클래스 생성자를 반환하고, 하위 클래스의 생성자가 호출되며 다음과 같은 코드가 실행된다.
__vptr == 하위 클래스의 vtable의 주소
하위 클래스의 함수 테이블 주소가 상위 클래스의 함수 테이블 주소를 덮어쓰게 된다.
이러한 방식으로 가상함수테이블은 작동한다.