클래스를 struct와 전역 함수로 풀어서 나타내면 다음과 같다.
struct Data
{
int data;
void (*ShowData)(Data*);
void (*Add)(Data*, int);
}
void ShowData(Data* this) { cout << "data : "<< this->data<< endl; }
void Add(Data* this, int num) { this->data += num; }
int main()
{
Data obj1 = {15, ShowData, Add};
Data obj2 = {10, ShowData, Add};
obj1.Add(&obj1, 5);
obj1.ShowData(&obj1);
}
class도 똑같다. 같은 class형식의 여러 객체가 생성된다해도 멤버함수는 하나만 존재하고 이를 각각의 함수 포인터가 가리키는 형태가 된다.
하지만 함수호출 시 각 객체로 정보가 전달되고(this) 이를 기반으로 함수를 실행하므로 논리적으론 객체안에 멤버함수가 존재한다고 할 수 있다.
하지만 virtual 선언된 멤버함수가 하나라도 있을시에는 그 구조가 바뀐다.
멤버함수를 Key로 갖고 그 위치를 Value로 가지는 가상함수 테이블이 class마다 생기게 된다. 멤버함수가 호출된다하면 virtual table에서 함수의 주소를 탐색/추출한 다음 그 주소의 함수를 호출한다.
위오 같은 클래스 AAA, BBB가 존재한다고 하면 virtual table은
AAA class
BBB class
이때 오버라이드한 Func1의 경우 BBB class의 virtual table에서 AAA::Func1은 삭제되고 BBB::Func1만 남게 된다.
따라서 BBB객체에서 Fuc1을 호출하기 위해 virtual table을 탐색하면 BBB의 Func1밖에 없으므로 BBB::Fun1이 호출된다.
여러 개의 class를 상속받는 것
class Derived :public FirstBase, public SecondBase { }
base1에 Fun1이 있고 base2에도 Fun1가 있다고 가정했을 때
이 둘을 다중상속하는 Derived의 객체를 통해 Fun1을 호출한다했을 때 어떤 base의 Fun1을 호출할지 모호해진다.
위와 같은 상속구조를 가지고 있는 LastDerived 객체가 있다하면
같이 Base의 멤버가 2개 존재하게 된다.
따라서 이를 해결하기 위해 virtual 상속을 한다.
class Derived : virtual public Base { }
virtual 상속으로 base를 상속하게 되면 Base의 멤버를 하나만 포함하게 된다.