가상 함수는 클래스에서 다형성을 구현하는 방법입니다.
이는 실지로는 가상 함수 테이블(Virtual Table)을 통해 구현되기 때문에
이 특성을 이용해서 본래 구현된 가상 함수나 메소드가 아닌 다른 함수나 메소드가 실행되게 할 수 있습니다.
이런 경우 코드를 보는게 훨씬 빠르기 때문에 아래 코드를 보시면
Test 클래스를 상속 받아 TTest 클래스가 존재하고
add 라는 가상 함수가 오버라이딩되어 있습니다.
add 함수는 주어진 정수 값이 일정한 값을 더해서 리턴하는 간단한 테스트용 함수입니다.
그래서 Test::add(10) 을 호출하면 11이 리턴.
TTest::add(10) 을 호출하면 12가 리턴 됩니다.
그런데, add함수를 전혀 다른 클래스의 내가 원하는 함수(이 함수는 가상함수이던 아니던 관계 없습니다)로
바꿔치기하는 예제입니다.
//---------------------------------------------------------------------------
void __fastcall TFormMain::FormCreate(TObject *Sender)
{
class Test
{
public:
int v;
virtual int add(int a)
{
return a + 1;
}
};
class TTest : public Test
{
public:
virtual int add(int a)
{
return a + 2;
}
};
TTest aa;
//aa.v = 100;
int n = aa.add(10);
Caption = n; // 확인: 12가 리턴됨.
Test *bb = &aa;
typedef int (__closure *TFunc2)(int);
TFunc2 func = &_addtest;
asm
{
lea eax, [bb]
mov eax, [eax]
lea edx, [func]
mov [eax], edx
};
n = bb->add(10);
Caption = n; // 확인: n 값은 15가 리턴됨.
}
// 이 메소드로 바꿔치기 할 것입니다.
int TFormMain::_addtest(int a)
{
return a + 5;
}
//---------------------------------------------------------------------------
이 코드에 보시면 버추얼 메소드 TTest :: add 를
다른 클래스의 메소드 _addtest로 바꿔치기한 것입니다.
그래서 bb->add(10)을 호출하면
TFormMain 클래스의 바꿔치기한 메소드가 실행되어 15를 리턴합니다.
심심할 때 보는 팁이었습니다.
팁 속에 팁이라고나 할까요.
위 코드를 보시면 어떻게 클래스 함수 포인트를 조작하는지 알수 있습니다.
이렇게 조작이 가능한 이유는,
VirtualTable 자체가 _DATA 영역에 놓여 있어 수정이 가능하기 때문입니다.
VirtualTable을 가르키는 클래스 인스턴스의 선두에 놓이는 VirtualTable Pointer는
위 예제의 경우는 인스턴스를 스택에 생성했으므로 스택에 놓이는 거고,
만일 전역 변수이면 _DATA 정적 데이타 영역에 놓이고
new로 생성하면 힙에 놓이게 됩니다.