[C++] 가상 함수/가상 함수 테이블의 동작 원리/다형성 핵심 원리

지즈·2025년 8월 23일
0

C++

목록 보기
3/5

클래스와 가상 함수 테이블

가상 함수 테이블(V-Table)과 객체의 관계

C++에서 어떤 클래스에 virtual 함수가 하나라도 선언되면, 컴파일러는 해당 클래스 전용의 가상 함수 테이블(V-Table) 을 생성합니다. 이 테이블은 클래스 단위로 하나만 존재하며, 이 클래스로부터 만들어지는 모든 객체는 자신이 속한 클래스의 V-Table을 가리키는 가상 함수 테이블 포인터(vptr) 를 내부에 가지게 됩니다. 즉, 객체마다 별도의 테이블을 갖는 것이 아니라, 클래스 단위로 공유하는 하나의 테이블을 참조하는 구조입니다.

V-Table의 원소와 코드 영역

가상 함수 테이블은 함수 포인터들의 배열이라고 볼 수 있습니다. 각 원소는 해당 클래스의 가상 함수 구현부가 실제로 메모리에 올라가 있는 코드 영역(.text 섹션) 의 시작 주소를 가리킵니다. 따라서 객체가 가상 함수를 호출하면, 내부의 vptr을 통해 자신이 참조하는 V-Table을 확인하고, 그 안에서 올바른 함수 주소를 찾아 실제 코드로 점프하게 되는 것입니다.



상속과 가상 함수

Base 클래스를 상속하는 Derived1 클래스와 Derived2 클래스가 있습니다.

상속 관계에서의 V-Table 생성 원리

자식 클래스에서 오버라이딩을 하지 않는 경우


C++ 컴파일러는 클래스마다 고유한 V-Table을 생성합니다. 자식 클래스에서 부모의 가상 함수를 재정의하지 않고, 새로운 가상 함수를 추가하지 않았다면 자식 클래스의 V-Table은 부모 V-Table의 원소와 완전히 동일한 함수 포인터들을 담게 됩니다. 중요한 점은, 부모의 V-Table을 그대로 재사용하는 것이 아니라 자식 전용의 V-Table이 새로 생성된다는 것입니다.



자식 클래스에서 오버라이딩을 하는 경우


자식 클래스에서 부모의 가상 함수를 재정의(override) 하면, 컴파일러는 자식 클래스 전용 V-Table을 생성할 때 부모의 V-Table 원소를 그대로 복사하지 않고, 해당 함수 슬롯을 자식 함수의 주소로 교체합니다.
즉, 같은 위치의 슬롯이지만 이제는 Base::f() 대신 Derived2::f()의 코드 영역 주소를 가리키게 되는 것이죠. 덕분에 부모 타입 포인터를 통해 함수를 호출하더라도, 실제 객체가 자식 타입이라면 자식에서 재정의한 함수가 호출됩니다. 이것이 C++ 다형성의 핵심 동작 원리입니다.



왜 별도의 V-Table을 만들까?

클래스의 확장성과 타입 구분성을 보장하기 위함입니다. 만약 자식이 나중에 새로운 가상 함수를 추가하거나 기존 함수를 재정의할 경우, 자식 전용 V-Table을 유지해야 안전하게 동작할 수 있습니다. 또한 RTTI(dynamic_cast, typeid)와 같은 기능은 V-Table을 통해 클래스 타입을 식별하는데, 자식이 부모와 V-Table을 공유해버리면 타입 판별이 불가능해집니다.


C++에서 다형성을 구현하기 위한 핵심 개념인 가상 함수와 가상 함수 테이블의 동작 원리에 대해 공부했습니다. 비록 가상 함수를 사용할 때 한 차례 더 참조가 일어나 성능상 약간의 비용이 발생할 수 있지만, 객체 지향의 본질인 다형성을 제대로 구현하기 위해 반드시 이해해야 하는 중요한 지식이라고 느꼈습니다.

profile
클라이언트 개발자가 되는 그 날까지 킵 고잉

0개의 댓글