컴파일 시 가상함수가 정의된 클래스가 있다면 가상함수테이블(Virtual function table)이 만들어져서 바이너리 'rdata'영역에 기록되며 해당 클래스로 만들어진 객체에서 함수를 호출할 때 해당 클래스의 가상함수 테이블을 참조해서 함수를 호출된다. 편의를 위해 가상함수테이블은 "vtable" 이라고 명칭한다.
///// vtable(Virtual Table)
//_vfptr : virtual function pointer (배열로 구성. Key / Value)
//정적할당일 때는 부모(Base)에 할당. 동적할당일 때는 통해서 와야하기 때문에 자기자신한테 가상함수가 있는지 먼저 확인하기 위해 가상테이블이 따로 할당됨.
//C에서는 대부분 동적할당일 때만 가상화를 한다.
class Character
{
public:
Character(int id, string name)
{
this->id = id;
this->name = name;
printf("캐릭터 생성 %p - %s\n", this, this->name.c_str());
}
virtual ~Character() //가상 소멸자
{
printf("캐릭터 소멸 %p - %s\n", this, this->name.c_str());
}
virtual void Move() //가상화
{
printf("캐릭터 이동 %p - %s\n", this, this->name.c_str());
}
//순수 가상 함수(C에서는 추상클래스, 즉 추상함수의 abstract 키워드가 존재하지 않음.)
virtual void Attack() = 0;
protected:
int id;
string name;
};
class Player : public Character
{
public:
Player(int id, string name)
: Character(id, name)
{
printf("플레이어 생성 %p - %s\n", this, this->name.c_str());
}
//C에서는 가상화를 할 때, 소멸자도 가상화를 해줘야함.
~Player() //override -> 안 붙여도 됨.
{
printf("플레이어 소멸 %p - %s\n", this, this->name.c_str());
}
//재정의 X. 부모의 별도 영역에 들어간 것.
void Move() override //C는 override 안 써도 됨. 그러나 써주는게 좋음.
{
Character::Move(); //재정의 X
printf("플레이어 이동 %p - %s\n", this, this->name.c_str());
}
void Attack() override
{
printf("플레이어 공격 %p - %s\n", this, this->name.c_str());
}
};
int main()
{
//Character character(1, "Character");
//생성과 소멸은 역순으로(스택 때문에)
Player player(2, "Player");
//player.Move();
//Character player2 = player;
//player2.Move(); //직접 접근 : 자기가 소유하고 있는 Move()를 콜 -> Character의 Move()
Character* player3 = new Player(3, "Player3"); //주소로 접근
player3->Move(); //간접 접근 : Player의 Move()
player3->Attack();
delete player3;
return 0;
}
출처 :