W C ++ klasa może dziedziczyć (bezpośrednio lub pośrednio) z więcej niż jednej klasy, co jest określane jako
wielokrotne dziedziczenie .
Jednak języki C # i Java ograniczają klasy do pojedynczego dziedziczenia, które każda klasa dziedziczy z jednej klasy nadrzędnej.
Dziedziczenie wielokrotne to przydatny sposób tworzenia klas, które łączą aspekty dwóch różnych hierarchii klas, co często się zdarza, gdy używa się różnych struktur klas w jednej aplikacji.
Jeśli dwie struktury definiują własne klasy bazowe dla wyjątków, na przykład można użyć dziedziczenia wielokrotnego, aby utworzyć klasy wyjątków, które mogą być używane z dowolną strukturą.
Problem z dziedziczeniem wielokrotnym polega na tym, że może prowadzić do niejednoznaczności. Klasycznym przykładem jest sytuacja, gdy klasa dziedziczy po dwóch innych klasach, z których każda dziedziczy po tej samej klasie:
class A {
protected:
bool flag;
};
class B : public A {};
class C : public A {};
class D : public B, public C {
public:
void setFlag( bool nflag ){
flag = nflag; // ambiguous
}
};
W tym przykładzie element flag
członkowski danych jest zdefiniowany przez class A
. Ale class D
pochodzi od class B
i class C
, które wywodzą się z obu A
, a więc w istocie dwie kopie z flag
są dostępne, ponieważ dwa przypadki A
są w D
„s klasowej hierarchii. Który chcesz ustawić? Kompilator będzie narzekał, że odwołanie do flag
in D
jest niejednoznaczne . Jedną z poprawek jest jawne ujednoznacznienie odniesienia:
B::flag = nflag;
Innym rozwiązaniem jest zadeklarowanie B i C jako virtual base classes
, co oznacza, że tylko jedna kopia A może istnieć w hierarchii, eliminując wszelkie niejednoznaczności.
Istnieją inne zawiłości związane z dziedziczeniem wielokrotnym, na przykład kolejność inicjowania klas podstawowych podczas konstruowania obiektu pochodnego lub sposób, w jaki elementy członkowskie mogą być nieumyślnie ukrywane w klasach pochodnych. Aby uniknąć tych zawiłości, niektóre języki ograniczają się do prostszego pojedynczego modelu dziedziczenia.
Chociaż znacznie upraszcza to dziedziczenie, ogranicza również jego użyteczność, ponieważ tylko klasy mające wspólnego przodka mogą dzielić zachowania. Interfejsy nieco łagodzą to ograniczenie, umożliwiając klasom w różnych hierarchiach ujawnianie wspólnych interfejsów, nawet jeśli nie są one implementowane przez udostępnianie kodu.