가상 함수가 호출될 때 그 함수에 대해 실행되는 코드는 함수가 호출되는 객체의 동적 타입에 맞는 것이어야 함
컴파일러는 가상 테이블(vtbl)과 가상 테이블 포인터(vptr)를 사용하여 동적 타입을 판단
vtbl
vtbl의 각 요소는 클래스에 정의한 가상 함수 포인터
vtbl의 크기는 클래스에 선언된 가상 함수의 수에 비례
vtbl은 클래스에 한 개만 생성되기 때문에 크기는 미미하지만 소프트웨어 전체에 가상 클래스나 가상 함수가 많을 때에는 무시 못할 메모리 부담이 됨
vtbl은 일반적으로 컴파일러에 의해 가상 함수의 정의가 있는 오브젝트 파일에 위치
vptr
vptr은 가상 테이블을 가리키는 포인터
가상 함수를 선언한 클래스에는 숨겨진 데이터 멤버 vptr이 있어서 객체의 크기에 포인터의 크기만큼 추가됨
class C1;
class C2 : public C1;
void makeACall(C1* pC1)
{
pC1->f1();
}
가상 함수가 호출되는 과정
pC1이 가리키는 객체의 vptr을 가리키는 vtbl을 참조하여 호출해야 하는 함수의 포인터를 찾아옴
찾아온 함수 포인터가 가리키는 함수를 호출
가상 함수 호출에 드는 비용은 함수 포인터를 통해 함수를 호출하는 비용과 같기 때문에 가상 함수만 놓고 볼 때는 수행 성능의 발목을 잡는 요인은 아님
인라인은 컴파일 도중에 호출 위치에 호출되는 함수의 본문을 끼워 넣는다는 의미인데, 가상 함수는 호출할 함수를 런타임까지 기다려 결정한다는 의미이므로 가상 함수는 인라인 효과를 포기해야 함
다중 상속 중 다이아몬드 상속일 경우 기본 클래스의 데이터 멤버가 중복 생성되므로 가상 상속을 통해 중복 생성을 방지할 수 있음
하지만 가상 상속도 비용이 발생하므로 아래 글을 참고
Effective C++ 항목 40 다중 상속은 심사숙고해서 사용하자
이것만은 잊지 말자!- 다중 상속은 단일 상속보다 확실히 복잡함 모호성 문제 뿐만 아니라 가상 상속이 필요해질 수 있음 - 가상 상속을 쓰면 크기 / 속도 비용이 늘어나며 초기화 및 대입 연산의
eovywjr1.tistory.com
RTTI(RunTime Type Identification)
런타임에 객체와 클래스의 정보를 알아낼 수 있게 하는 정보
type_info 타입의 객체에 저장하고, type_info 객체는 typeid 연산자를 써서 접근할 수 있음
클래스마다 RTTI 정보를 하나씩만 유지하고 vbtl의 0번째 인덱스에 type_info 객체에 대한 포인터가 위치
객체의 동적 타입을 정확히 알아내려면 클래스에 가상 함수가 최소한 하나는 있어야 함
'C++ > More Effective C++' 카테고리의 다른 글
[More Effective C++] 항목 28 스마트 포인터 (0) | 2025.03.20 |
---|---|
[More Effective C++] 항목 26 클래스 인스턴스의 개수를 의도대로 제한하는 방법 (0) | 2025.03.17 |
[More Effective C++] 항목 22 단독 연산자 대신에 =이 붙은 연산자를 사용하는 것이 좋을 때가 있다 (0) | 2025.03.10 |
[More Effective C++] 항목 20 반환값 최적화(return value optimization)가 가능하게 하자 (0) | 2025.03.07 |
[More Effective C++] 항목 19 임시 객체의 원류를 정확히 이해하자 (0) | 2025.03.06 |