글을 정리하다, 우연히 예전에 가상함수에 대해 정리해 놓은 글을 찾아서, 다시 올려 본다.
^0^
-----------------------------------------------------------
가상함수는 일반적으로 base class type의 객체가 child class type으로 cast 되었을때,
호출되는 해당 함수를 cast되는 chid class type에 맞도록 런타임에 변형시켜주는 기술을 말한다.
용법은 다음과 같다.
class CBase
{
public:
virtual void VFunc() { printf("Base"); }
};
class CChild
{
public:
void VFunc() { printf("Child"); }
};
void main()
{
CBase* pBase = new CChild;
pBase->VFunc();
}
라고 했을때 실행 결과는??
가상 함수의 작동 원리에 의하여 Child 가 출력되는 것이다.
그러나 이렇게 설명하면 좀 알기 힘들고
더 자세히 얘기하면 이렇다는 것이다.
void main()
{
CChild child;
CBase* pBase;
pBase = (CBase*)(&child);
pBase->Func(); // A.
}
이것의 결과는 위 처럼 child 이다. 왜냐하면 Func()가 가상 함수이므로, 위와 같이 child객체의
가상 table 주소를 참조하여 함수를 호출키 때문이다.
그렇다면, Func()가 가상 함수가 아니라면, 답은 Base 일 것이다.
그렇다면 이것의 결과는??
void main()
{
CChild* pChild;
CBase base
pChild = (CChild*)(&base);
pChild->Func(); // B.
}
이 결과는 무엇일까??
정답은 가상이 아닐때는 Child 지만, 가상일때는 Base 라는 것이다.
이제 좀 감이 오는가??
즉, 정리하자면 이런 것이다.
C++의 일반적인 용법은, cast 를 받는 type에 함수가 결정되게 되어있다.
이것이 바로 컴파일 time에 함수가 정적으로 결정된다라는 것이다.
그러나 가상함수를 사용하면, 함수의 결정권이 cast되는 쪽으로 간다.
즉, 특정 포인터에 어떤 type이 캐스트 되어 넘어갈지 런타임에만 오직 알수 있게 될 것이며,
이런 런타임에 캐스트 되는 쪽을 기준으로 함수를 바로바로 작동케 해 것이 가상함수 라는 기술인 것이다.
따라서 가상 함수는 포인터로 인한 cast에는 여전히 잘 작동케 된다. A, B 처럼 말이다.
그러나 정적인 변환에는 유효하지 않다.
CBase base;
CChild child;
base = (CChild)child;
base.Func();
포인터 cast가 아닌 위와 같은 객체 변환의 경우, (내부적으로 객체의 멤버 변수가 단순 복사될 것이다.)
가상 table 주소는 그대로 이므로(복사 대상이 아니므로)
이때는 그냥 Base가 나오게 된다. - 가상이든, 아니든. |