우리는 이미 오버라이딩에 대해 알고 있다.
간단하게 오버라이딩과 virtual의 차이에 대해 알아보면서 시작해보자.
class A
{
public:
void OverTest()
{ cout<<"Hi My name is A\n"; }
}
class B : public A
{
public:
void OverTest()
{ cout<<"Hi My name is B\n"; }
void main()
{
A* a = new A();
B* b = new B();
a->OverTest();
b->OverTest();
를 동작하게 된다면 어떻게 될까?

오버라이딩 되어서 원하는 결과를 얻었다.
하지만 여기서 아래와 같이 부모포인터에 자식 포인터를 할당해서 OverTest에 접근을 한다면?
A* a1 = new B();
a1->OverTest();

부모 포인터가 불리게 된다. 이런 경우 원하는 B를 찾기 위해 즉, 다형적으로 동작하기 위해 virtual 키워드를 붙인다.
class A
{
public:
virtual void OverTest()
{ cout<<"Hi My name is A\n"; }
}
class B : public A
{
public:
virtual void OverTest() override
{ cout<<"Hi My name is B\n"; }
override 키워드는 붙여도 되고 안붙여도 되는 걸로 알고 있다. 하지만 코드를 읽는 사람이 직관적으로 알 수 있게 명시적으로 붙여주는게 좋다.
위와 같이 작성된 코드를 동일하게 돌려준다면
A* a1 = new B();
a1->OverTest();

원하는 결과를 얻을 수 있다. 이는 virtual의 키워드가 붙는 순간 클래스 앞에 virtual function table이 생성되고 그 포인터가 놓이게 된다. 이를 바탕으로 함수가 호출될때, 즉, 컴파일 타임에 어떤 함수를 호출할지 찾아서 호출한다.
위의 경우는 이미 알고 있었던 내용이지만 한번 더 정리했다. 여기서 가장 헷갈리고 궁금했던 점은 이렇게 virtual된 함수를 부모 객체에서 virtual이 아닌 함수에서 호출하면 어떤 함수가 불릴지였다.
이를 위해 테스트를 해보았다.
class A
{
public:
void totalTest()
{ test(); }
virtual void test()
{ cout<<"This is A\n"; }
};
class B : public A
{
public:
virtual void test() override
{ cout<<"This is B\n"; }
};
class C : public B
{
public:
virtual void test() override
{ cout<<"this is C\n"; }
};
로 작성하였고, 테스트는
A* a = new C();
a->totalTest();
a->test();
A* a1 = new B();
a1->totalTest();
a1->test();
A* a2 = new A();
a2->totalTest();
a2->test();
B* b = new C();
b->totalTest();
b->test();
B* b1 = new B();
b1->totalTest();
b1->test();
//B* b2 = new A();
//당연하게 문제가 발생 자식 포인터에 부모 포인터를
//할당하려고 했으니깐
C* c = new C();
c->totalTest();
c->test();

아래와 같은 결과를 얻을 수 있었다.
즉, 부모 포인터에 넣는 자식의 함수가 호출되는 것을 확인 할 수 있었다. 나는 그 전까지는 외부에서 부르는 것만 해보았기 때문에 내부에서 부르면(아무리 자식포인터를 할당했다고 해도) 부모의 함수가 불리지 않을까 생각했는데, 생각해보면 컴파일 타임에 가상 함수 테이블이 불리면서 할당된 포인터에 접근하여 함수를 호출하는것 같다.