본문 바로가기

C++

가상 함수 테이블(Virtual Function Table)

C++ 은 다형성을 지원하기 위해 가상 키워드(virtual)를 지원한다.

상속 관계에서 하위 클래스에서 부모의 함수를 재정의하기 위해 키워드를 사용한다.

 

- 가상 함수를 하나라도 가진 클래스는 '가상 함수 테이블'를 가지게 된다.

- 가상 함수 테이블을 이용하여 자신의 클래스에 맞는 함수를 찾아 호출하게 된다.

 

가상 함수가 없는 경우

class Parent
{
public:
    void Print()
    {
        cout << "Parent" << endl;
    }
};

class ChildA : public Parent
{
public:
    void Print()
    {
        cout << "ChildA" << endl;
    }
};

class ChildB : public Parent
{
    void Print()
    {
        cout << "ChildB" << endl;
    }
};

int main()
{
    Parent* pA = new ChildA();
    Parent* pB = new ChildB();
    pA->Print();
    pB->Print();
    delete pA;
    delete pB;

    return 0;
}

결과  
Parent
Parent

가상 함수가 존재하는 경우

class Parent
{
public:
    virtual void Print()
    {
        cout << "Parent" << endl;
    }
};

class ChildA : public Parent
{
public:
    void Print() override
    {
        cout << "ChildA" << endl;
    }
};

class ChildB : public Parent
{
    void Print() override
    {
        cout << "ChildB" << endl;
    }
};

int main()
{
    Parent* pA = new ChildA();
    Parent* pB = new ChildB();
    pA->Print();
    pB->Print();
    delete pA;
    delete pB;
    
    return 0;
}

결과
ChildA
ChildB
Parent* pA = new ChildA();
pA->Print();
delete pA;

위 코드에 대하여 설명하면 자료형 타입과 동적 할당하는 타입은 다르지만 virtual 키워드가 없다면 

동적할당한 자료형과 무관하게 실제 자료형 타입의 함수를 호출해서 "Parent"가 출력되어야 한다.

 

하지만! 부모(Parent) 클래스에서 Print() 함수에 재정의(virtual) 키워드가 붙어있으므로 가상 함수 테이블이

만들어지며, 가상 함수 테이블을 가리키는 포인터는 런타임(Run-Time)에 가상 함수 테이블을 참조하여

자신이 호출해야 할 함수를 결정하게 된다. 


각 클래스의 가상 함수 테이블

Parent pP = new Parent() 객체의 가상 함수 테이블(__vfptr)
Parent* pA = new ChildA() 객체의 가상 함수 테이블(__vfptr)
Parent* pB = new ChildB() 객체의 가상 함수 테이블(__vfptr)

위 그림과 같이 부모의 자료형으로 생성된 객체라도 동적 할당된 자신의 클래스 내부의 Print() 함수를 호출한다.

당연한 이야기지만 재정의가 되어있지 않는 Print() 함수였다면 부모(Parent) 클래스의 Print()만 세번 호출된다.


가상 함수 주의사항 !!

- 아래 코드에서 ChildB 클래스에서 void VAFunction() 함수는 virutal, override 키워드가 생략된것이다.

  절대! 부모 클래스에서 virtual 키워드가 있지만 자식에서 사용하지 않았다고 없다고 생각하지말자.

class ChildA : public Parent
{
public:
    void VAFunction() override
    {
        cout << "ChildA Virtual Function" << endl;
    }

    virtual void VAFunction2()
    {
        cout << "ChildA Virtual Function2" << endl;
    }

    void Function()
    {
        cout << "ChildA Function" << endl;
    }
};

class ChildB : public ChildA
{
    void VAFunction()
    {
        cout << "ChildB Virtual Function" << endl;
    }    
    void VAFunction2() override
    {
        cout << "ChildB Virtual Function2" << endl;
    }    
    void Function()
    {
        cout << "ChildB Function" << endl;
    }
};

'C++' 카테고리의 다른 글

다중 상속(Diamond of Death)의 문제점  (0) 2019.07.10
상속을 사용하는 의미  (0) 2019.07.09
순수 가상 함수(Pure Virtual Function)  (0) 2019.07.09
객체지향이란?  (0) 2019.07.09
객체지향 - SOLID 개발 5대 원리  (0) 2019.07.09