프로그래밍/cpp

가상함수(virtual),vptr,vtable C++

콘파냐 2014. 1. 4. 18:25

가상함수 그리고 vptr,vtable

 

가상함수란 클래스 타입의 포인터로 멤버함수를 호출할 경우 동작하는 함수를 말하는데 함수의 선언앞에 virtual 키워드가붙은 함수를 말합니다.

가상함수라는 언어적인 측면으로 이해하기에는 다소 무리가 있기때문에 가상함수란 뜻의 의미보다는 가상함수란 무엇인가를 알아야 하겠습니다.

 

관련링크 2013/06/04 - [프로그래밍/c++] - 가상함수 virtual 키워드 C++

 

위 관련링크에 가상함수에 대한 기본적인 설명이 있습니다.

이번 포스팅은 vtable에 대해서 이야기를 해볼까합니다.

 

vtable을 설명하기에 앞서 정적결합과 동적결합에 대해서 알아야하셔야하는데요. 위 링크에선 간략하게 설명했습니다.

정적바인딩은 클래스타입의 포인터에 따른 결합이고

동적바인딩은 클래스타입의 포인터가 가르키는 대상체에 따른 결합을 말합니다.

 

상속관계의 두 클래스를 동적바인딩을 통해서 함수 func() 를 호출한 코드입니다.

 

virtual 키워드는 클래스멤버함수를 동적바인딩을 시켜주기위한 키워드입니다.

동적바인딩은 클래스 타입 포인터가 가르키는 대상체에 따른 결합입니다.

우선 상속관계에 있는 클래스상에선 포인터가 가르키는 대상체의 타입은 가변적으로 변할 수 있습니다.

부모 클래스타입의 포인터는 항상 자식클래스객체를 가르킬 수 있기 때문입니다.(이성질은 다형성을 설명하기위한 중요한 핵심이죠)

이런 변화는 대상체의 타입에 따라서 호출하는 함수의 변화를 가져오게됩니다.

(위 예처럼 parrent 가 a객체와 b객체를 가르키때 같은 func()함수를 호출하지만 실제로는 다른작동을 합니다..)

parrent->func()라는 하나의 표현이지만, parrent 클래스타입포인터가 가르키는 대상체에 따라 결과가 달라지기때문에 컴파일시간에 결정할 수 없는 사항입니다.(parrent->func()라는 표현만 놓고 본다면 결과가 같아야하지만 컴파일러는 parrent가 담고 있는 객체 즉 대상체의 클래스내에 가상함수가 있다면(여기서는 func()함수가 가상함수이기때문에) vptr포인터를 생성하여 vtable의 시작번지값을 저장해줍니다. vptr포인터가 생성되었다는건 동적결함을 할 준비가 되어있다는 뜻입니다.)

메카니즘은 이렇습니다.

가상함수를 호출하려한다.->vptr->vtable->A(또는B)::func()

 

vtable은 가상함수테이블인데 각 클래스별로 가상함수만 빼내어 그 주소를 가르키는 함수포인터를 저장해놓은 테이블입니다.

vtable은 일종의 함수포인터배열이라 할 수 있습니다.

 

2014/01/02 - [프로그래밍/c++] - 함수포인터(1)

이테이블의 장점은 실행시간에 모든경우에따른 실행할 가상함수의 경로(주소)를 나타냅니다.

컴파일을 하게되면 vtable과 더불어 vptr이라는 포인터변수가 생성되는데 vptr을 그림으로 그려보면 다음과 같습니다.

 

위 코드를 토대로 일어나는일을 간략화해 보았습니다. func()는 가상함수기때문에 객체의 생성시 vptr이라는 포인터가 생성됩니다.

vptr포인터는 객체가 생성될 때 클래스에 가상함수가 하나라도 있다면 생성되게 됩니다.

sizeof 연산자를 통해 각 클래스 크기를 알아보면 이해가 될것입니다.

cout<<sizeof(A)<<endl;을 코드에 추가해 보면 알겠지만 4 가 나옵니다. 멤버변수가 없고 vptr포인터가 4바이트이기 때문입니다.

이렇게 가상함수가 포함된 클래스의 객체크기는 4바이트가 추가로 더해집니다.

vptr은 vtable의 시작주소를 가르키는 포인터고, 각 클래스마다 하나의 고유 vtable이 생성되게 됩니다.

위 그림처럼 고유의 vtable은 가상함수를 가르키는 함수포인터배열로 되어있습니다.

(vtable에 함수포인터또한 포인터이므로 한개당 4바이트입니다. 단 이경우 객체에 포함되는것이아니고 별도의 메모리를 필요로합니다.)

실제로 가상함수가아닌 함수를 호출하는 경우 vptr->vtable 단계가 없습니다.

반대로 말하자면 가상함수의 호출은 vptr->vtable 단계가 추가가 된 것이죠.

반응형