가상함수의 원리

가상 함수는 실행 시간에 호출될 함수를 결정합니다.

- 그럼 어떻게 실행 시간에 객체의 클래스형에 따라 재정의된 가상 함수를 찾아 호출할 수 있는 것일까요?


C++는 가상 함수를 처리하기 위해 가상 함수를 갖는 클래스마다 가상 함수 테이블을 생성합니다.

- 또한, 가상 함수를 갖는 클래스의 객체마다 가상 함수 테이블 포인터라는 숨겨진 멤버 변수를 생성합니다.

- C++ 컴파일러가 가상 함수를 갖는 클래스에 대해 가상 함수 테이블을 생성하고 가상 함수 테이블을 갖는 클래스의 객체를 생성하면 객체의 멤버 변수를 할당하기 전에 먼저 가상 함수 테이블의 주소를 저장하는 가상 함수 테이블 포인터가 할당됩니다.

- 바로 이 가상 함수 테이블 포인터가상 함수 테이블이 가상 함수를 호출할 때 이용되는 것입니다.



그렇다면 상속에서 가상 함수 테이블은 어떻게 처리될까요?

- 파생 클래스는 기본 클래스의 가상 함수 테이블을 상속받아서 수정, 확장합니다.

- 기본 클래스의 가상 함수 테이블 복사 -> 가상 함수 테이블에 등록된 가상함수의 주소를 재정의된 멤버 함수의 주소로 수정 

- 파생 클래스가 상속받은 가상 함수를 재정의하지 않으면 상속받은 가상 함수의 주소를 그대로 사용합니다.

- 만약 파생 클래스에 새로운 멤버 함수가 가상 함수라면, 상속받은 기본 클래스의 가상 함수 테이블 맨 뒤쪽에 추가된 가상 함수의 주소를 저장해서 가상 함수 테이블을 확장한다.





클래스 B는 클래스 A를 상속받는 구조입니다.


pA = new B;         // pA라는 이름의 클래스B 파생 클래스 생성

pA->f1()              // A::f1 호출 - 클래스B 파생 클래스에는 f1함수가 없기 때문에 기본 클래스의 f1이 호출되었습니다.

pA->f2()              // B::f2 호출 - 클래스B 파생 클래스가 f2를 가상함수로서 재정의했고 기본 클래스의 f1도 가상함수

                                                로 선언되었기 때문에 B::f2가 호출되었습니다.

pA->f3()              // A::f3 호출 - f3은 가상 함수가 아니기 때문에 기본 클래스의 f1이 호출됩니다.

pA->f4()              // 컴파일 에러 - 기본 클래스A의 포인터로 파생 클래스B에 새로 추가된 가상 멤버 함수를 호출할 수 

                                                 없으므로 컴파일 에러

Posted by 긍정왕오킹