C++에서 가상 기본 클래스(virtual base class)는 파생 클래스가 기본 클래스의 복사본을 여러 개 갖게 되는 상황, 즉 흔히 말하는 '다이아몬드 상속' 문제를 해결하기 위해 사용됩니다.
문제 상황: 중복 상속으로 인한 모호성
먼저 가상 상속을 사용하지 않은 일반적인 예제를 살펴보겠습니다.
#include <iostream>
using namespace std;
class B {
public:
int b;
};
class D1 : public B {
public:
int d1;
};
class D2 : public B {
public:
int d2;
};
class D3 : public D1, public D2 {
public:
int d3;
};
int main() {
D3 obj;
obj.b = 40; // 오류 발생!
obj.b = 30; // 오류 발생!
obj.d1 = 60;
obj.d2 = 70;
obj.d3 = 80;
cout << "\n B : " << obj.b;
cout << "\n D1 : " << obj.d1;
cout << "\n D2 : " << obj.d2;
cout << "\n D3 : " << obj.d3;
}왜 오류가 발생할까요?
위 예제에서 D1과 D2는 각각 B를 상속받으므로 둘 다 B의 복사본을 하나씩 가지고 있습니다. 그런데 D3는 D1과 D2를 동시에 상속하기 때문에 결과적으로 B의 복사본을 두 개 갖게 됩니다. 하나는 D1을 통해, 다른 하나는 D2를 통해 전달된 것입니다.
이 때문에 obj.b = 40;처럼 멤버 b에 접근하는 구문에서는 컴파일러가 어느 쪽 복사본을 참조해야 할지 판단할 수 없어 모호성(ambiguity) 오류가 발생합니다.
해결 방법: virtual 키워드로 가상 상속하기
D3에서 B의 중복 복사본을 제거하려면, D1과 D2가 B를 상속할 때 virtual 키워드를 사용해야 합니다. 이렇게 하면 컴파일러는 최종 파생 클래스에서 공통 기본 클래스의 인스턴스를 단 하나만 생성합니다.
#include<iostream>
using namespace std;
class B {
public:
int b;
};
class D1 : virtual public B {
public:
int d1;
};
class D2 : virtual public B {
public:
int d2;
};
class D3 : public D1, public D2 {
public:
int d3;
};
int main() {
D3 obj;
obj.b = 40; // 구문 3
obj.b = 30; // 구문 4
obj.d1 = 60;
obj.d2 = 70;
obj.d3 = 80;
cout << "\n B : " << obj.b;
cout << "\n D1 : " << obj.d1;
cout << "\n D2 : " << obj.d2;
cout << "\n D3 : " << obj.d3;
}실행 결과
B : 30 D1 : 60 D2 : 70 D3 : 80
결과 분석
가상 상속을 적용한 후에는 D3 객체 안에 B의 복사본이 단 하나만 존재합니다. 따라서 b에 대한 접근이 더 이상 모호하지 않으며, 구문 4(obj.b = 30;)가 구문 3(obj.b = 40;)에서 설정한 값을 덮어쓰게 되어 최종적으로 30이 출력됩니다.
정리
- 다중 상속 구조에서 같은 기본 클래스가 여러 경로로 상속되면 복사본이 중복되어 모호성 오류가 발생합니다.
- 중간 클래스들이 기본 클래스를
virtual로 상속하면, 최종 파생 클래스에는 기본 클래스의 인스턴스가 하나만 생성됩니다. - 이 기법은 C++의 대표적인 상속 구조 문제인 '다이아몬드 상속(diamond inheritance)' 문제를 해결하는 표준적인 방법입니다.