The convenience of inheritance in this example is reducing the amount of boilerplate code you have to write, but this falls under code reuse rather than behavioral reuse. By itself, that isn't an argument against using inheritance but rather that code reuse isn't a sufficient argument for inheritance.
Inheritance has significance and consequences beyond reducing code repetition. When the consequences aren't intended, issues can arise. Issues with using inheritance for a "has-a" relationship include:
- it violates encapsulation (as you mention in the question)
- it couples the classes tighter than necessary, which has far reaching consequences
- coupling can unintentionally create overridden methods, which can produce different bugs:
- in the child, the derived method doesn't call the base when it should (can be caught in unit tests & fixed)
- in parent methods, calls to the overriden method may instead call the derived method when they shouldn't
- coupling can also unintentionally create overridden fields, which, combined with encapsulation violation, can cause bugs
- it allows child instances to be used wherever the parent can be used. This may be desirable behavior or it may be a source of bugs. Does it make sense for a function taking a subsystem to instead receive the mechanical system?
- it affects how the type hierarchy can be altered and the classes extended
If you don't want to explicitly write forwarding methods, you can sometimes use language features to forward undefined method calls to another object (the functional part of the (Object) Proxy Pattern). This may still violate access restrictions, but largely avoids other issues.
In Python, you can use __getattr__, __setattr__ and __delattr__ (there are other special methods that may be useful or necessary); these also give you the opportunity to implement access restriction, as unpythonic as that is. Implement the Proxy Pattern in its own class, and you can add the functionality to most any other class you want with less than 2 lines of code.
In C++, you can overload operator->, but at the cost of unrestricted access and you must use the arrow operator rather than the dot operator to call methods (which is less coupling than inheritance but slightly more than explicitly writing each forwarding method).