가상 상속
Virtual inheritance
가상 상속은 기본 클래스의 멤버 변수의 사본 하나만 손자 파생 클래스에 상속되도록 하는 C++ 기법이다.가상 상속 없이 두 클래스인 경우B
그리고C
계급으로부터 물려받다.A
, 그리고 클래스.D
양쪽으로부터 물려받다.B
그리고C
그때D
의 사본 2부를 포함할 것이다.A
의 멤버 변수: 하나의 ~를 통해B
, 그리고 하나를 통해C
스코프 해상도를 사용하여 독립적으로 접근할 수 있다.
대신, 만약 수업들이B
그리고C
사실상 계급으로부터 물려받다.A
, 그리고 클래스의 사물들D
클래스의 멤버 변수 집합 하나만 포함A
.
이 기능은 가상 기반이 파생 클래스와 그로부터 파생된 모든 클래스의 공통 하위 객체로 만들기 때문에 다중 상속에 가장 유용하다.이는 파생계급(생성계급)의 관점에서 사용하고자 하는 조상계급에 대한 모호성을 명확히 함으로써 다이아몬드 문제를 피하는 데 사용할 수 있다.D
위의 예에서) 가상 기반(기존)A
)은 의 직접 베이스 클래스인 것처럼 행동한다.D
, 베이스를 통해 간접적으로 파생된 클래스가 아님(B
또는C
).[1][2]
상속이 부품 구성보다 세트 제한을 나타낼 때 사용한다.C++에서는 계층 전체에서 공통으로 사용할 기본 클래스를 가상 클래스와 함께 가상 클래스로 표시한다.virtual
키워드
다음 클래스 계층 구조를 고려하십시오.
구조상의 애니멀 { 가상의 ~애니멀() = 체납; 가상의 공허하게 하다 먹다() {} }; 구조상의 포유류: 애니멀 { 가상의 공허하게 하다 숨쉬다() {} }; 구조상의 윙에디애니멀: 애니멀 { 가상의 공허하게 하다 플랩() {} }; // 박쥐는 날개가 달린 포유동물이다. 구조상의 박쥐: 포유류, 윙에디애니멀 {}; 박쥐 박쥐;
위와 같이, 에 대한 호출.bat.Eat
두 개가 있기 때문에 모호하다.Animal
(iii) 기본 클래스:Bat
, so anythingBat
물체는 두 가지 다른 것을 가지고 있다.Animal
기본 클래스 하위 개체그래서 참조를 에 직접 바인딩하려는 시도Animal
의 하위 목적.Bat
바인딩이 본질적으로 모호하기 때문에 객체가 실패할 수 있음:
박쥐 b; 애니멀& a = b; // 오류: 박쥐가 어떤 동물 하위 객체를 캐스팅, // 포유류::Animal 또는 WingedAnimal:동물?
모호함을 해소하려면 명시적으로 변환해야 한다.bat
기본 클래스 하위 개체로:
박쥐 b; 애니멀& 포유 동물 = 정적_캐스트<포유류&>(b); 애니멀& 날개가 달린 = 정적_캐스트<윙에디애니멀&>(b);
전화를 걸기 위해서Eat
, 동일한 모호성 또는 명시적 자격 부여가 필요하다.static_cast<Mammal&>(bat).Eat()
또는static_cast<WingedAnimal&>(bat).Eat()
또는 대안으로bat.Mammal::Eat()
그리고bat.WingedAnimal::Eat()
명시적 자격은 포인터와 물체 모두에 대해 더 쉽고 균일한 구문을 사용할 뿐만 아니라 정적 파견도 허용하기 때문에 논란의 여지가 없이 선호되는 방법이 될 것이다.
이 경우 의 이중 상속.Animal
우리가 그 관계를 모델링하기를 원하듯이, 아마도 원하지 않을 것이다.Bat
이다Animal
)은 한 번만 존재함, 즉Bat
a이다Mammal
그리고 is a이다.WingedAnimal
그것이 라는 것을 의미하지 않는다.Animal
두 번: aAnimal
기본 클래스는 다음 계약에 해당한다.Bat
구현(위의 "a" 관계는 실제로 "요구사항"을 의미한다) 및Bat
오직 을 실행하다Animal
한 번 계약하다'한 번뿐'이라는 현실의 의미는.Bat
실행 방법은 하나뿐이어야 한다.Eat
, 두 가지 다른 방법이 아니라, 그 두 가지 방법이 있다.Mammal
에 대한 견해Bat
먹는 것이다, 또는WingedAnimal
에 대한 견해Bat
. (첫 번째 코드 예에서는Eat
어느 쪽에서도 오버라이드되지 않다.Mammal
또는WingedAnimal
그래서 두 사람은Animal
하위 객체는 실제로 똑같이 행동하겠지만, 이것은 단지 퇴보한 경우일 뿐, C++ 관점과 차이가 없다.)
이러한 상황을 다이아몬드 상속(다이아몬드 문제 참조)이라고 부르기도 하는데, 그 이유는 상속도가 다이아몬드 모양이기 때문이다.가상 상속은 이 문제를 해결하는 데 도움이 될 수 있다.
해결책
우리는 다음과 같이 우리의 수업을 다시 선언할 수 있다.
구조상의 애니멀 { 가상의 ~애니멀() = 체납; 가상의 공허하게 하다 먹다() {} }; // 실질적으로 Animal을 계승하는 두 가지 등급: 구조상의 포유류: 가상의 애니멀 { 가상의 공허하게 하다 숨쉬다() {} }; 구조상의 윙에디애니멀: 가상의 애니멀 { 가상의 공허하게 하다 플랩() {} }; // 박쥐는 여전히 날개가 달린 포유동물이다. 구조상의 박쥐: 포유류, 윙에디애니멀 {};
그Animal
의 일부Bat::WingedAnimal
이제 똑같다 Animal
의 예로서.Bat::Mammal
, 즉 A라는 말이다.Bat
단 하나, 공유,Animal
그 대표에 있어서의 예.Bat::Eat
확실하지 않다.또한, 다음에서 직접 캐스팅Bat
로Animal
또한 명확하지 않다. 이제 오직 하나만이 존재한다.Animal
어떤 경우.Bat
로 변환될 수 있다.
의 단일 인스턴스를 공유하는 기능Animal
사이의 친족.Mammal
그리고WingedAnimal
메모리 오프셋을 기록하여Mammal
또는WingedAnimal
기지의 구성원들과 그 기지의 사람들.Animal
파생 계급 내에서그러나 이 오프셋은 일반 사례에서 런타임에만 알 수 있으므로Bat
반드시 ()이 되어야 한다vpointer
,Mammal
,vpointer
,WingedAnimal
,Bat
,Animal
. 가상으로 상속되는 상속 계층당 하나씩, 두 개의 vtable 포인터가 있다.Animal
. 이 예에서, 다음 예에서.Mammal
을 위한 것WingedAnimal
따라서 개체 크기는 두 개의 포인터가 증가했지만, 지금은 한 개뿐입니다.Animal
모호하지 않고.모든 개체 유형Bat
동일한 vpointer를 사용하지만 각각Bat
사물은 그 고유한 것을 포함할 것이다.Animal
반대하다다른 클래스가 다음 클래스에서 상속되는 경우Mammal
예를 들어Squirrel
, 그리고 vpointer in the.Mammal
의 일부Squirrel
일반적으로 vpointer와 다를 것이다.Mammal
의 일부Bat
비록 그들이 같은 것이 될 수도 있지만Squirrel
클래스가 와 같다Bat
.
여러 조상의 추가 예제
이 예제는 기본 클래스가 있는 경우를 보여 주기 위한 것이다.A
생성자 변수 있음msg
그리고 추가 조상E
손주 계층에서 파생되었다.D
.
A / \ B C \ / D E
여기,A
둘 다 건설해야 한다.D
그리고E
. 또한 변수에 대한 검사msg
등급이 어떻게 되는지 보여 준다.A
중간 파생 클래스의 기본 클래스와 반대로, 파생 클래스의 직접 기본 클래스가 된다.A
그리고 마지막 파생 수업.아래의 코드는 여기에서 대화형으로 탐색할 수 있다.
#include <끈> #include <아이오스트림> 계급의 A { 사유의: 찌꺼기::끈을 매다 _msg; 공중의: A(찌꺼기::끈을 매다 x): _msg(x) {} 공허하게 하다 시험하다(){ 찌꺼기::뻐드렁니가 나다<<"안녕하십니까 A: "<<_msg <<"\n"; } }; // B,C는 사실상 A를 상속한다. 계급의 B: 가상의 공중의 A { 공중의: B(찌꺼기::끈을 매다 x):A("b"){} }; 계급의 C: 가상의 공중의 A { 공중의: C(찌꺼기::끈을 매다 x):A("c"){} }; // B,C는 사실상 A를 상속받으므로 A는 각 아동에 대해 생성되어야 한다. 계급의 D: 공중의 B,C { 공중의: D(찌꺼기::끈을 매다 x):A("d_a"),B("d_b"),C("d_c"){} }; 계급의 E: 공중의 D { 공중의: E(찌꺼기::끈을 매다 x):A("e_a"),D("e_d"){} }; // A을(를) 구성하지 않고 중단 // 클래스 D: 공용 B,C { 공용: D(std::string x):B(x),C(x){}; // A을(를) 구성하지 않고 중단 //클래스 E: 공개 D { 공개: E(std::string x):D(x){} }; 인트로 본래의(인트로 argc, 마를 뜨다 ** 아그브){ D d("d"); d.시험하다(); // A: d_a 안녕 E e("e"); e.시험하다(); // A: e_a에서 hello }
순수 가상 메서
기본 클래스에 순수 가상 메서드가 정의되어 있다고 가정해 보십시오.파생 클래스가 기본 클래스를 가상으로 상속하는 경우 순수 가상 방법은 해당 파생 클래스에 정의될 필요가 없다.그러나 파생 클래스가 가상으로 기본 클래스를 상속하지 않는 경우 모든 가상 방법을 정의해야 한다.아래의 코드는 여기에서 대화형으로 탐색할 수 있다.
#include <끈> #include <아이오스트림> 계급의 A { 보호받는: 찌꺼기::끈을 매다 _msg; 공중의: A(찌꺼기::끈을 매다 x): _msg(x) {} 공허하게 하다 시험하다(){ 찌꺼기::뻐드렁니가 나다<<"안녕하십니까 A: "<<_msg <<"\n"; } 가상의 공허하게 하다 pure_virtual_test() = 0; }; // B,C가 사실상 A를 상속하므로 순수한 가상 방법 pure_virtual_test는 정의할 필요가 없다. 계급의 B: 가상의 공중의 A { 공중의: B(찌꺼기::끈을 매다 x):A("b"){} }; 계급의 C: 가상의 공중의 A { 공중의: C(찌꺼기::끈을 매다 x):A("c"){} }; // B,C는 사실상 A를 상속받으므로 A는 각 아동에 대해 생성되어야 한다. // 단, D가 B,C를 가상으로 상속하지 않기 때문에 A *musted 가상 메서드를 정의해야 함* 계급의 D: 공중의 B,C { 공중의: D(찌꺼기::끈을 매다 x):A("d_a"),B("d_b"),C("d_c"){} 공허하게 하다 pure_virtual_test() 무효로 하다 { 찌꺼기::뻐드렁니가 나다<<"가상 인사말 위치: "<<_msg <<"\n"; } }; // 부모가 가상 방법을 정의한 후 순수 가상 방법을 재정의할 필요는 없음 계급의 E: 공중의 D { 공중의: E(찌꺼기::끈을 매다 x):A("e_a"),D("e_d"){} }; 인트로 본래의(인트로 argc, 마를 뜨다 ** 아그브){ D d("d"); d.시험하다(); d.pure_virtual_test(); E e("e"); e.시험하다(); e.pure_virtual_test(); }
참조
- ^ Milea, Andrei. "Solving the Diamond Problem with Virtual Inheritance". Cprogramming.com. Retrieved 2010-03-08.
One of the problems that arises due to multiple inheritance is the diamond problem. A classical illustration of this is given by Bjarne Stroustrup (the creator of C++) in the following example:
- ^ McArdell, Ralph (2004-02-14). "C++/What is virtual inheritance?". All Experts. Archived from the original on 2010-01-10. Retrieved 2010-03-08.
This is something you find may be required if you are using multiple inheritance. In that case it is possible for a class to be derived from other classes which have the same base class. In such cases, without virtual inheritance, your objects will contain more than one subobject of the base type the base classes share. Whether this is what is the required effect depends on the circumstances. If it is not then you can use virtual inheritance by specifying virtual base classes for those base types for which a whole object should only contain one such base class subobject.