Почему множественное наследование возможно в C ++, но не в C #?
Я думаю (не имея точных ссылок), что в Java они хотели ограничить выразительность языка, чтобы облегчить его изучение, и потому что код, использующий множественное наследование, чаще всего слишком сложен для своего блага, чем нет. А поскольку полное множественное наследование намного сложнее в реализации, оно значительно упростило и виртуальную машину (множественное наследование особенно плохо взаимодействует со сборщиком мусора, поскольку требует хранения указателей на середину объекта (в начале базы). )
И при разработке C # я думаю, что они смотрели на Java, увидели, что полное множественное наследование действительно не так уж и упущено, и решили сделать все проще.
Как C ++ разрешает неоднозначность идентичных сигнатур методов, унаследованных от нескольких базовых классов?
Это не так . Существует синтаксис для вызова метода базового класса из конкретной базы в явном виде, но нет способа переопределить только один из виртуальных методов, и если вы не переопределите метод в подклассе, его невозможно вызвать без указания базы учебный класс.
И почему тот же дизайн не включен в C #?
Там нет ничего, чтобы включить.
Поскольку Джорджио упомянул методы расширения интерфейса в комментариях, я объясню, что такое миксины и как они реализованы на разных языках.
Интерфейсы в Java и C # ограничены только объявлением методов. Но методы должны быть реализованы в каждом классе, который наследует интерфейс. Однако существует большой класс интерфейсов, где было бы полезно предоставить реализации некоторых методов по умолчанию в терминах других. Типичный пример сопоставим (на псевдо-языке):
mixin IComparable {
public bool operator<(IComparable r) = 0;
public bool operator>(IComparable r) { return r < this; }
public bool operator<=(IComparable r) { return !(r < this); }
public bool operator>=(IComparable r) { return !(r > this); }
public bool operator==(IComparable r) { return !(r < this) && !(r > this); }
public bool operator!=(IComparable r) { return r < this || r > this; }
};
Отличие от полного класса в том, что он не может содержать никаких элементов данных. Есть несколько вариантов реализации этого. Очевидно, множественное наследование одно. Но множественное наследование довольно сложно реализовать. Но это действительно не нужно здесь. Вместо этого многие языки реализуют это, разделяя миксин в интерфейсе, который реализуется классом, и в хранилище реализаций методов, которые либо внедряются в сам класс, либо генерируется промежуточный базовый класс, и они размещаются там. Это реализовано в Ruby и D , будет реализовано в Java 8 и может быть реализовано вручную в C ++ с использованием любопытно повторяющегося шаблона . Выше, в форме CRTP, выглядит так:
template <typename Derived>
class IComparable {
const Derived &_d() const { return static_cast<const Derived &>(*this); }
public:
bool operator>(const IComparable &r) const { r._d() < _d(); }
bool operator<=(const IComparable &r) const { !(r._d() < _d(); }
...
};
и используется как:
class Concrete : public IComparable<Concrete> { ... };
Для этого не требуется ничего объявлять виртуальным, как это делает обычный базовый класс, поэтому, если интерфейс используется в шаблонах, остаются открытыми полезные параметры оптимизации. Обратите внимание, что в C ++ это, вероятно, все еще будет наследоваться как второй родитель, но в языках, которые не допускают множественное наследование, оно вставляется в единую цепочку наследования, так что это больше похоже на
template <typename Derived, typename Base>
class IComparable : public Base { ... };
class Concrete : public IComparable<Concrete, Base> { ... };
Реализация компилятора может или не может избежать виртуальной диспетчеризации.
В C # была выбрана другая реализация. В C # реализации являются статическими методами совершенно отдельного класса, и синтаксис вызова метода соответствующим образом интерпретируется компилятором, если метод с данным именем не существует, но определен «метод расширения». Это имеет преимущество в том, что методы расширения могут быть добавлены к уже скомпилированному классу, и недостаток в том, что такие методы не могут быть переопределены, например, для обеспечения оптимизированной версии.