Nie jestem facetem w C ++, ale muszę o tym myśleć. Dlaczego wielokrotne dziedziczenie jest możliwe w C ++, ale nie w C #? (Wiem o problemie z diamentem , ale nie o to tu pytam). Jak C ++ rozwiązuje niejednoznaczność identycznych podpisów metod odziedziczonych z wielu klas podstawowych? I dlaczego ten sam projekt nie jest włączony do C #?
c#
c++
language-design
multiple-inheritance
Sandeep
źródło
źródło
Odpowiedzi:
Myślę (bez twardego odniesienia), że w Javie chcieli ograniczyć ekspresję języka, aby uczynić go łatwiejszym do nauczenia się, a ponieważ kod wykorzystujący wielokrotne dziedziczenie jest często zbyt skomplikowany dla własnego dobra. A ponieważ pełne wielokrotne dziedziczenie jest o wiele bardziej skomplikowane do wdrożenia, dlatego też znacznie uprościło maszynę wirtualną (wielokrotne dziedziczenie szczególnie źle współdziała z modułem wyrzucania elementów bezużytecznych, ponieważ wymaga utrzymywania wskaźników na środku obiektu (na początku bazy) )
I projektując C #, myślę, że spojrzeli na Javę, zauważyli, że pełne wielokrotne dziedziczenie rzeczywiście nie było wiele pominięte i postanowili również uprościć sprawę.
Tak nie jest . Istnieje składnia umożliwiająca jawne wywołanie metody klasy bazowej z konkretnej bazy, ale nie ma sposobu, aby zastąpić tylko jedną z metod wirtualnych, a jeśli nie zastąpisz metody w podklasie, nie będzie możliwe jej wywołanie bez określenia bazy klasa.
Nie ma nic do włączenia.
Ponieważ Giorgio wspominał o metodach rozszerzenia interfejsu w komentarzach, wyjaśnię, czym są miksy i jak są one implementowane w różnych językach.
Interfejsy w Javie i C # są ograniczone tylko do metod deklarowania. Ale metody muszą być zaimplementowane w każdej klasie, która dziedziczy interfejs. Istnieje jednak duża klasa interfejsów, w których przydatne byłoby zapewnienie domyślnych implementacji niektórych metod w odniesieniu do innych. Typowy przykład jest porównywalny (w pseudo-języku):
Różnica w stosunku do pełnej klasy polega na tym, że nie może zawierać żadnych elementów danych. Istnieje kilka opcji realizacji tego. Oczywiście wielokrotne dziedziczenie jest jednym. Ale wdrożenie wielokrotnego dziedziczenia jest raczej skomplikowane. Ale tutaj tak naprawdę nie jest to potrzebne. Zamiast tego wiele języków implementuje to, dzieląc mixin w interfejsie, który jest implementowany przez klasę i repozytorium implementacji metod, które są albo wstrzykiwane do samej klasy, albo generowana jest pośrednia klasa bazowa i tam umieszczane. Jest to zaimplementowane w Ruby i D , będzie zaimplementowane w Javie 8 i może być zaimplementowane ręcznie w C ++ przy użyciu ciekawie powtarzającego się wzorca szablonu . Powyższe w formie CRTP wygląda następująco:
i jest używany jak:
Nie wymaga to deklarowania niczego wirtualnego, tak jak zrobiłaby to zwykła klasa podstawowa, więc jeśli interfejs jest używany w szablonach, pozostają użyteczne opcje optymalizacji otwarte. Zauważ, że w C ++ prawdopodobnie nadal byłby dziedziczony jako drugi element nadrzędny, ale w językach, które nie pozwalają na wielokrotne dziedziczenie, jest wstawiany do łańcucha pojedynczego dziedziczenia, więc bardziej przypomina
Implementacja kompilatora może, ale nie musi, unikać wirtualnej wysyłki.
W C # wybrano inną implementację. W języku C # implementacje są metodami statycznymi całkowicie oddzielnej klasy, a składnia wywołania metody jest odpowiednio interpretowana przez kompilator, jeśli metoda o podanej nazwie nie istnieje, ale zdefiniowano „metodę rozszerzenia”. Ma to tę zaletę, że metody rozszerzeń można dodawać do już skompilowanej klasy, a wadą jest to, że takich metod nie można zastąpić, np. W celu zapewnienia zoptymalizowanej wersji.
źródło
Odpowiedź brzmi: nie działa poprawnie w C ++ w przypadku kolizji przestrzeni nazw. Zobacz to . Aby uniknąć kolizji przestrzeni nazw, musisz wykonywać wszelkiego rodzaju zawirowania za pomocą wskaźników. Pracowałem w MS w zespole Visual Studio i przynajmniej częściowo powodem, dla którego opracowali delegację, było całkowite uniknięcie kolizji przestrzeni nazw. Uprzednio powiedziałem, że uważali również interfejsy za część rozwiązania wielokrotnego dziedziczenia, ale się myliłem. Interfejsy są naprawdę niesamowite i można je uruchomić w C ++, FWIW.
Delegowanie dotyczy konkretnie kolizji przestrzeni nazw: możesz delegować do 5 klas, a wszystkie 5 z nich wyeksportuje swoje metody do twojego zakresu jako członkowie pierwszej klasy. Na zewnątrz patrząc na to JEST wielokrotne dziedzictwo.
źródło