1 분 소요

동적 결합

가상 함수는 자신을 호출하는 객체 타입, 즉 동적 타입에 따라 실제 호출될 함수가 결정된다.

gotoxy(…);
printf(…);

컴파일러는 gotoxy 함수가 어떤 주소에 있는지 알고 있으며 그래서 gotoxy 호출문을 이 함수의 주소로 점프하는 코드로 번역할 것이다. 컴파일하는 시점(링크)에 이미 어디로 갈것인가가 결정되는 이런 결합 방법을 정적 결합(Static Binding) 또는 이른 결합(Early Binding)이라고 한다. 결합이란 함수 호출문에 대해 실제 호출될 함수의 번지를 결정하는 것을 말하는데 지금까지 작성하고 사용했던 일반적인 함수들은 모두 정적 결합에 의해 번역된다.
StaticBinding

그런데 가상 함수는 포인터가 가리키는 객체의 타입에 따라 호출될 실제 함수가 달라지므로 컴파일시에 호출할 주소가 결정되는 정적 결합으로는 정확하게 호출할 수 없다. 왜냐하면 포인터가 실행중에 어떤 타입의 객체를 가리킬지 컴파일 중에는 알 수 없기 때문이다. 대입은 실행중에 회수에 상관없이 얼마든지 할 수 있는 연산이고 포인터는 타입만 일치하면 얼마든지 다른 대상을 가리킬 수 있다. 컴파일러는 앞 예제의 Message 함수의 본체를 특정 번지로의 점프문으로 번역할 수 없으며 조건에 따라 호출할 함수를 결정하는 문장으로 번역해야 한다.

void message(Base *pB)
{
    // if pB가 Base 객체를 가리키면 Base::OutMessage 호출
    // if pB가 Derived 객체를 가리키면 Derived::OutMessage 호출
}

실행중에 호출할 함수를 결정하는 이런 결합 방법을 동적 결합(Dynamic Binding) 또는 늦은 결합 (Late Binding)이라고 한다. pB->OutMessage 호출문을 미리 고정된 번지로의 점프문으로 번역하는 것이 아니라 pB가 가리키는 객체의 타입에 따라 적절한 함수를 선택해서 점프하는 코드로 번역해야 하는 것이다. 이렇게 해야 전달된 객체에 따라 각기 다른 동작을 할 수 있는 다형성을 구현할 수 있다.
DynamicBinding

동적 결합은 멤버 함수를 포인터(또는 레퍼런스)로 호출할 때만 동작한다. 객체로부터 함수를 호출할 때는 설사 그 함수가 가상 함수라 할 지라도 컴파일 시에 호출할 함수를 정확하게 결정할 수 있다. 왜냐하면 객체는 자신이 소속된 클래스 타입일 뿐이지 다른 타입이 될 수 없기 때문이다. 포인터는 부모 타입의 포인터가 자식을 가리킬 수 있기 때문에 정확한 함수를 호출하기 위해 동적 결합을 해야 하지만 객체로 직접 호출할 때는 호출 객체의 타입을 분명히 알 수 있으므로 그럴 필요가 없다. 다음 코드는 어떤 함수를 호출할지 분명히 알 수 있다.

B.OutMessage();
D.OutMessage();

가상함수란?

가상 함수란 무엇인가에 대한 가장 짧고도 정확한 대답은 , “동적 결합을 하는 함수”라고 할 수 있다.
사실 가상 함수를 칭하는 vitrual이라는 키워드는 상당히 잘못 선택되었으며 이 말을 한국어로 번역한 결과인 “가상”이라는 말도 마찬가지이다. 가상이라는 말은 “~이 아니다”라는 뜻이므로 이 말을 처음 들었을 때 누구나 함수가 아닌 것처럼 오해할 소지가 있으며 독자의 직관력을 전혀 쓸모없게 만들어 버린다. virtual이라는 용어에 뜻이 분명히 표현되지 않으므로써 안그래도 어려운 개념을 더 어렵게 만든다. “가상”이라는 단어에서 의미를 찾으려고 하면 헷갈리기만 할 뿐이다.