I might be misreading your question, but I'll give it a go, because I suspect it is a question I also had at one point.
It might look like Java doesn't have multiple implementation inheritance because it's hard to implement, while C++ has because its implementors found a way to implement it. This isn't the case. As others have said, it was a deliberate design decision to avoid a category of programming problems, not something to enable easier implementation of Java.
The very fact that Java has mulitple inheritance from interfaces means that its implementors have done the hard work of implementing multiple inheritance anyway - the fact that interfaces are abstract is mostly irrelevant.
In an implementation using vtables, to implement polymorphism under single inheritance, you need a vtable of the runtime type of your instance, perferably at a predetermined position. To implement polymorphism under multiple inheritance, you need multiple vtables.
For ease, let's say that each type's memory layout starts with its vtable (it's not required to be like this, but it'll make my description easier).
Type A [vtableA, mem1, mem2, ...]
Type B [vtableB, mem3, mem4, ...]
Type C : A, B [vtableA, mem1, mem2, ..., vtableB, mem3, mem4,...]
C* from C ^
Under single inheritance, you just pass the object reference around and you can always find the vtable of its runtime type.
Type C : A, B [vtableA, mem1, mem2, ..., vtableB, mem3, mem4,...]
A* from C ^
Under multiple inheritance, in order to use substitution, you have to pass a reference such that, if interpretreted as a reference to an instance of a supertype, it will still work. This means that the reference should point to another vtable in the memory layout of the whole instance. This is simply done by adding an offset, until the new reference points to the new vtable. This offset is known at compile time, and it's probably the total size of the preceding data in the layout.
Type C : A, B [vtableA, mem1, mem2, ..., vtableB, mem3, mem4,...]
B* from C ^
<----memory size of A---->
Notice that in the schematics I used "type", not "class". If you assume that the language doesn't allow you to declare your own data members, all the above still holds. The only difference is that Type B
will now consist of only a vtable (because the language won't allow mem3
, mem4
, etc). But the mechanism is the same.
Clearly, C++ successfully addressed this, since it has multiple inheritance.
Clearly C++ implemented multiple inheritance; that doesn't mean it addressed its problems. C++ has no problem with you shooting your feet off.uint32_t
, etc), preprocessing macros, raw pointers and explicitly allocated memory, in each case, a deliberate conscious decision about what the language should be. Just because a language doesn't implement something doesn't mean that the designers didn't know how.