프로그래밍C++ 개발자

C++에서 virtual table (vtable)이란 무엇인가요? 컴파일러는 동적 다형성을 어떻게 구현하며, 이 메커니즘과 관련된 세부 사항은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

C++에서 동적 다형성은 가상 함수 메커니즘을 통해 vtable (virtual table)이라는 특별한 테이블을 사용하여 구현됩니다. 적어도 하나의 가상 함수를 갖는 클래스에 대해 컴파일러는 vtable을 생성합니다: 이는 클래스의 가상 함수에 대한 포인터 배열입니다. 이러한 클래스의 각 객체는 vtable에 대한 숨겨진 포인터인 vptr (virtual pointer)를 저장합니다.

가상 함수가 있는 클래스의 객체가 생성될 때 vptr은 객체 클래스에 해당하는 vtable 테이블을 가리키도록 초기화됩니다. 가상 함수를 호출하면 호출은 직접적으로 이루어지지 않고, vtable에 저장된 주소를 통해 수행되며, 객체의 실제 타입에 따라 필요한 구현이 선택됩니다 (기본 타입을 통해 호출할 때도 마찬가지입니다).

예시:

class Base { public: virtual void foo() { std::cout << "Base::foo() "; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo() "; } }; void call_foo(Base* obj) { obj->foo(); // vtable을 통한 호출 }

foo 호출은 vtable에 대한 접근이 될 것입니다: 만약 Derived에 있다면, 재정의된 함수가 호출됩니다.

세부 사항:

  • 클래스에 가상 함수가 포함되어 있지 않으면, vtable과 vptr는 추가되지 않습니다.
  • 가상 메서드는 인라인되지 않으므로 종종 성능이 저하됩니다.
  • 클래스 인스턴스의 크기가 증가합니다 (vptr 크기 — 일반적으로 4/8 바이트).
  • 가상 호출은 직접 호출보다 느립니다.

함정 질문.

"메서드가 virtual로 선언되었지만 파생 클래스에서 재정의되지 않으면 그 메서드는 여전히 가상 메서드인가요? 이런 경우 vtable을 통한 호출은 작동할까요?"

답변: 네, 함수가 기본 클래스에서 virtual로 선언되면 모든 자손에서 가상 함수로 남아 있으며, 재정의되지 않았더라도 마찬가지입니다. 기본 포인터를 통한 호출은 어떤 경우에도 vtable을 통해 진행됩니다. 만약 메서드가 재정의되지 않았다면, 기본 클래스의 버전이 호출됩니다.

예시:

struct Base { virtual void foo() { std::cout << "B "; } }; struct Derived : Base { }; Base* obj = new Derived(); obj->foo(); // "B"를 출력하지만 vtable을 통한 호출!

주제에 대한 세부 사항을 모르면 발생할 수 있는 실제 오류의 예.


이야기

대규모 프로젝트에서 가상 함수가 포함된 기본 클래스에서 소멸자를 가상으로 선언하는 것을 잊었습니다. 이로 인해 메모리 누수가 발생했습니다 — 기본 포인터로 삭제할 때 자손의 소멸자는 호출되지 않았습니다.


이야기

클래스에서 가상 메서드가 선언되었으나, 실수로 상속 클래스에서 숨겨졌습니다 (재정의가 아닌 다른 서명/인자를 가진 경우). 기본 클래스에 대한 호출이 필요한 함수로 전달되지 않아 올바르지 않은 동작을 초래했습니다.


이야기

다중 상속 후 vtable의 업데이트가 오류를 발생시켰습니다: 복잡한 클래스 계층 구조에서 vptr 오프셋이 고려되지 않아 잘못된 메서드 호출이 발생했습니다.