가상 기본 클래스
클래스는 한 번 이상 파생된 클래스에 대한 간접 기본 클래스일 수 있으므로 C++는 이런 기본 클래스가 작동하는 방식을 최적화하는 방법을 제공합니다. 가상 기본 클래스는 다수의 형식 상속을 사용하는 클래스 계층에 모호성이 발생하지 않도록 공간을 절약하는 방법을 제공합니다.
각 비가상 개체에는 기본 클래스에 정의된 데이터 멤버의 복사본이 있습니다. 이 복제는 공간을 낭비하고 기본 클래스 멤버에 액세스할 때마다 기본 클래스 멤버의 어떤 사본을 원하는지 지정할 것을 요구합니다.
기본 클래스를 가상 기본으로 지정하면, 데이터 멤버가 중복되지 않으면서 한 번 이상 간접 기본으로 작동할 수 있습니다. 해당 데이터 멤버의 단일 복사본은 해당 복사본을 가상 기본 클래스로 사용하는 모든 기본 클래스에서 공유합니다.
가상 기본 클래스를 선언할 때 해당 가상 키워드는 파생된 클래스의 기본 목록에 나타납니다.
다음 그림에서 시뮬레이션된 런치 라인을 설명하는 클래스 계층 구조를 살펴보십시오.
시뮬레이션된 Lunch-Line 그래프
그림에서 Queue는 CashierQueue 및 LunchQueue에 대한 기본 클래스입니다. 하지만 두 클래스가 결합되어 LunchCashierQueue를 형성할 경우 새 클래스에 형식이 Queue인 두 하위 개체가 포함되는데 하나는 CashierQueue의 하위 개체이고 하나는 LunchQueue의 하위 개체인 문제가 발생할 수 있습니다. 다음 그림에서는 개념적 메모리 레이아웃을 보여 줍니다.(실제 메모리 레이아웃은 최적화될 수도 있습니다.)
시뮬레이션된 Lunch-Line 개체
LunchCashierQueue 개체에 두 개의 Queue 하위 개체가 있습니다. 다음 코드에서는 Queue를 가상 기본 클래스로 선언합니다.
// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};
virtual 키워드는 단 하나의 하위 개체 Queue 사본을 포함하도록 합니다(아래 그림 참조).
가상 기본 클래스를 사용하여 시뮬레이션된 Lunch-Line 개체
클래스에는 주어진 형식의 가상 구성 요소와 비가상 구성 요소가 둘 다 있을 수 있습니다. 이는 다음 그림에서 설명하는 조건에서 발생합니다.
같은 클래스의 가상 및 비가상 구성 요소
그림에서 CashierQueue 및 LunchQueue는 Queue를 가상 기본 클래스로 사용합니다. 하지만 TakeoutQueue는 Queue를 가상 기본 클래스가 아니라 기본 클래스로 지정합니다. 따라서, LunchTakeoutCashierQueue는 형식이 Queue인 두 개의 하위 개체를 가지는데, 하나는 LunchCashierQueue를 포함하는 상속 경로의 하위 개체이고 하나는 TakeoutQueue를 포함하는 경로의 하위 개체입니다. 이는 다음 그림에 설명되어 있습니다.
가상 및 비가상 상속을 사용하는 개체 레이아웃
참고
가상 상속은 비가상 상속과 비교했을 때 상당한 크기의 혜택을 제공하지만,추가 처리 오버헤드가 발생할 수 있습니다.
파생된 클래스가 가상 기본 클래스에서 상속된 가상 함수를 재정의하고 파생된 기본 클래스의 생성자 또는 소멸자가 가상 기본 클래스에 대한 포인터를 사용하여 해당 함수를 호출하는 경우, 컴파일러는 추가로 숨겨진 "vtordisp" 필드를 가상 기본 클래스에 사용할 수 있습니다. /vd0 컴파일러 옵션은 숨겨진 vtordisp 생성자/소멸자 치환 멤버의 추가를 표시하지 않습니다. 기본값인 /vd1 컴파일러 옵션은 필요할 경우 사용하도록 설정됩니다. 모든 클래스 생성자와 소멸자가 실제로 가상 함수를 호출한다고 확신하는 경우에만 vtordisps를 해제합니다.
/vd 컴파일러 옵션은 전체 컴파일 모듈에 적용됩니다. vtordisp pragma를 사용하여 클래스 단위로 vtordisp 필드를 비활성화한 후 다시 활성화하십시오.
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )