A클래스를 상속받은 B클래스와 C클래스를 다중 상속받을 경우 발생하는 문제이다.
왼쪽 그림과 같이 다이아몬드 모양으로 상속이 될 것 같지만 사실은 오른쪽 그림과 같다. D클래스에선 B클래스가 상속받은 A클래스와 C클래스가 상속받은 A클래스가 같다는 것을 알지못한다.
-> D클래스에서 A클래스로부터 상속받은 멤버에 접근할 경우 B가 상속받은 A클래스의 멤버에 접근해야하는지 C가 상속받은 A클래스의 접근해야하는지 알지 못하는 모호성이 생긴다.
위 같은 문제를 가상 상속을 이용해 해결할 수 있다.
D클래스가 다중 상속 받은 B, C클래스가 A클래스를 상속받을 때 virtual 키워드를 붙이면 가상 상속을 받게된다.
virtual키워드를 붙이면 해당하는 변수, 함수들이 가상 테이블에 올라가게된다.
D클래스에서 A클래스에서 선언된 멤버에 접근할 경우 가상 테이블을 이용해 멤버에 접근하게되면서 모호성을 제거할 수 있다.
상속받은 클래스에서 생성자 함수를 구현할 경우 아래와 같이 작성한다.
D::D() : B() {};
D클래스의 객체를 생성하면 B클래스의 객체를 생성할 때 A클래스의 생성자를 자동 호출하므로 따로 명시해줄 필요가 없었다.
만약 아래처럼 A클래스의 생성자도 작성하는 경우엔 에러가 발생한다.
D::D() : A(), B() {};
하지만 D가 가상 상속을 받았다면 위같은 생성자도 문제가 없게된다. B클래스를 생성할 때 A클래스의 생성자를 호출되는 것이 아니라 D클래스를 생성할 때 A클래스의 생성자가 바로 호출될 수 있는 것이다.
가상 상속을 받을 경우 생성자 함수는 중복으로 호출할 수 없게된다.
D클래스에서 호출하는 A클래스의 생성자와 B클래스에서 호출하는 A클래스의 생성자가 같다면(인자 값까지 같다면) 크게 문제되지않지만 다른 생성자를 호출한다면 원하던 결과가 나오지 않을 수도 있다.
Class A {
public :
A() : _a(1) {};
A(int a) : _a(a) {};
~A(){};
protected :
int _a;
}
class B : virtual public A{
public :
B() : A(5) {};
~B(){};
}
class C : public B{
public :
C() : A(), B() {};
~C(){};
}
가상 상속받은 자식클래스가 아니었다면 C클래스를 생성했을 때 멤버 변수 _a의 값은 5가 될 것이다.
하지만 가상 상속을 받은 C를 생성할 때 A클래스의 생성자가 호출됐고 _a는 이미 1로 초기화가 됐고, B클래스 생성자 함수에서는 이미 호출된 A클래스의 생성자는 호출되지 않으므로 _a는 5로 초기화될 수 없게된다.
작은 예지만 위 같은 문제가 생기지 않도록 주의해서 작성해야한다.