"function call이 있을 때, 어떤 function을 실행해야 하는가?"는 컴파일러가 대답해야 한다. C에서는 이걸 결정하는 게 아주 쉬웠다. 이름만 찾아서 매칭해주면 끝이었다. C++은 다르다. 함수 오버로딩 등이 있기 때문이다. 하지만 함수 오버로딩 등을 포함해서 왠만해서 이런 매칭은 컴파일 타임에 일어난다.
함수 콜과 실제 함수 코드를 매칭해주는 것을 Binding이라고 부른다. Binding이 컴파일 타임에 일어나면 Static Binding, 런타임에 일어나면 Dynamic Binding이다. virtual function은 Dynamic Binding이다.
Dynamic Binding은 런타임에 타입에 따른 함수를 매칭해주기 때문에 편하다. 그렇다면 두가지 질문이 생길 것이다.
여기엔 두 가지 이유가 있다. 효율성(efficiency)과 클래스 디자인(a conceptual model)이다.
Virtual Function의 작동 방식을 살펴보면 알겠지만, Dynamic Binding은 기존 함수 호출에 한단계가 더 추가되면서 오버헤드가 발생한다. Static Binding은 이를 컴파일에 다 해결해놓기 때문에 비용이 더 싸다. Stroustrup은 C++ 원칙 중 하나로 '사용하지 않는 기능으로 인한 비용을 지불하지 않는다'(you shouldn't have to pay for features you don't use)를 말했다.
클래스 디자인 측면에서 virtual을 함수에 넣지 않음으로써 이 함수가 재정의되기를 원하지 않는다는 의도를 강조할 수 있다.
C++은 Virtual 함수의 특징을 서술하지만 어떻게 작동해야 하는지는 컴파일러에게 맡긴다. 그래서 컴파일러마다 작동방식은 다르다.
보통 컴파일러는 virtual function을 관리하기 위해서 각 오브젝트마다 숨겨진 멤버 변수를 추가한다. 이 변수는 virtual function들을 담은 table을 가리키는 포인터다. 포인터를 vptr, 테이블을 vtable이라고 부른다. vtable은 해당 클래스의 가상 함수의 주소를 담고 있다.
출처: C++ Primer Plus 6th 741p
virtual function을 쓴다는 것은 다음의 side effect를 일으킨다.
각 오브젝트의 크기가 vptr 등으로 인해 증가한다.
각 클래스마다 컴파일러가 vtable을 만든다.
virtual function을 호출할 때마다 vtable을 조회하는 추가적인 절차가 생긴다.