O problema mais óbvio é a substituição da função.
Digamos que tenha duas classes A
e B
, ambas definam um método doSomething
. Agora você define uma terceira classe C
, que herda de ambos A
e B
, mas não substitui o doSomething
método.
Quando o compilador propaga esse código ...
C c = new C();
c.doSomething();
... qual implementação do método ele deve usar? Sem mais esclarecimentos, é impossível para o compilador resolver a ambiguidade.
Além de substituir, o outro grande problema com herança múltipla é o layout dos objetos físicos na memória.
Idiomas como C ++, Java e C # criam um layout fixo baseado em endereço para cada tipo de objeto. Algo assim:
class A:
at offset 0 ... "abc" ... 4 byte int field
at offset 4 ... "xyz" ... 8 byte double field
at offset 12 ... "speak" ... 4 byte function pointer
class B:
at offset 0 ... "foo" ... 2 byte short field
at offset 2 ... 2 bytes of alignment padding
at offset 4 ... "bar" ... 4 byte array pointer
at offset 8 ... "baz" ... 4 byte function pointer
Quando o compilador gera código de máquina (ou código de código), ele usa esses deslocamentos numéricos para acessar cada método ou campo.
A herança múltipla torna muito complicado.
Se a classe C
herda de ambos A
e B
, o compilador deve decidir se deseja layout dos dados em AB
ordem ou em BA
ordem.
Mas agora imagine que você está chamando métodos em um B
objeto. É realmente apenas um B
? Ou é realmente um C
objeto chamado polimorficamente, por meio de sua B
interface? Dependendo da identidade real do objeto, o layout físico será diferente e é impossível saber o deslocamento da função a ser chamada no local da chamada.
A maneira de lidar com esse tipo de sistema é abandonar a abordagem de layout fixo, permitindo que cada objeto seja consultado por seu layout antes de tentar invocar as funções ou acessar seus campos.
Então ... para encurtar a história ... é uma dor de cabeça para os autores de compiladores apoiarem herança múltipla. Então, quando alguém como Guido van Rossum cria python, ou quando Anders Hejlsberg cria c #, eles sabem que o suporte à herança múltipla tornará as implementações do compilador significativamente mais complexas e, presumivelmente, elas não acham que o benefício vale o custo.