Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
05-06 20:35
관리 메뉴

nomad-programmer

[Programming/C++] 가상함수의 동작원리와 가상함수 테이블 본문

Programming/C++

[Programming/C++] 가상함수의 동작원리와 가상함수 테이블

scii 2021. 2. 3. 02:08

가상함수의 동작원리를 이해하면, C++이 C보다 느린 이유를 조금이나마 알 수 있다.

다음의 예를 살펴보자.

#include <iostream>
#include <cstring>

#pragma warning(disable: 4996)

using std::cout;
using std::cin;
using std::endl;


class AAA {
private:
    int num1;

public:
    virtual void Func1() {
        cout << "Func1" << endl;
    }

    virtual void Func2() {
        cout << "Func2" << endl;
    }
};

class BBB : public AAA {
private:
    int num2;

public:
    virtual void Func1() {
        cout << "BBB::Func1" << endl;
    }
    void Func3() {
        cout << "Func3" << endl;
    }
};


 int main(const int argc, const char* const argv[]) { 
     AAA* aptr = new AAA();
     aptr->Func1();
     
     BBB* bptr = new BBB();
     bptr->Func1();

     return 0;
}

/* 결과

Func1
BBB::Func1

*/

AAA클래스에 virtual로 선언된 가상함수가 존재한다. 이렇듯 한 개 이상의 가상함수를 포함하는 클래스에 대해서는 컴파일러가 다음 표와 같은 형태의 '가상함수 테이블'이란 것을 만든다.
이를 간단히 'V-Table(Virtual Table)'이라고도 하는데, 이는 실제 호출되어야 할 함수의 위치정보를 담고 있는 테이블이다.

Key Value
void AAA::Func1() 0x1024 번지
void AAA::Func2() 0x2048 번지

위의 가상함수 테이블을 보면, key가 있고 value가 있다.

  • key는 호출하고자 하는 함수를 구분지어주는 구분자의 역할을 한다.
  • value는 구분자에 해당하는 함수의 주소정보를 알려주는 역할을 한다. 

그래서 AAA객체의 Func1함수를 호출해야 할 경우, 위의 테이블에 첫 번째 행의 정보를 참조하여, 0x1024번지에 등록되어 있는 Func1함수를 호출하게 되는 것이다. 

BBB클래스 역시 한 개 이상의 가상함수를 포함하고 있으므로 다음의 형태로 가상함수 테이블이 생성된다.

Key Value
void BBB::Func1() 0x3072 번지
void AAA:Func2() 0x2048 번지
void BBB::Func3() 0x4096 번지

위의 가상함수 테이블을 보면, 다음과 같은 특징을 발견할 수 있다.

"AAA 클래스의 오버라이딩 된 가상함수 Func1에 대한 정보가 존재하지 않는다."

이렇듯, 오버라이딩 된 가상함수의 주소정보는 파생 클래스의 가상함수 테이블에 포함되지 않는다. 때문에 오버라이딩 된 가상함수를 호출하면, 무조건 가장 마지막에 오버라이딩을 한 파생 클래스의 멤버함수가 호출되는 것이다.


가상함수 테이블이 참조되는 방식

main함수가 호출되기 이전에 다음의 구조로 가상함수 테이블이 메모리 공간에 할당된다. 참고로 가상함수 테이블은 객체의 생성과 상관없이 메모리 공간에 할당된다.
이는 가상함수 테이블이 멤버함수의 호출에 사용되는 일종의 데이터이기 때문이다.

가상함수 테이블과 가상함수와의 관계

main함수가 호출되어 객체가 생성되고 나면, 다음의 구조로 참조관계를 구성한다. 참고로 아래의 그림은 AAA 객체와 BBB 객체가 각각 두 개씩 생성되었을 때를 예로 든 것이다.

객체의 가상함수 테이블 참조

위 그림에서 보듯이, AAA객체에는 AAA클래스의 가상함수 테이블의 주소 값이 저장되고, BBB객체에는 BBB클래스의 가상함수 테이블의 주소 값이 저장된다.

즉, 가상함수를 하나이상 멤버로 지니는 클래스의 객체에는 가상함수 테이블의 주소 값이 저장된다.

물론 이 주소 값은 프로그래머가 직접 참조할 수 있는 주소 값이 아니다. 다만 내부적으로 필요에 의해서 참조되는 주소 값일 뿐이다.

BBB클래스의 가상함수 테이블을 살펴보면, 오버라이딩 된 AAA클래스의 Func1함수에 대한 정보가 없음을 알 수 있다. 그래서 BBB클래스의 Func1함수가 대신 호출되는데, 이것이 바로 가상함수의 호출원리이다.


가상함수 테이블에 의한 속도 저하

가상함수가 포함되면, 가상함수 테이블이 생성되고, 또 이 테이블을 참조하여 호출될 함수가 결정되기 때문에 실행속도가 감소하기 마련이다. 하지만 그 속도의 차이가 극히 미미하고 또 이러한 단점에도 불구하고 가상함수는 많은 장점을 제공하기 때문에 유용하게 활용되는 것이다.

Comments