[C++] 가상 함수와 V-Table: 동적 바인딩의 내부 메커니즘
객체지향의 꽃 '다형성'을 구현하는 하부 구조와 성능 최적화
1. V-Table (가상 함수 테이블)의 동작 원리
C++에서 virtual 키워드를 사용하는 순간, 컴파일러는 단순히 함수를 호출하는 코드를 생성하는 것이 아니라 복잡한 메모리 구조를 설계합니다.
- 📌 V-Table 생성: 클래스에 가상 함수가 하나라도 있으면, 해당 클래스만을 위한 함수 포인터 배열(V-Table)이 읽기 전용 메모리 영역에 생성됩니다.
- 📌 V-Ptr (가상 함수 포인터): 객체가 생성될 때, 메모리 레이아웃 최상단에 V-Table의 주소를 가리키는 포인터가 자동으로 추가됩니다.
- 📌 오버라이딩(Overriding): 자식 클래스가 부모의 함수를 재정의하면, 자식 클래스의 V-Table 슬롯에 있는 부모 함수의 주소가 자식 함수의 주소로 교체(Overwrite)됩니다.
2. 오버라이딩(Overriding) vs 하이딩(Hiding)
가장 혼동하기 쉬운 개념입니다. 핵심은 "가상 함수냐 아니냐"에 있습니다.
| 구분 | 오버라이딩 (Overriding) | 하이딩 (Hiding) |
|---|---|---|
| Virtual 여부 | 필수 (부모 함수) | 없음 |
| 바인딩 시점 | 런타임 (동적 바인딩) | 컴파일 타임 (정적 바인딩) |
| 호출 기준 | 실제 객체의 타입 | 포인터 변수의 타입 |
⚠️ 주의: 하이딩이 발생하면 부모 포인터로 자식 객체를 가리킬 때 자식의 함수가 호출되지 않고 부모의 함수가 호출됩니다. 이는 객체지향의 다형성을 깨뜨리는 주범이 되므로, 의도치 않은 하이딩을 막기 위해 override 키워드를 명시하는 습관이 중요합니다.
3. 최적화의 열쇠: final 키워드
C++11에서 도입된 final은 설계를 명확히 할 뿐만 아니라 성능 최적화의 도구로 사용됩니다.
① 가상 호출 최적화 (Devirtualization)
가상 함수는 기본적으로 V-Ptr -> V-Table -> 주소 점프라는 간접 참조 과정을 거칩니다. 하지만 함수에 final이 붙어있으면 컴파일러는 이 함수가 더 이상 오버라이딩되지 않음을 확신하고, 직접 호출 코드로 변경하여 성능을 높입니다.
② 설계적 의도
"이 로직은 여기서 완성되었으므로 하위 클래스에서 수정하지 마라"는 가이드라인을 동료 개발자에게 명확히 전달합니다.
4. 모바일 환경에서의 고려사항
Android Vulkan과 같은 최신 모바일 API 환경에서도 CPU 성능 최적화는 여전히 중요합니다.
- 캐시 효율성: 동적 바인딩에 의한 간접 참조는 CPU 캐시 미스(Cache Miss) 확률을 높일 수 있습니다.
- 전략적 판단: 빈번하게 호출되는 틱(Tick) 함수나 루프 내부에서는 가급적
final을 활용하여 탈가상화를 유도하거나, 상속 깊이를 얕게 유지하는 것이 유리합니다.
포스팅이 도움이 되셨다면 공감과 댓글 부탁드립니다! :)
'C++ 공부' 카테고리의 다른 글
| [C++ Study] 가상상속과 연산자 오버로딩 (0) | 2026.04.28 |
|---|---|
| [C++ Study] 가상 함수부터 인터페이스까지: 객체지향의 핵심 정리 (0) | 2026.04.24 |
| [C++ Study] 객체지향 설계의 핵심: 관계 정의와 다형성 (Is-A, Has-A, Casting) (0) | 2026.04.18 |
| [C++ Study] 키워드와 메모리 구조를 통한 안전한 코드 설계 (explicit, 형변환연산자, cast, mutable, 메모리영역) (0) | 2026.04.17 |
| [C++ Study] friend, 상속의 경계, 그리고 static의 메모리 전략 (0) | 2026.04.10 |