상속에서 중요한 사실은 부모는 자식을 가리킬 수 있다는 것입니다.
즉, 부모 클래스 B가 자식 클래스 D를 가리킬 수 있다는 말입니다.
하지만 이런 방법은 부모에 없는 자식 멤버는 사라진다는 문제점이 있습니다.
부모가 수용할 수 있는 것이 없기 때문에 버려지게 되는 것입니다.
그리고 역으로 B = D는 될 수 있지만 D = B는 성립하지 않습니다.
다음의 코드를 보도록 하겠습니다.
#include <iostream> using namespace std; class Base { public: void Output() { cout << "Base::Output() called" << endl; } }; class Derived : public Base { public: void Output() { cout << "Derived::Output() called" << endl; } }; int main() { Base b; Derived d; b.Output(); d.Output(); }
실행시켜 보면 다음과 같이 정확하게 출력되는 것을 알 수 있습니다.
위에서 부모가 자식을 가리킬 수 있다고 했으니 코드를 살짝 수정해 보겠습니다.
main() 함수 부분을 수정해보도록 하겠습니다.
int main() { Base b, *pB; Derived d; pB = &b; pB->Output(); pB = &d; pB->Output(); }
부모는 자식을 가리킬 수 있으니 문제가 없는 코드이며 처음과 동일한 출력을 기대할 것입니다.
하지만 실제는 다음과 같이 출력됩니다.
예상한 것과는 다른 출력 화면을 보일 것입니다.
부모의 포인터가 실제로는 자식클래스의 객체를 가리키기 때문에
Derived::Output()이 호출되는 것을 기대하고 코드를 작성할 것입니다.
하지만 위와 같이 출력되는 이유는 포인터의 타입이 정해진 시점에 이미 어떤 멤버 함수를 호출할 지가
결정이 되기 때문입니다.
포인터가 무엇을 가리키든 상관없이 이미 이 포인터가 부모타입(Base)이기 때문에
호출되는 함수도 부모의 것으로 정해져있다는 것입니다.
제대로 된 결과를 얻기 위해서 사용하는 방법이 바로 가상함수입니다.
함수의 리턴타입 앞에 virtual이라는 키워드를 붙여주면 가상함수가 됩니다.
부모 클래스에만 virtual을 붙여주면 되지만, 보통은 자식 클래스에도 붙여서 명시합니다.
C++11에서는 override라는 키워드가 추가되서 명시적으로 가상함수를 상속받을 수 있습니다.
아래의 링크에서 확인이 가능합니다.
2014/12/21 - [Programming/C++11&14] - [C++11] final과 override
virtual 키워드를 붙여주면 우리가 원하는데로 값이 출력되는 것을 볼 수 있습니다.
가상함수는 정적 결합(많이 들어본 용어일 것입니다. 정확히는 링킹시에 결합입니다.)을
하지 않고 동적 결합(실행 중에 결정됩니다.)을 사용합니다.
그렇기 때문에 상황에 따라 호출할 멤버 함수를 알아야 합니다.
이것을 알 수 있게 만들어주는 방법 중의 하나는 vtable을 이용하는 것입니다.
vtable은 호출되는 함수의 목록이라고 할 수 있습니다.
그리고 숨겨진 멤버 변수를 하나 추가시켜서 그 변수가 vtable을 가리키게 하는 것입니다.
이런 방식으로 멤버 함수를 동적 결합시킬 수 있습니다.
가리키는 클래스가 달라져도 숨겨진 멤버 변수가 vtable을 가리키고 있기 때문에
vtable을 참조해서 호출할 수 있기 때문에 클래스는 다형성을 확보하게 됩니다.
즉, 같은 함수라도 다른 기능을 하는 게 가능합니다.
가상함수는 이렇게 동적으로 결합시켜서 실제 가리키는 클래스의 함수를 실행하게 합니다.
또한, virtual 리턴타입 함수명(인자) = 0의 가상함수를 만들면 순수 가상함수가 됩니다.
이렇게 만들어두면 자식 클래스는 반드시 이 가상함수를 재정의해서 사용해야 하는
제약이 생기게 됩니다.
가상함수는 COM(Component Object Model)의 기반이 되기도 합니다.
이렇게 하나 이상의 순수 가상함수를 가지는 클래스를 추상 클래스라고 합니다.
추상 클래스는 인스턴스를 생성할 수 없습니다.
반드시 상속을 해서 자식 클래스에서 실체를 만들어준 후에야 사용이 가능합니다.
가상함수는 중요한 개념 중에 하나이기 때문에 C++에서 반드시 알아야 하는 개념 중 하나입니다.
'Programming > C&CPP' 카테고리의 다른 글
재귀 함수 & 재귀적 함수호출(recursive function call) (2) | 2014.12.04 |
---|---|
함수 오버로딩(overloading)과 오버라이딩(overriding) (0) | 2014.12.04 |
한글 윈도우즈, Visual C++ 한글 처리 방식 (0) | 2014.12.03 |
C++ 클래스 상속 (0) | 2014.12.01 |
Calling Convention(함수 호출 규약) (0) | 2014.11.25 |