프로그래밍 언어/C & C++ 정리

virtual 키워드 메서드

뽀또치즈맛 2025. 4. 16. 09:22

virtual 메서드 동작방식


override하지 않고 상속 관계의 자식클래스가
같은 이름의 함수를 정의하면,
각각의 동일한 이름의 함수가 재정의되지 않고 개별적으로 존재한다.

이처럼 메서드를 가리는 일을 방지하려면
virtual 키워드가 내부적으로 처리되는 과정을 이해할 필요가 있다.
C++에서 클래스를 컴파일하면
그 클래스에 있는 메서드를 모두 담은 바이너리 객체가 생성된다.

그런데 컴파일러는 virtual로 선언하지 않은
메서드를 호출하는 부분을
컴파일 시간에 결정된 타입의 코드로 교체한다.

이를 정적 바인딩 또는 이른 바인딩이라 부른다.

메서드를 virtual로 선언하면
vtalble(가상 테이블)이라 부르는 특수 메모리 영역을 활용해서
가장 적합한 구현 코드를 호출한다.

virtual 메서드가 하나라도 정의된 클래스에는
vtable이 하나씩 있는데,
이 클래스로 생성한 객체마다
vtable에 대한 포인터를 갖게 된다.

바로 이 vtable에 virtual 메서드의 구현 코드에 대한 포인터가 담겨 있다.
그러므로 객체에 대해 메서드를 호출하면
vtable을 보고 그 시점에 적합한 버전의 메서드를 실행한다.
이를 동적 바인딩 또는 늦은 바인딩이라 한다.

virtual 키워드가 필요한 이유


자바와 같은 언어는 기본적으로 virtual로 만들기 때문에
올바르게 오버라이드할 수 있었다.
C++은 virtual같은 키워드를 이용해 동적 바인딩을
사용자가 직접 지정해준다.

요즘 같은 고성능 컴퓨터 시대에는 virtual의 오버헤드는 미미한 수준이다.

그러나 간혹 특정 사례의 경우 오버헤드 비용이 유의미한 수준에서 발생한다.
또한 이러한 경우 객체마다 메모리 오버헤드도 발생한다.
대부분은 문제가 없지만 간혹 영향을 끼칠 수는 있다.

객체마다 vtable 포인터를 가지느냐 안가지느냐
이러한 사소한 차이도 메모리 오버헤드이긴 하다.
수십억개의 객체가 모두 vtable을 차지하고,
그 vtable에 대한 포인터의 메모리 오버헤드를 무시할 순 없다.

또한 소멸자도 유의해야한다.
상속 관계를 가질땐 부모 생성자가 호출되므로
소멸자를 virtual 키워드로 지정해준 뒤
부모 생성자의 동적할당 된 메모리를 지워줘야한다.