> every call to a virtual method-- including calls internal to the class-- goes through a dispatch step
You've touched on a whole host of performance issues here too. Not only are we making indirection after indirection when accessing virtual methods and data (my poor cache), but invariably these are all stored in heap memory, so we get the worst possible performance profile baked in.
> the actual code that gets executed depends on whether the object is one of a "derived" class, where that method might have been changed in ways that might break any amount of expected invariants.
On top of that inheritance makes the concrete type unknowable at compile time just to add to the pain.
> Implementation inheritance is often justified as a way of "reusing" code, but as it turns out, we've merely introduced undesired coupling instead of the seamless reuse we might have expected.
Yes this 100%. Inheritance actually stands in the way of code reuse. If I have a class that is kind of similar to another, but different enough to "require" being another class, oh dear they're now incompatible. So you try to kind of fit it into a subclass, but it makes more sense in another, and now you're spending mental effort on a problem created by architecture and not the actual thing you're trying to solve.
This is really clear in the game development world, where game objects often just don't fit into the inheritance format, even with multiple inheritance. This is why most complex games are built with the entity component system pattern as it focuses entirely on composition.
Perhaps the crux of the issue is that inheritance encourages design by "identity" (mental concept) rather than "attribute" (data). Thus to solve problems we must mentally classify them into some taxonomy as if they are animal species before we even write any code. This is just extra work in exchange for all the issues mentioned.