Der Diamant ist kein Problem, solange Sie nicht so etwas wie die virtuelle Vererbung in C++ zu verwenden: Bei der normalen Vererbung ähnelt jede Basisklasse einem Mitgliedsfeld (tatsächlich sind sie in RAM auf diese Weise angeordnet), was Ihnen einen gewissen syntaktischen Zucker und eine zusätzliche Möglichkeit bietet, weitere virtuelle Methoden zu überschreiben. Das kann zur Kompilierzeit zu einer gewissen Mehrdeutigkeit führen, aber das ist normalerweise leicht zu lösen.
Andererseits gerät das virtuelle Erbe allzu leicht außer Kontrolle (und wird dann zum Chaos). Nehmen wir als Beispiel ein "Herz"-Diagramm:
A A
/ \ / \
B C D E
\ / \ /
F G
\ /
H
In C++ ist das völlig unmöglich: Sobald F
y G
zu einer einzigen Klasse verschmolzen werden, deren A
s werden ebenfalls zusammengeführt, Punkt. Das bedeutet, dass Sie Basisklassen in C++ niemals als undurchsichtig betrachten dürfen (in diesem Beispiel müssen Sie A
en H
Sie müssen also wissen, dass es irgendwo in der Hierarchie vorhanden ist). In anderen Sprachen kann es jedoch funktionieren, zum Beispiel, F
y G
könnten A ausdrücklich als "intern" deklarieren, was eine konsequente Verschmelzung verbietet und sich selbst zu einer festen Größe macht.
Ein weiteres interessantes Beispiel ( pas C++-spezifisch):
A
/ \
B B
| |
C D
\ /
E
Hier wird nur B
verwendet virtuelle Vererbung. Also E
enthält zwei B
s, die die gleiche A
. Auf diese Weise können Sie eine A*
Zeiger, der auf E
aber man kann es nicht auf eine B*
Zeiger, obwohl das Objekt ist eigentlich B
ist mehrdeutig, und diese Mehrdeutigkeit kann zur Kompilierzeit nicht erkannt werden (es sei denn, der Compiler sieht das gesamte Programm). Hier ist der Testcode:
struct A { virtual ~A() {} /* so that the class is polymorphic */ };
struct B: virtual A {};
struct C: B {};
struct D: B {};
struct E: C, D {};
int main() {
E data;
E *e = &data;
A *a = dynamic_cast<A *>(e); // works, A is unambiguous
// B *b = dynamic_cast<B *>(e); // doesn't compile
B *b = dynamic_cast<B *>(a); // NULL: B is ambiguous
std::cout << "E: " << e << std::endl;
std::cout << "A: " << a << std::endl;
std::cout << "B: " << b << std::endl;
// the next casts work
std::cout << "A::C::B: " << dynamic_cast<B *>(dynamic_cast<C *>(e)) << std::endl;
std::cout << "A::D::B: " << dynamic_cast<B *>(dynamic_cast<D *>(e)) << std::endl;
std::cout << "A=>C=>B: " << dynamic_cast<B *>(dynamic_cast<C *>(a)) << std::endl;
std::cout << "A=>D=>B: " << dynamic_cast<B *>(dynamic_cast<D *>(a)) << std::endl;
return 0;
}
Außerdem kann die Implementierung sehr komplex sein (je nach Sprache; siehe die Antwort von benjismith).
60 Stimmen
Ich möchte nur erwähnen, dass C++ großartig ist, um Ihnen genug Seil zu geben, um sich selbst aufzuhängen.
1 Stimmen
Eine Alternative zur Mehrfachvererbung, die viele der gleichen Probleme angeht (und, IMHO, auch löst), sind Traits ( iam.unibe.ch/~scg/Forschung/Traits )
54 Stimmen
Ich dachte, C++ gibt Ihnen genug Spielraum, um sich selbst in den Fuß zu schießen.
7 Stimmen
Diese Frage scheint davon auszugehen, dass es ein Problem mit MI im Allgemeinen gibt, während ich viele Sprachen gefunden habe, in denen MI gelegentlich verwendet wird. Es gibt sicherlich Probleme mit der Handhabung von MI in bestimmten Sprachen, aber ich bin mir nicht bewusst, dass MI im Allgemeinen signifikante Probleme hat.