C++ vtable 에러 | "undefined reference to vtable" 링커 에러 해결
이 글의 핵심
C++ vtable 에러에 대한 실전 가이드입니다.
들어가며: “undefined reference to vtable for MyClass"
"가상 함수를 선언했더니 링커 에러가 나요”
C++에서 가상 함수를 선언만 하고 정의하지 않으면 vtable 링커 에러가 발생합니다.
// ❌ vtable 에러
class Base {
public:
virtual void foo(); // 선언만
virtual ~Base(); // 선언만
};
// 링크 에러:
// undefined reference to `vtable for Base'
이 글에서 다루는 것:
- vtable이란?
- vtable 에러 원인
- 해결 방법
- 순수 가상 함수
목차
1. vtable이란?
vtable (Virtual Table)
vtable은 가상 함수 테이블로, 런타임에 어떤 함수를 호출할지 결정하는 테이블입니다.
class Base {
public:
virtual void foo() {
std::cout << "Base::foo\n";
}
virtual void bar() {
std::cout << "Base::bar\n";
}
};
class Derived : public Base {
public:
void foo() override {
std::cout << "Derived::foo\n";
}
};
// vtable 구조 (개념적)
// Base vtable:
// [0] -> Base::foo
// [1] -> Base::bar
//
// Derived vtable:
// [0] -> Derived::foo
// [1] -> Base::bar
vtable 생성 시점
컴파일러는 첫 번째 비인라인 가상 함수가 정의된 번역 단위에 vtable을 생성합니다.
2. vtable 에러 원인
원인 1: 가상 함수 정의 누락
// ❌ 정의 없음
// base.h
class Base {
public:
virtual void foo(); // 선언만
virtual ~Base(); // 선언만
};
// 링크 에러: undefined reference to `vtable for Base'
원인 2: 소멸자 정의 누락
// ❌ 소멸자 정의 없음
class Base {
public:
virtual ~Base(); // 선언만
virtual void foo() {
std::cout << "foo\n";
}
};
// 링크 에러: undefined reference to `vtable for Base'
원인 3: 순수 가상 함수 구현
// ❌ 순수 가상 함수를 일반 함수처럼 선언
class Base {
public:
virtual void foo(); // = 0 없음
};
// Base를 인스턴스화하려고 하면 링크 에러
3. 해결 방법
해결책 1: .cpp에 정의 추가
// ✅ base.h
class Base {
public:
virtual void foo();
virtual ~Base();
};
// ✅ base.cpp
void Base::foo() {
std::cout << "Base::foo\n";
}
Base::~Base() {
std::cout << "~Base\n";
}
해결책 2: 헤더에 인라인 정의
// ✅ base.h
class Base {
public:
virtual void foo() {
std::cout << "Base::foo\n";
}
virtual ~Base() {
std::cout << "~Base\n";
}
};
해결책 3: = default 사용
// ✅ base.h
class Base {
public:
virtual void foo() {
std::cout << "Base::foo\n";
}
virtual ~Base() = default; // 기본 구현
};
4. 순수 가상 함수
순수 가상 함수는 = 0
// ✅ 순수 가상 함수
class Base {
public:
virtual void foo() = 0; // 순수 가상
virtual ~Base() = default;
};
class Derived : public Base {
public:
void foo() override {
std::cout << "Derived::foo\n";
}
};
// Base b; // 컴파일 에러: 추상 클래스
Derived d; // OK
순수 가상 소멸자는 정의 필요
// ✅ 순수 가상 소멸자
class Base {
public:
virtual ~Base() = 0; // 순수 가상
};
// 정의 필수!
Base::~Base() {
std::cout << "~Base\n";
}
class Derived : public Base {
public:
~Derived() override {
std::cout << "~Derived\n";
}
};
실전 예시
예시 1: 인터페이스 클래스
// ✅ 인터페이스
class ILogger {
public:
virtual void log(const std::string& msg) = 0;
virtual ~ILogger() = default;
};
class ConsoleLogger : public ILogger {
public:
void log(const std::string& msg) override {
std::cout << msg << '\n';
}
};
예시 2: 추상 베이스 클래스
// ✅ 추상 클래스
class Shape {
public:
virtual double area() const = 0;
virtual double perimeter() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
double radius_;
public:
Circle(double r) : radius_(r) {}
double area() const override {
return 3.14159 * radius_ * radius_;
}
double perimeter() const override {
return 2 * 3.14159 * radius_;
}
};
정리
vtable 에러 해결
| 원인 | 해결책 |
|---|---|
| 가상 함수 정의 없음 | .cpp에 정의 추가 |
| 소멸자 정의 없음 | = default 사용 |
| 순수 가상 표시 없음 | = 0 추가 |
| 순수 가상 소멸자 | 정의 제공 |
핵심 규칙
- 모든 가상 함수에 정의 제공 (순수 가상 제외)
- 순수 가상 함수는 = 0
- 순수 가상 소멸자도 정의 필요
- = default 활용
체크리스트
- 모든 가상 함수에 정의가 있는가?
- 가상 소멸자에 정의가 있는가?
- 순수 가상 함수는 = 0으로 표시했는가?
- 순수 가상 소멸자에 정의를 제공했는가?
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ 가상 함수 | virtual function 가이드
- C++ 가상 소멸자 | 메모리 누수 방지
- C++ 다형성 | Polymorphism 가이드
- C++ 링커 에러 | multiple definition 해결
마치며
vtable 에러는 가상 함수 정의 누락으로 발생합니다.
핵심 원칙:
- 모든 가상 함수에 정의 제공
- 순수 가상 함수는 = 0
- = default 활용
가상 함수를 선언했다면 반드시 정의를 제공하세요.
다음 단계: vtable을 이해했다면, C++ 가상 함수 가이드에서 더 깊이 배워보세요.
관련 글
- C++ 시리즈 전체 보기
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ ADL |
- C++ Aggregate Initialization |