Virtual Table(가상 함수 테이블, Vtable)은 C++에서 다형성(Polymorphism)을 구현하기 위해 컴파일러가 내부적으로 생성하는 함수 포인터 테이블입니다. 가상 함수가 있는 클래스마다 하나씩 생성되며, 런타임에 올바른 함수를 호출하기 위한 핵심 메커니즘입니다.
// 객체 생성 시 Vptr 초기화
Base* obj = new Derived(); // Derived의 Vtable을 가리키도록 Vptr 설정


void callFunction(Base* obj) {
obj->func1(); // 런타임에 올바른 함수 호출
}
int main() {
Base* base = new Base();
Base* derived = new Derived();
callFunction(base); // "Base::func1()" 출력
callFunction(derived); // "Derived::func1()" 출력
delete base;
delete derived;
return 0;
}
obj->func1() 호출// 잘못된 예제 - 소멸자가 가상 함수가 아님
class BadBase {
public:
virtual void func() { cout << "BadBase::func()" << endl; }
~BadBase() { cout << "BadBase 소멸자" << endl; } // 비가상 소멸자!
};
class BadDerived : public BadBase {
private:
int* data;
public:
BadDerived() {
data = new int[1000]; // 메모리 할당
cout << "BadDerived 생성자 - 메모리 할당" << endl;
}
~BadDerived() {
delete[] data; // 메모리 해제
cout << "BadDerived 소멸자 - 메모리 해제" << endl;
}
void func() override { cout << "BadDerived::func()" << endl; }
};


void demonstrateProblem() {
cout << "=== 비가상 소멸자 문제 시연 ===\n";
BadBase* obj = new BadDerived(); // 파생 클래스 객체 생성
// 가상 함수는 정상 동작 (Vtable 통해 호출)
obj->func(); // "BadDerived::func()" 출력
// 소멸자 호출 시 문제 발생!
delete obj; // BadBase::~BadBase()만 호출됨!
// BadDerived::~BadDerived() 호출 안됨!
// → 메모리 누수 발생!
}
/* 문제가 되는 출력:
=== 비가상 소멸자 문제 시연 ===
BadDerived 생성자 - 메모리 할당
BadDerived::func()
BadBase 소멸자 ← BadDerived 소멸자가 호출되지 않음!
→ 메모리 누수 및 리소스 누수!
*/
class GoodBase {
public:
virtual void func() { cout << "GoodBase::func()" << endl; }
virtual ~GoodBase() { cout << "GoodBase 소멸자" << endl; } // 가상 소멸자
};
class GoodDerived : public GoodBase {
private:
int* data;
public:
GoodDerived() {
data = new int[1000];
cout << "GoodDerived 생성자" << endl;
}
~GoodDerived() override {
delete[] data;
cout << "GoodDerived 소멸자" << endl;
}
void func() override { cout << "GoodDerived::func()" << endl; }
};
void demonstrateSolution() {
cout << "=== 가상 소멸자 해결책 ===\n";
GoodBase* obj = new GoodDerived();
obj->func(); // Vtable을 통한 가상 함수 호출
// delete obj 실행 시:
// 1. obj의 Vptr을 통해 GoodDerived Vtable 접근
// 2. 소멸자 엔트리에서 GoodDerived::~GoodDerived() 주소 획득
// 3. GoodDerived 소멸자 실행 → 메모리 해제
// 4. 자동으로 부모 클래스 소멸자도 연쇄 호출
delete obj; // 올바른 소멸자 체인 실행!
}
/* 올바른 출력:
=== 가상 소멸자 해결책 ===
GoodDerived 생성자
GoodDerived::func()
GoodDerived 소멸자 ← 파생 클래스 소멸자 먼저 호출
GoodBase 소멸자 ← 부모 클래스 소멸자 연쇄 호출
*/
// 소멸 과정에서의 Vtable 상태 변화
void destructionProcess() {
GoodBase* obj = new GoodDerived();
delete obj; // 소멸 과정 시작
}
소멸 과정의 Vtable 상태 변화:
1. delete obj 호출
[객체] → [GoodDerived Vtable] → GoodDerived::~GoodDerived() 호출
- GoodDerived 소멸자 실행 중
[객체] → [GoodDerived Vtable] (여전히 GoodDerived 타입)
- GoodDerived 소멸자 완료, GoodBase 소멸자 시작
[객체] → [GoodBase Vtable] (Vtable이 부모로 변경됨)
- GoodBase 소멸자 실행 중
[객체] → [GoodBase Vtable]
- 완전 소멸 완료
class ResourceHolder : public BadBase {
private:
std::unique_ptr<int[]> buffer;
std::fstream file;
public:
ResourceHolder() {
buffer = std::make_unique<int[]>(1000);
file.open("temp.txt", std::ios::out);
}
~ResourceHolder() {
// 이 소멸자가 호출되지 않으면:
// 1. unique_ptr 소멸자 호출 안됨 → 메모리 누수 (이론적으로)
// 2. fstream 소멸자 호출 안됨 → 파일 핸들 누수
file.close();
cout << "ResourceHolder 리소스 정리" << endl;
}
};
// RAII가 제대로 동작하지 않는 예제
void raiiBroken() {
BadBase* obj = new ResourceHolder();
try {
// 일부 작업 수행
throw std::runtime_error("예외 발생");
}
catch (...) {
delete obj; // ResourceHolder 소멸자가 호출되지 않음!
// → RAII 패턴 완전 파괴
}
}
// 많은 컴파일러에서 경고 발생
// warning: 'class BadBase' has virtual functions but non-virtual destructor
class BadBase {
public:
virtual void func() = 0;
~BadBase() {} // 경고 발생!
};
class CorrectBase {
public:
virtual void func() = 0;
virtual ~CorrectBase() = default; // 가상 소멸자 명시
// 또는 삭제된 소멸자로 다형성 사용 금지
// ~CorrectBase() = delete; // 다형성 사용 시 컴파일 에러
};
// 스마트 포인터 사용으로 안전성 보장
#include <memory>
void modernApproach() {
auto obj = std::make_unique<GoodDerived>();
obj->func();
// 스코프 종료 시 자동으로 올바른 소멸자 호출
}
#include <iostream>
#include <typeinfo>
using namespace std;
class Base {
public:
Base() {
cout << "Base 생성자: Vtable = " << typeid(*this).name() << endl;
virtualFunc();
}
virtual void virtualFunc() {
cout << "Base::virtualFunc() - Vtable이 Base를 가리킴" << endl;
}
virtual ~Base() {
cout << "Base 소멸자: Vtable = " << typeid(*this).name() << endl;
virtualFunc();
}
};
class Derived : public Base {
public:
Derived() : Base() {
cout << "Derived 생성자: Vtable = " << typeid(*this).name() << endl;
virtualFunc();
}
void virtualFunc() override {
cout << "Derived::virtualFunc() - Vtable이 Derived를 가리킴" << endl;
}
~Derived() {
cout << "Derived 소멸자: Vtable = " << typeid(*this).name() << endl;
virtualFunc();
}
};
int main() {
cout << "=== 객체 생성 ===\n";
Derived d;
cout << "\n=== 정상 호출 ===\n";
d.virtualFunc();
cout << "\n=== 객체 소멸 ===\n";
return 0;
}

Virtual Table은 C++의 다형성을 구현하는 핵심 메커니즘으로, 각 클래스마다 컴파일 타임에 생성되어 런타임에 올바른 함수 호출을 보장합니다. 특히 다음 사항들이 중요합니다.
Vtable의 동작 원리를 정확히 이해하고 올바른 설계 패턴을 적용하면, 안전하고 효율적인 객체지향 코드를 작성할 수 있습니다.