API 나 라이브러리 등을 보면 자주 보이는 것이 Virtual 함수를 통한 상속이다. 그냥 그런가보다 하고 쓸 수도 있지만, 정확한 개념을 알면서 사용하는 것이 좋다.
기본적으로 기억해야 할 개념은 Virtual 함수는, 객체지향 프로그래밍의 철학중 하나인 '함수의 다형성(polymorphism)' 을 위해 존재한다는 것이다.
'함수의 다형성'은 다시 한문장으로 정리하면 "동일한 명령에 대해서, 서로 다른 객체가 서로 다르게 수행한다" 라고 할 수 있다.
이는 이렇게 쉽게 설명할 수 있다. '도형' 이라는 부모 클래스를 가지고 있고, '삼각형', '사각형', '원' 이라는 자녀 클래스를 가지고 있을 때, 자녀 클래스에 모두 필요한 draw() 라는 함수를 구현한다고 하자. 같은 명령이지만, 각 객체가 다른 일을 하는, 다형성이 적용될 수 있는 좋은 사례이다.
이 경우에 물론 각각 자녀 클래스의 인스턴스를 만들어 일을 수행할 수도 있지만, 부모인 '도형' 인스턴스를 자녀 클래스의 포인터 인스턴스로 만들수 있다.
Figure *fig1 = new Triangle;
Figure *fig2 = new Square;
Figure *fig3 = new Circle;
이렇게 만들면, Figure 라는 클래스 하나로 각각의 자녀 클래스들이 하는 일들 specific 하게 수행하는 인스턴스들을 만들어낼 수 있다. 좀 더 코드가 깔끔해지고 가독성이 분명해질 것으로 보인다. 이런 작업이 '함수의 다형성' 을 이용한 프로그래밍이다.
이럴 경우, draw() 라는 함수는 원래 Figure 라는 클래스에 무언가 형태를 가지고 있고, 실제로 실행될 때에는 Triangle, Square, Circle 안에 들어있는 draw() 함수에 맞게 각각 실행되어야 할 것이다. 이를 위해서 Figure 클래스 내부에
class Figure {
public:
virtual bool draw() = 0;
};
이런 식으로 virtual 키워드를 이요한 가상함수를 만들고, 각 자녀 클래스들은
class Triangle : public Figure {
public:
bool draw(){
...draw triangle...
}
};
각자 가지고 있는 draw() 함수로 일을 하게 되는 것이다. 이렇게 각각의 자녀 클래스 인스턴스에 맞게 함수가 실행되는 것을 '동적 바인딩' 이라고 한다.
위에서 예를 들은 virtual 함수는 정말로 아무런 일도 하지 않는 '순수 가상함수' 이다. 이런 순수 가상 함수를 가지고 있는 클래스는 '추상 클래스' 로서, 독자적으로 인스턴스를 생성할 수 없고, 딱 이러한 다형성을 목적으로 존재하는 클래스이다.
참고링크 : '새-치킨-독수리' 예를 통하여 알기쉽게 Virtual 함수의 쓰임새를 설명해주신 좋은 링크
참고링크 : 위에서 설명한 도형 예제로 다형성과 Virtual 함수의 쓰임새에 대해서 설명해 주신 링크
(http://yagi815.tistory.com/220)
참고링크 : 왜 소멸자는 Virtual 로 만들어야만 하는가, 에 대한 링크.
(http://www.borlandforum.com/impboard/impboard.dll?action=read&db=bcb_tutorial&no=91)
간략하게만 설명하면, 위의 다형성 예제에서 생각했을 때, 부모 클래스의 소멸자를 Virtual 로 생성하지 않으면, 위의 예제처럼 자녀 클래스의 포인터로 생성했다면? 소멸자가 불려졌을 때? 자녀 클래스의 소멸자를 호출할 수가 없다. 그래서 Virtual 함수로 소멸자를 생성하면 자녀클래스->부모클래스 순으로 안전하게 소멸자가 호출된다.