C++ 가상함

야민·2023년 3월 29일
0

참조

https://nomad-programmer.tistory.com/359
https://cosyp.tistory.com/228

가상함수테이블(Virtual function table)

컴파일 시 가상함수가 정의된 클래스가 있다면 가상함수테이블(Virtual function table)이 만들어져서 바이너리 'rdata'영역에 기록되며 해당 클래스로 만들어진 객체에서 함수를 호출할 때 해당 클래스의 가상함수 테이블을 참조해서 함수를 호출된다. 편의를 위해 가상함수테이블은 "vtable" 이라고 명칭한다.

가상함수 테이블은 클래스 단위로 만들어 진다

객체를 생성하면 위 사진과 같이 객체마다 공간이 생성된다. 해당 객체의 시작주소에는 참조해야할 vtable 의 주소가 저장된다.

부모 클래스를 상속 받는 자식클래스에서 오버라이딩 된 함수들은
vtable의 함수 주소가 자식클래스의 함수로 추가된다
자식클래스에서 추가로 생성된 가상함수는 해당 클래스의 vtable 아래에 추가된다.

vtable을 보면 같은 함수더라도 Child 에서 오버라이딩 된 함수(func1,func3)는 주소가 다르고, 오버라이딩 되지 않은 함수(func2)는 Parent 클래스 vtable의 함수 주소와 같다 .

자식 클래스의 vtable은 부모 클래스의 vtable 값이 그대로 복사되며, 오버라이딩 된 함수만 주소가 새로 업데이트 된다고 한다. 그리고 만약 자식 클래스에 부모에 없는 새로운 가상함수를 추가할 경우, 객체의 vtable 마지막 부분에 추가된다.

vtable에는 virtual 로 선언된 가상 함수만 저장된다. SouceCode를 보면 Parent에 func4만 일반 멤버 함수로 선언되어 있다. [image-1]에서 Parent 클래스 vtable을 보면 func4는 없는 것을 확인할 수 있다.

#include <iostream>
#include <cstring>
#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;

class AAA 
{
private:
	int num1;
public:
	virtual void Func1() {
		cout << "Func1" << endl;
	}
	virtual void Func2() {
		cout << "Func2" << endl;
	}
};


class BBB : public AAA 
{
private:
	int num2;
public:
	virtual void Func1() {
		cout << "BBB::Func1" << endl;
	}
	void Func3() {
		cout << "Func3" << endl;
	}
};


int main(const int argc, const char* const argv[])
{
	AAA* aptr = new AAA();
	aptr->Func1();
	BBB* bptr = new BBB();
	bptr->Func1();
	return 0;
}

A클래스에 virtual로 선언된 가상함수가 존재한다. 이렇듯 한 개 이상의 가상함수를 포함하는 클래스
대해서는 컴파일러가 다음 표와 같은 형태의 '가상함수 테이블'이란 것을 만든다.
이를 간단히 'V-Table(Virtual Table)'이라고도 하는데, 이는 실제 호출되어야 할 함수의 위치정보를 담고
있는 테이블이다.

위의 가상함수 테이블을 보면, key가 있고 value가 있다.
key는 호출하고자 하는 함수를 구분지어주는 구분자의 역할을 한다.
value는 구분자에 해당하는 함수의 주소정보를 알려주는 역할을 한다.
그래서 AAA객체의 Func1함수를 호출해야 할 경우, 위의 테이블에 첫 번째 행의 정보를 참조하여, 0x1
번지에 등록되어 있는 Func1함수를 호출하게 되는 것이다.
BBB클래스 역시 한 개 이상의 가상함수를 포함하고 있으므로 다음의 형태로 가상함수 테이블이 생성된다.

0개의 댓글