В C ++ класс может наследовать (прямо или косвенно) более чем от одного класса, что называется
множественным наследованием. .
Однако C # и Java ограничивают классы одним наследованием, которое каждый класс наследует от одного родительского класса.
Множественное наследование - это полезный способ создания классов, которые объединяют аспекты двух разрозненных иерархий классов, что часто случается при использовании разных структур классов в одном приложении.
Если две структуры определяют свои собственные базовые классы для исключений, например, вы можете использовать множественное наследование для создания классов исключений, которые могут использоваться с любой структурой.
Проблема множественного наследования в том, что оно может привести к неоднозначности. Классический пример - это когда класс наследуется от двух других классов, каждый из которых наследуется от того же класса:
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
}
};
В этом примере flag
член данных определяется как class A
. Но class D
нисходит class B
и class C
, которые оба происходят из A
, так что по сути две копии из flag
доступны , потому что два экземпляра A
в D
иерархии классов «S. Какой из них вы хотите установить? Компилятор будет жаловаться , что ссылка на flag
ин D
является неоднозначной . Одно исправление - явное устранение неоднозначности ссылки:
B::flag = nflag;
Еще одно исправление - объявить B и C как virtual base classes
, что означает, что в иерархии может существовать только одна копия A, что устраняет любую двусмысленность.
Другие сложности существуют с множественным наследованием, например порядок, в котором базовые классы инициализируются при построении производного объекта, или способ непреднамеренного скрытия членов от производных классов. Чтобы избежать этих сложностей, некоторые языки ограничиваются более простой моделью одиночного наследования.
Хотя это значительно упрощает наследование, оно также ограничивает его полезность, поскольку только классы с общим предком могут совместно использовать поведение. Интерфейсы несколько смягчают это ограничение, позволяя классам в разных иерархиях предоставлять общие интерфейсы, даже если они не реализованы путем совместного использования кода.