출처 : http://blog.naver.com/ruo1022/110172566177

 

가상함수를 왜써요?

Up Casting

하위 클래스(자식, 부모보다 큰 타입) 에 상위 클래스(부모, 자식보다 작은 타입) 를 캐스팅 하는 것.

원칙적으론 불가능 하지만 상속관계에서는 가능. 다만 부모 포인터로는 자식의 기능(함수)를 사용할 수 없다.

ex)

CParent * m_parent  = new  CChild;

 

이 과정에서 자식 클래스의 함수를 호출하기위해 virtual 가상함수로 오버라이딩 해야 부모가 자식클래스의 함수에 접근 가능하다 .

 

cf) Down Casting

상위 클래스(부모, 자식보다 작은 타입) 에 하위 클래스(자식, 부모보다 큰 타입)를 캐스팅 하는 것.

원칙적으로 가능함. 보통 Up Casting 을 되돌려서 부모에겐 없고 자식에게만 있는 함수를 쓰기위해 부모타입 포인터를

자식타입으로 바꿀 때 사용함.

ex)

CParent * m_pParent  = new  CChild;

(CChild *)m_pParent -> OnlyChildFuntion();

가상함수의 정의방식

부모에서 virtual 을 한번만 정의하면 해당 함수는 상속과정 내내 virtual 이지만 명시적 표현, 가독성을 위해서 자식에서 정의할때도

되도록 virtual 함수는 virtual 키워드를 붙여주는게 좋다.

 

가상함수의 호출방식

만약 부모로 부터 virtual 함수가 계속 상속되었지만 해당 함수가 최하위 단계에 정의가 안되어있다면,

virtual 함수는 내려갈 수 있는 최대까지 내려가서 호출됨.

 

가상함수 테이블이란?

virtual 함수가 정의된 클래스 인스턴스(메모리할당) 시 가상함수 포인터 배열이 클래스 최전방 메모리 영역에 잡힌다.

즉 가상함수 테이블의 크기는 가상함수 갯수에 비례한다.

그리고 클래스는 이 가상함수 포인터 배열을 가리키는 포인터(4byte) 가  추가적으로 잡힌다.

 

여기에 가상함수들의 주소들이 등록되며, 각 클래스단위로 그 가상함수 테이블을 공유함.

부모 클래스 포인터 타입에 자식포인터를 집어넣으면 실제 저장된 데이터인 자식클래스의 가상함수테이블을 찾아간다.

 

순수 가상함수

순수 가상함수를 가지고 있는 함수는 추상 클래스이며 인스턴스(메모리 할당)을 받을 수 없는 객체이다.

ex) virtual  void  OutPut(void) = 0;

 

사용용도

1. 자식 클래스에게 상속만 제공하는 인터페이스 용도 클래스

2. 순수 가상함수를 이용할 경우, 순수가상함수는 일단 테이블에서 제외 되므로(정의가 안되어있으므로)

   자식클래스의 함수로 접근하는 속도면에서도 빠르다.

 

가상함수의 함수호출속도

inline 함수(매크로와 비슷한 함수)

static 함수(정적 함수)

Data 영역에 잡히는 함수

지역함수

순수가상함수(부모영역에서 호출이 없이 건너뛰므로 바로 자식쪽 호출)

가상함수(가장 느리지만 편의성이 쩐다.)

 

가상함수의 호출 속도가 느린 이유는, 실제 호출된 함수를 찾으려면 해당 클래스의 가상함수 테이블을

찾아야 하는데, 이 찾는 과정이 클래스명 문자열 비교과정이라는 것이다.

최악의 경우 상속계층 바닥까지 찾아가서 호출된 실제 클래스를 찾는 경우가 발생한다는 것.

이렇게 찾은 클래스의 가상함수 테이블을 참조하여 또 다시 함수를 찾고, 이 함수를 호출한다.

 


 

 

순수가상함수의 상속

부모가 순수가상함수를 가지고 있으면 자식도 순수가상함수를 물려받음.

따라서 사용하려는 시점의 자식클래스는 순수가상함수를 가상함수로 오버라이딩해서

사용하면 됨

 

가상소멸자

UpCasting 시에 소멸자 호출시 부모포인터로 소멸자를 접근하게 되면 클래스 자체는 메모리 해제되나

자식 영역의 소멸자를 호출하지 못하여 해제하지 못하는 메모리가 생겨난다.

따라서 가상 소멸자를 통해서 자식부터 쭉 소멸자 호출이 되도록 한다.

 

가상 함수의 동적 바인딩

컴파일 시 링커 구간에서 함수가 어디로 쓸지 연결되는 과정

가상함수를 쓰게되면 컴파일 중에 어떤 함수를 쓸지 알수없게 됨.

컴파일 시점에 함수 호출지점이 결정되는 것 - 함수의 정적 바인딩

파일 실행 후에 함수 호출지점이 결정되는 것 - 함수의 동적 바인딩

 

 

결론적으로 가상함수를 왜쓰냐

 개인적인 의견을 적어보자면, 굉장히 편리하다는 것이다. 어떤점이?

일단 부모포인터 타입으로 일괄적으로 함수에 접근이 가능하다.

이말은 일일이 다운캐스팅 하여 자식클래스의 함수를 호출하지 않아도 된다는 것이다.

이것만 해도 엄청나게 지저분해 질 수 있는 코드량을 획기적으로 줄일 수 있다.

 

 특정 객체타입을 관리하기 편리하다.

가령 게임상에서 존재하는 모든 객체들을 GameObject 라는 클래스를 상속받아서 구현했다면,

해당 객체들을 최상위 부모타입인 GameObject* 타입으로 관리가 가능하다.

공통적으로 사용할 기능들을 가상함수로 정의하고, 호출하면 각 객체별로 자신만의 기능을 수행 할테니

이 얼마나 편리한가.

 

'Java' 카테고리의 다른 글

[Java] JUnit 사용하기  (0) 2014.09.12
[Java] J Unit 이란?  (0) 2014.09.11
[Thread Pool] ThreadPoolExecutor  (0) 2012.09.06
Bing Translator API를 이용한 번역 - JAVA  (0) 2012.07.21
Java ::: cmd 명령어 실행법  (0) 2012.06.28
블로그 이미지

kuku_dass

,