Loading [MathJax]/jax/output/CommonHTML/jax.js
본문 바로가기
개발일지/Unreal&C++

[C++] 가상 함수와 비 가상 함수

by 쫌눈 2025. 3. 18.
728x90
반응형

가상함수는 다형성을 구현하는 핵심 메커니즘으로, 객체지향 프로그래밍에서 중요한 역할을 한다.

비 가상함수는 성능이 중요한 경우에 선호되며, 가상함수는 유연한 설계가 필요한 경우에 사용된다.

이는 C++에서 중요한 개념이다.

 

호출 방식

비 가상 함수 정적 바인딩을 사용한다.staticbinding
컴파일 시간에 어떤 함수를 호출할 지 결정된다.
객체의 선언된 타입을 기준으로 함수가 호출된다.
가상 함수 동적 바인딩을 사용한다.dynamicbinding
런타임에 어떤 함수를 호출할지 결정된다.
객체의 실제 타입을 기준으로 함수가 호출된다.

메모리 구조

비 가상 함수 클래스의 모든 객체가 함수를 공유한다.
별도의 메모리 공간에 위치하며, 객체 내부에 존재하지 않는다.
가상 함수 가상함수 테이블을 사용한다.vtable
클래스에 가상함수가 하나라도 있으면 vtable이 생성된다.
각 객체는 vtable을 가리키는 포인터를 가진다.

비 가상함수

가상함수


동작 원리

비 가상 함수

  1. 컴파일러가 객체의 선언된 타입을 확인
  2. 해당 타입의 클래스에 정의된 함수를 직접 호출

가상함수

  1. 객체의 vtable참조
  2. vtable에서 해당 함수의 주소 찾기
  3. 찾은 주소의 함수를 호출

가상함수의 실제 호출은 다음과 같이 변환된다.

  • vtable
    • 각 클래스 마다 생성되는 가상 테이블. 가상함수의 주소 저장
    • 상속받은 가상함수가 오버라이딩 되면 파생 클래스의 함수 주소로 업데이트
  • vptr
    • 객체에 숨겨진 포인터, 해당 객체가 참조하는 클래스의 vtable주소
    • 객체가 생성될 때 컴파일러가 vptr을 초기화

가상함수 호출 과정

  1. 객체 포인터ptr를 통해 vtable 접근
  2. vtable에서 호출할 함수 주소 검색
  3. 해당 함수의 주소로 이동하여 호출, 객체포인터 전달
ptr->VirtualFunc();

(*ptr->vptr[n])(ptr)
더보기

예제 코드

class Base {
public:
    virtual void func() { std::cout << "Base\n"; }
};

class Derived : public Base {
public:
    void func() override { std::cout << "Derived\n"; }
};

int main() {
    Base* obj = new Derived();
    obj->func(); // 가상 함수 호출
    delete obj;
}
  1. obj->func는 컴파일러에 의해 다음과 같이 변환된다.
    obj>vptr[0]obj;
    여기서 *obj->vptr : 객체 obj의 vtable
    [0]은 vtable에서 첫번째 항목func()
  2. 실행 시 obj는 실제로 Derived객체를 가리킨다. obj->vptr은 Derived클래스의 vtable을 참조한다.
  3. Derived의 vtable에서 첫 번째 항목은vptr[0]은 Derived::func의 주소를 가진다.
  4. obj>vptr[0]obj; 여기서 obj를 전달하는 이유는 함수 내부에서 호출될 객체의 멤버변수에 접근해야하기 때문

 


성능과 유연성

비 가상 함수 호출 속도가 빠르다
컴파일 시간에 결정되어 최적화에 용이하다
다형성을 지원하지 않는다
가상 함수 호출 시 약간의 오버헤드가 발생한다.
런타임 다형성을 지원하여 유연한 설계가 가능하다
상속과 오버라이딩을 통한 확장성이 높다.

 

728x90
반응형