[oop] 다중 상속의 정확한 문제는 무엇입니까?

여러 상속이 다음 버전의 C # 또는 Java에 포함되어야하는지 항상 묻는 사람들을 볼 수 있습니다. 이 능력을 가질만큼 운이 좋은 C ++ 사람들은 이것이 마치 누군가가 결국 스스로 매달릴 줄을주는 것과 같다고 말합니다.

다중 상속의 문제는 무엇입니까? 구체적인 샘플이 있습니까?



답변

가장 명백한 문제는 함수 재정의입니다.

하자 두 개의 클래스가 있다고 가정 A하고 B하는 방법을 정의하는 두 가지 모두를 doSomething. 이제 및 C둘 다에서 상속되는 세 번째 클래스를 정의 하지만 메서드를 재정의하지 않습니다 .ABdoSomething

컴파일러가이 코드를 시드하면 …

C c = new C();
c.doSomething();

… 방법의 어떤 구현을 사용해야합니까? 더 이상의 설명이 없으면 컴파일러가 모호성을 해결할 수 없습니다.

재정의 외에도 다중 상속의 또 다른 큰 문제는 메모리에있는 물리적 개체의 레이아웃입니다.

C ++, Java 및 C #과 같은 언어는 각 객체 유형에 대해 고정 주소 기반 레이아웃을 만듭니다. 이 같은:

class A:
    at offset 0 ... "abc" ... 4 byte int field
    at offset 4 ... "xyz" ... 8 byte double field
    at offset 12 ... "speak" ... 4 byte function pointer

class B:
    at offset 0 ... "foo" ... 2 byte short field
    at offset 2 ... 2 bytes of alignment padding
    at offset 4 ... "bar" ... 4 byte array pointer
    at offset 8 ... "baz" ... 4 byte function pointer

컴파일러가 기계어 코드 (또는 바이트 코드)를 생성 할 때 해당 숫자 오프셋을 사용하여 각 메서드 또는 필드에 액세스합니다.

다중 상속은 매우 까다 롭습니다.

클래스 CA및 둘 다에서 상속하는 경우 B컴파일러는 데이터를 AB순서대로 레이아웃할지 아니면 순서대로 레이아웃할지 결정해야합니다 BA.

그러나 이제 B객체 에 대해 메서드를 호출한다고 상상해보십시오 . 정말 그냥 B? 아니면 실제로 인터페이스를 C통해 다형 적으로 호출 되는 객체 B입니까? 개체의 실제 ID에 따라 물리적 레이아웃이 달라지며 호출 사이트에서 호출 할 함수의 오프셋을 알 수 없습니다.

이러한 종류의 시스템을 처리하는 방법은 고정 레이아웃 접근 방식을 버리고 함수를 호출하거나 해당 필드에 액세스 하기 전에 각 개체의 레이아웃을 쿼리 할 수 ​​있도록하는 것 입니다.

그래서 … 긴 이야기를 짧게 … 컴파일러 작성자가 다중 상속을 지원하는 것은 목에 고통입니다. 따라서 Guido van Rossum과 같은 사람이 파이썬을 설계하거나 Anders Hejlsberg가 c #을 설계 할 때 다중 상속을 지원하는 것이 컴파일러 구현을 훨씬 더 복잡하게 만들 것이라는 것을 알고 있으며, 아마도 그 혜택이 비용의 가치가 있다고 생각하지 않습니다.


답변

여러분이 언급 한 문제는 해결하기가 그리 어렵지 않습니다. 사실 에펠은 완벽하게 잘합니다! (임의의 선택 등을 도입하지 않고)

예를 들어 A와 B에서 모두 foo () 메서드를 사용하는 경우, 물론 A와 B 모두에서 상속하는 클래스 C에서 임의의 선택을 원하지 않습니다. foo를 재정의해야하므로 무엇이 될지 분명합니다. c.foo ()가 호출되거나 그렇지 않으면 C의 메소드 중 하나의 이름을 변경해야합니다. (bar ()가 될 수 있습니다.)

또한 다중 상속이 종종 매우 유용하다고 생각합니다. Eiffel 라이브러리를 보면 어디에서나 사용되고 있다는 것을 알 수 있으며 개인적으로 Java 프로그래밍으로 돌아 가야 할 때 기능을 놓쳤습니다.


답변

다이아몬드 문제 :

두 클래스 B와 C가 A에서 상속하고 클래스 D가 B와 C 모두에서 상속 할 때 발생하는 모호함. A에 B와 C가 재정의 한 메서드 가 있고 D가 재정의하지 않는 경우 어떤 버전의 D가 상속하는 방법 : B 또는 C의 방법?

…이 상황에서 클래스 상속 다이어그램의 모양 때문에 “다이아몬드 문제”라고합니다. 이 경우 클래스 A는 상단에 있고 B와 C는 그 아래에 별도로 있으며 D는 하단에서 두 개를 결합하여 다이아몬드 모양을 형성합니다.


답변

다중 상속은 자주 사용되지 않고 오용 될 수 있지만 때때로 필요한 것 중 하나입니다.

좋은 대안이 없을 때 오용 될 수 있다는 이유만으로 기능을 추가하지 않는 것을 이해하지 못했습니다. 인터페이스는 다중 상속의 대안이 아닙니다. 우선, 그들은 전제 조건이나 사후 조건을 강제 할 수 없습니다. 다른 도구와 마찬가지로 사용하기에 적합한시기와 사용 방법을 알아야합니다.


답변

C에 의해 상속 된 객체 A와 B가 있다고 가정 해 봅시다. A와 B는 모두 foo ()를 구현하고 C는 그렇지 않습니다. C.foo ()를 호출합니다. 어떤 구현이 선택됩니까? 다른 문제가 있지만 이러한 유형은 큰 문제입니다.


답변

다중 상속의 주요 문제는 tloach의 예제로 잘 요약됩니다. 동일한 함수 또는 필드를 구현하는 여러 기본 클래스에서 상속 할 때 컴파일러는 상속 할 구현을 결정해야합니다.

동일한 기본 클래스에서 상속하는 여러 클래스에서 상속하면 더 나빠집니다. (다이아몬드 상속, 상속 트리를 그리면 다이아몬드 모양이됩니다)

이러한 문제는 컴파일러가 극복하는 데 실제로 문제가되지 않습니다. 그러나 여기서 컴파일러가 선택해야하는 것은 다소 임의적이므로 코드가 훨씬 덜 직관적입니다.

좋은 OO 디자인을 할 때 다중 상속이 필요하지 않습니다. 필요한 경우에는 일반적으로 상속을 사용하여 기능을 재사용하는 반면 상속은 “is-a”관계에만 적합합니다.

동일한 문제를 해결하고 다중 상속이 갖는 문제가없는 믹스 인과 같은 다른 기술이 있습니다.


답변

다이아몬드 문제가 문제가 아니라고 생각합니다.

내 관점에서 다중 상속과 관련된 최악의 문제는 RAD입니다. 피해자와 개발자라고 주장하지만 실제로는 절반의 지식 (기껏해야)에 갇혀있는 사람들입니다.

개인적으로 Windows Forms에서 다음과 같은 작업을 마침내 수행 할 수 있다면 매우 기쁠 것입니다 (올바른 코드는 아니지만 아이디어를 제공해야 함).

public sealed class CustomerEditView : Form, MVCView<Customer>

이것이 다중 상속이없는 주요 문제입니다. 인터페이스로 비슷한 일을 할 수 있지만 제가 “s *** 코드”라고 부르는 것이 있습니다. 예를 들어 데이터 컨텍스트를 얻기 위해 각 클래스에 작성해야하는 고통스러운 반복적 인 c ***입니다.

제 생각에는 현대 언어로 코드를 반복 할 필요가 전혀 없어야합니다.