2017-10-24 17 views
0

내 질문이 이전과 동일하지만, 나는 아직 주어진 대답은 이해하지 않는다 : 모두가 상속 다이아몬드 문제에 Diamond Problem다이아몬드 삼각형 상속 대

, D는 B와 C에서 상속을, B와 C는 둘 다 A에서 foo 메소드를 오버라이드합니다.

대신에 삼각형이 있다고 가정합니다. A와 D는 B와 C에서 상속 받았으며 두 메소드 모두 foo 메소드를 구현합니다. 이 전화 foo는 어떤 명확하지 않기 때문에

내가 알고있는 것처럼, 특별한 처리없이, 다음 다이아몬드 중 하나 또는 삼각형에 모호한 것 :

D d = new D; 
d.foo(); 

을 그래서 난 아직 확실하지 않다 무엇을 이 문제를 다이아몬드 문제로 만들고 더 일반적인 다중 상속 문제는 아닙니다. "삼각형"문제에서조차도 모호성을 제거 할 수있는 방법을 제시해야 할 것 같습니다.

+1

두 번째 예제에서 모호성을 이해할 수 있을지 모르겠습니다. 여러분은'B''' foo' 메서드 또는'D'에서 재정의 할 것입니다. 그러나 이런 유형의 문제에 대한 대부분의 설명은'D'에서 더 이상 무시하지 않으므로 호출하는 메소드를 정확히 알고 있습니다. –

+0

언어가 객체의 런타임 유형을 기반으로 메서드를 호출하는 방식으로 동작했기 때문에 D 유형의 객체에서 B.foo() 또는 C.foo()를 호출할지 여부가 모호했습니다. . 이 방법으로 처리 되든 설명하는 방식이든 다이아몬드와 삼각형에 공통적 인 것처럼 보입니다. – allstar

+0

이름 조회는 대부분의 언어에서 컴파일 타임에 발생합니다. 두 번째 예제에서, 런타임에, 당신은 "B.foo"또는 더 파생 된 클래스에서'B.foo'의 재정의를 찾을 것입니다. " 당신은 "foo"라고 불리는 메소드를 찾지 않을 것입니다. –

답변

1

의견에서 언급했듯이 일부 문제는 일반적으로 동적 디스패치가 구현되는 방식과 관련이 있습니다.

vtable 접근 방식을 가정 할 때 특정 유형은 vtable을 자체 또는 수퍼 유형으로 취급 할 수있는 vtable을 생성 할 수 있어야합니다. 단일 상속에서는 각 유형의 vtable이 즉각적인 상위 유형과 동일한 vtable 레이아웃으로 시작하고 그 뒤에 새로운 멤버가 오는 것이므로 쉽게 구현할 수 있습니다.

예.B이 두 가지 방법

vtable_B 
Slot #  Method 
1   B.foo 
2   B.bar 

그리고 B에서 D 상속, bar 무시를 가지고 도입하는 경우 baz :

vtable_SI_D 
Slot #  Method 
1   B.foo 
2   D.bar 
3   D.baz 

D 때문에이 B의 VTABLE에서 찾은 어떤 항목이 단지 복사, foo을 무시하지 않았다 슬롯 # 1에 대해

D에서 B까지의 변수를 사용하는 코드는 슬롯 # 1과 # 2 만 사용하며 모든 것이 올바르게 작동합니다.

그러나 다중 상속을 도입하면 단일 vtable을 사용할 수 없게 될 수도 있습니다. foobar 방법을 가진 C을 소개한다고 가정 해보십시오.

vtable_MI_D_as_B 
Slot #  Method 
1   B.foo 
2   D.bar 

또는 C에 : DB로 캐스팅 할 때 이제 우리는 다른 vtable을을 사용해야합니다

vtable_MI_D_as_C 
Slot #  Method 
1   C.foo 
2   D.bar 

이들은 1 모호하다. 문제는이 아무것도 캐스팅하지 않을 경우 D의 VTABLE을 채우기 위해 노력하고있다 :

Slot #  Method 
1   <what goes here> 
2   D.bar 
3   D.baz 

그래서, 당신은 올바른 것 삼각형 상속이 몇 가지 문제를 제기하고있다. 우리가 (B 또는 CD 반대) DD에 대한 다른의 vtable을 사용하고 있기 때문에 그러나 우리는 슬롯 # 1에 대한 항목을 생략하고 불법 간단한 경우 (D.foo를 호출 할 수 있도록 간단 수있는

vtable_MI_D 
Slot #  Method 
2   D.bar 
3   D.baz 

이의 지금 A을 소개하자하고 다시 고전적인 다이아몬드 패턴, foo 정의가 : 아무것도 더 Bfoo 또는 오버라이드 (override) foo)를 사용하는 등 D의 정의에 명시되지 않습니다. 그래서 A의 VTABLE은 다음과 같습니다

vtable_A 
Slot #  Method 
1   A.foo 

BC는 위와 같이 설명되어 있습니다. 추가 문제가 하나만있는 경우를 제외하고 위와 똑같은 방법으로 D을 수행 할 수 있습니다. D에 대한 vtable을 A으로 제공해야합니다. 은 슬롯 # 1을 생략 할 수 없습니다. A을 다루는 코드는 foo을 호출 할 수 있어야합니다. 그리고 값이 서로 다르며 즉각적인 수퍼 유형이기 때문에 B 또는 C의 vtable에서 항목을 복사 할 수 없습니다.

이, 저는 믿습니다, 다이아몬드 패턴이 일반적으로 사용되는 이유의 요지입니다 - 우리는 그냥 구현할 수 없기 때문에 규칙 "당신은 Dfoo를 호출 할 수 없습니다"과 함께 할 수.


1 그것은 또한 vtable_MI_D_as_Bvtable_MI_D_as_C vtable을에 # 1과 # 2 슬롯이 여기 완전히 관련이없는 관찰 가치가있다. Cfoo 방법에 슬롯 2를, 그 bar 방법에 슬롯 6을 사용할 수 있습니다. 동일한 이름을 가진 메소드가 반드시 "동일한"슬롯을 공유하지는 않습니다.

이것은 슬롯 # 1이 모든 유형에 걸쳐 실제로 동일한 슬롯 인 다이아몬드 상속 패턴에 대한 후반 논의와 대조됩니다.

+0

이것은 대단한 설명입니다. 정말 고마워요. 나는 vtable_MI_D_as_A가 vtable_A에서 A.too의 복사본을 가지고 있지 않을 것이라고 설명했다. vtable_MI_D_as_B – allstar

+1

@allstar - 글쎄, 당신은 * 할 수 있었지만 상속은 일반적으로 작동하지 않는다. 'vtable_MI_D_as_B'에서'B'는 즉각적인 상위 타입이므로 우리 자신이 없으면'foo' 구현을 상속받습니다. 단일 상속이지만 각각의 'as'인스턴스에 대해 별도의 vtable을 사용하면 vtable_SI_D_as_A는 여전히'B''' foo'를 사용합니다. 이 시점에서 제안 된 수정 사항은 다이아몬드 상속 문제를 해결하려고 시도합니다. - 그리고 이러한 모든 수정에는 여러 가지 단점이 있습니다. 이것이 처음에 많이 논의 된 문제입니다 :-) –

0
D d = new D(); 
d.foo(); 

호출이 모호하며 컴파일러 오류가 발생합니다. 원하는 foo 버전을 지정해야합니다.

(B)d.foo(); 
(C)d.foo(); 

아무 문제 없습니다. A와 B 모두 A를 상속하는지 여부에 관계없이 적용됩니다.

A a = new D(); 
a.foo(); 

이것은 분명히에만 foo는 한 버전이있다하더라도, 또한 모호 : 문제는 당신이 가지고있는 경우이다. A가 주어지고 D가되는 경우, 객체에 대한 메소드를 호출하더라도 오류가 발생합니다. 당신이이 D의 여부를 테스트 할 수있는 경우에, foo는 호출하기 전에 B 또는 C로 캐스팅 여부를 결정, 그 주석에 응답하는 다형성에게

편집을 나누기 :

내가하는 방법이

void DoStuff(A a) 
{ 
    a.foo(); 
} 

과 나는 어떤 버전의 foo()를 호출해야합니까? DoStuff가 B, C 및 D를 정의한 다른 코드에서 참조하는 라이브러리에있는 경우 라이브러리 유지자가 상속받은 모든 가능한 객체에 대한 대체를 구현할 것으로 예상 할 수 없으므로 DoStuff()는이 작업을 수행하지 않아도됩니다. from

+0

맞아. 같은 문제로 처리되는 것처럼 보일거야. (B) a.foo(); – allstar

+0

즉, 설명하는 문제는 확실히 사실이지만 다른 상속 계층을 추가하고 있기 때문에 B와 C의 다중 상속과 함께 문제가 이미 있으므로 표준 문제를 다이아몬드 문제. 나는 분명히 다른 것을 놓치고있는 것처럼 느껴진다. – allstar